using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Serveranwendung
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public delegate void UpdateChatCallback(string text);
        public delegate void UpdateClientListCallback();


        const int MAX_CLIENTS = 10;
       
        protected Socket m_mainSocket;
        protected Socket[] m_workerSocket = new Socket[10];

        protected ArrayList m_workerSocketList = ArrayList.Synchronized(new ArrayList());

        protected AsyncCallback asyncWorkerCallBack;
        protected int clientCount = 0;
    
        public MainWindow()
        {
            InitializeComponent();
            chat.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
            chat.IsReadOnly = true;
            chat.IsEnabled = true;
        }


        public void onConnectionStart() {
            try
            {
                int port = 5000;
                String ipAdresse = "127.0.0.1"; 

                m_mainSocket = new Socket(AddressFamily.InterNetwork,
                                          SocketType.Stream,
                                          ProtocolType.Tcp);

                //ipAdressvergabe falls gewollt
                //IPEndPoint iplocal = new IPEndPoint(IPAddress.Parse(ipAdresse),port);
                IPEndPoint iplocal = new IPEndPoint(IPAddress.Any, port);

                m_mainSocket.Bind(iplocal);

                m_mainSocket.Listen(4);

                m_mainSocket.BeginAccept(new AsyncCallback(OnConnectedClient), null);

            }
            catch (SocketException e) {
                MessageBox.Show(e.Message);
            }
        }


        //Zählt und (erstellt Socket) der jeweiligen anfragen der clienten,  
        public void OnConnectedClient(IAsyncResult asn) {
            try
            {
                
                //TODO abfrage ob main socket noch offen ist oder so  ----------------------------------> Abfrage erstellen 
                //if(m_mainSocket != null){
                Socket workerSocket = m_mainSocket.EndAccept(asn);

                Interlocked.Increment(ref clientCount);

                m_workerSocketList.Add(workerSocket);
                //m_workerSocket[clientCount] = m_mainSocket.EndAccept(asn);

               // String ncToClient = "Willkommen Klient "+clientCount+"\n";              

               // SendMsgToClient(ncToClient, clientCount);  //Sende NAchricht 

                //Update die Clientliste mit zb. UpdateClientListControl() oder so 
               Dispatcher.Invoke(
               DispatcherPriority.Normal,
               (ThreadStart)delegate { ServerStats.Text = "Mit Client " + clientCount + " Verbunden"; /*Clientlist.Items.Add("Client"+clientCount);*/});
                
                WaitforDatenfromClient(workerSocket,clientCount,"");

                //clientCount++;

                m_mainSocket.BeginAccept(new AsyncCallback(OnConnectedClient), null);

               // String info = String.Format("Client # {0} connected",clientCount);

               // Console.WriteLine(info);
                //ServerStats.Text = info;
               // }

            }
            catch (SocketException e) {
                MessageBox.Show(e.Message);
            }
        }

        //Listener der immer auf eine Antwort vom Clienten wartet
        public void WaitforDatenfromClient(Socket soc, int clientnummer, String clientname) {

            try
            {
                if (asyncWorkerCallBack == null) {
                    asyncWorkerCallBack = new AsyncCallback(OnReceiveData);
                }

                SocketPacket sockPac = new SocketPacket(soc,clientnummer,clientname);
               

                soc.BeginReceive(sockPac.dataBuffer,0,sockPac.dataBuffer.Length,SocketFlags.None,asyncWorkerCallBack,sockPac);

            }
            catch (SocketException e) {
                MessageBox.Show(e.Message);
            }

        
        }

        //spürt Clienten Auf die an Den Server Schreibn || Listener 
        public void OnReceiveData(IAsyncResult asn){
            SocketPacket socketDaten = (SocketPacket)asn.AsyncState;
           
            try
            {
         
                int anzahlre = socketDaten.m_currentSocket.EndReceive(asn);
                char[] chars = new char[anzahlre]; 

                Decoder decode = System.Text.Encoding.UTF8.GetDecoder();

                int charLenght = decode.GetChars(socketDaten.dataBuffer,0,anzahlre,chars,0);

                String erhaltenerDS = new String(chars);
            

                string msg = "" + socketDaten.m_clientNumber + ":";
              
                // Send back the reply to the client
                string replyMsg = "Server Reply:" + erhaltenerDS.ToUpper();
                // Convert the reply to byte array
                byte[] byData = System.Text.Encoding.ASCII.GetBytes(replyMsg);


                Socket workerSocket = (Socket)socketDaten.m_currentSocket;
               // workerSocket.Send(byData);
                


                //######################### Fische den Namen des Clienten Raus und weise ihm am ende zu ################ 
                if (socketDaten.m_clientname.ToString().Equals(""))
                {
                    int x;

                    String[] splitt = erhaltenerDS.Split('/');

                    if (splitt.Length > 1)
                    {
                        bool isNum = Int32.TryParse(splitt[1], out x);

                        if (isNum)
                        {
                            socketDaten.m_clientname = splitt[0];


                            Dispatcher.Invoke(
                            DispatcherPriority.Normal,
                            (ThreadStart)delegate { Clientlist.Items.Add(socketDaten.m_clientname); });
                            erhaltenerDS = splitt[0]+" Connected";

                        }
                        else
                        {
                            socketDaten.m_clientname = "Client";

                            Dispatcher.Invoke(
                            DispatcherPriority.Normal,
                            (ThreadStart)delegate { Clientlist.Items.Add("Client" + clientCount); });

                        }

                    }
                    else
                    {
                        socketDaten.m_clientname = "Client";

                        Dispatcher.Invoke(
                        DispatcherPriority.Normal,
                        (ThreadStart)delegate { Clientlist.Items.Add("Client" + clientCount); });
                    }
                //#####################################################################################################
                }
               

                    //hier die aufforderung die nachrichten an alle anderen clienten zu schicken  // Später umfunktioniert zu senden Klient / klienten              
                    if (!erhaltenerDS.Equals(""))
                    {
                        SendMessageToOtherClients(erhaltenerDS,socketDaten.m_clientNumber);
                    }

                    //aktualiesiere die gui
                    if (!erhaltenerDS.Equals(""))
                    {

                        //Hier abfrage ob ehlateneDaten  == Socketname dann soll maybe nix passieren/ erstellen wen gewünscht  

                        Dispatcher.Invoke(
                        DispatcherPriority.Normal,
                        (ThreadStart)delegate { chat.AppendText(socketDaten.m_clientname + ": " + erhaltenerDS + "\n"); chat.ScrollToEnd(); });

                        //Continue Waiting for data on The Current socket
                        WaitforDatenfromClient(socketDaten.m_currentSocket, socketDaten.m_clientNumber, socketDaten.m_clientname);

                    }
                    else
                    {//primitiv aber funktioniert entfernt den current client
                        Dispatcher.Invoke(
                        DispatcherPriority.Normal,
                        (ThreadStart)delegate
                        {


                            if (!socketDaten.m_clientname.ToString().Equals("Client"))
                            {
                                Clientlist.Items.Remove(socketDaten.m_clientname);
                                ServerStats.Text = socketDaten.m_clientname + " Disconnected ! ";

                                m_workerSocketList[socketDaten.m_clientNumber - 1] = null;
                                workerSocket.Close();

                            }
                            else
                            {
                                Clientlist.Items.Remove(socketDaten.m_clientname + socketDaten.m_clientNumber);
                                ServerStats.Text = socketDaten.m_clientname + socketDaten.m_clientNumber + " Disconnected ! ";

                                m_workerSocketList[socketDaten.m_clientNumber - 1] = null;
                                workerSocket.Close();
                            }
                        });

                    }
                
                //Continue the waiting for Data on The Socket
                //WaitforDatenfromClient(socketDaten.m_currentSocket,socketDaten.m_clientNumber,socketDaten.m_clientname);

            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Debugger.Log(0, "1", "\nOnReceiveData: Socket has been closed\n");
            }
            catch (SocketException se) {
                MessageBox.Show(se.Message);

                if (se.ErrorCode == 10054) // Error code for Connection reset by peer
                {
                    m_workerSocketList[socketDaten.m_clientNumber - 1] = null;
                }
            }

        }

        //erstellt den Current Socket und legt die Größe fest
        public class SocketPacket
        {
           // public Socket m_currentSocket;
           // public byte[] dataBuffer = new byte[1024];

            // Constructor which takes a Socket and a client number
            public SocketPacket(Socket socket, int clientNummer, String name)
            {
                m_currentSocket = socket;
                m_clientNumber = clientNummer;
                m_clientname = name;
            }
            public Socket m_currentSocket;
            public int m_clientNumber;
            public String m_clientname;
            // Buffer to store the data sent by the client
            public byte[] dataBuffer = new byte[1024];
        }

        //Schließt die Clienten Verbindung 
        public void CloseConnection() {
            try
            {
                if (m_mainSocket != null)
                {
                    m_mainSocket.Close();
                }

                Socket workerSocket = null;
                for (int i = 0; i < m_workerSocketList.Count; i++)
                {
                    workerSocket = (Socket)m_workerSocketList[i];
                    if (workerSocket != null)
                    {
                       
                        workerSocket.Close();
                        //workerSocket = null;
                    }
                }	       
            }
            catch (Exception ze)
            {
                Dispatcher.Invoke(
                DispatcherPriority.Normal,
                (ThreadStart)delegate { ServerStats.Text = "Ein Fehler im Bereich Schließen"; });              
            }
        }


        //Sendet eine Benachrichtungen Message zur info der Connection || Vorläufig
        void SendMsgToClient(string msg, int clientNumber)
        {
            // Convert the reply to byte array
            byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg);

            Socket workerSocket = (Socket)m_workerSocketList[clientNumber - 1];
            workerSocket.Send(byData);
        }

        //Sende Nachrichten von Client an alle anderen clienten
        public void SendMessageToOtherClients(String msg,int curClient)
        {
            try
            {             
                msg = "Client " + curClient + ": " + msg + "\n";
                byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg);
                Socket workerSocket = null;
                //curClient = curClient-1;
                int x=1;
                for (int i = 0; i < m_workerSocketList.Count; i++)
                {

                    if (x != curClient)
                    {
                        workerSocket = (Socket)m_workerSocketList[i];
                        if (workerSocket != null)
                        {
                            if (workerSocket.Connected)
                            {
                                workerSocket.Send(byData);
                            }
                        }
                    }
                    else {
                        //Console.WriteLine("I ist gleich curent Client");
                    }
                    x++;
                }

                //Console.WriteLine("Entweder Erfolgreich oder halt aber nicht");
                
            }
            catch (Exception xe)
            {
                Dispatcher.Invoke(
                DispatcherPriority.Normal,
                (ThreadStart)delegate { ServerStats.Text = "Ein Fehler im Bereich Message"; });

            }

        }


        //Sendet eine Nachricht zum Clienten
        public void SendMessage() {

            try
            {
                    string msg = Chatbox.Text;
                    chat.AppendText("Server: "+msg+"\n");
                    msg = "Server: "+msg + "\n";
                    byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg);
                    Socket workerSocket = null;
                    for (int i = 0; i < m_workerSocketList.Count; i++)
                    {
                        workerSocket = (Socket)m_workerSocketList[i];
                        if (workerSocket != null)
                        {
                            if (workerSocket.Connected)
                            {
                                workerSocket.Send(byData);                             
                            }
                       }
                    }

                   Dispatcher.Invoke(
                   DispatcherPriority.Normal,
                   (ThreadStart)delegate { ServerStats.Text = "Nachricht erfolgreich gesendet"; });
              /*  }
                else {
                    ServerStats.Text = "Keine Connection mit Client vorhanden";
                }*/
                
            }
            catch (Exception xe)
            {
               Dispatcher.Invoke(
               DispatcherPriority.Normal,
               (ThreadStart)delegate { ServerStats.Text = "Ein Fehler im Bereich Message"; });
                
            }
        
        }


        //Sendet eine Nachricht zum Clienten || Action Button 
        private void SendButton_Click(object sender, RoutedEventArgs e)
        {
            SendMessage();
            Chatbox.Text = "";
            chat.ScrollToEnd();
        }


        //Schließt die verbindung zum Clienten || Action Button 
        private void CloseConnectionButton_Click(object sender, RoutedEventArgs e)
        {
           CloseConnection();

           Close();
        }


        //-----------------------------------------------------------------------


        //Startet den Server || Action Button  
        private async void ServerButton_Click(object sender, RoutedEventArgs e) {

           // Console.WriteLine("Hänge hier herum ");

            onConnectionStart();

           ServerButton.IsEnabled = false;

          Dispatcher.Invoke(
          DispatcherPriority.Normal,
          (ThreadStart)delegate { ServerStats.Text = "Server Gestartet"; });
        
        
        }

        //----------------------------------------------------------------------

       
        
        
        
       



        //############################################################################

    }
}
