﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Collections;



namespace ChatServer
{
    class Program
    {
        public static Hashtable clientsList = new Hashtable();

        static void Main(string[] args)
        {
            Database.Connect();
            TcpListener serverSocket = new TcpListener(IPAddress.Any, 8888);
            TcpClient clientSocket = new TcpClient();

            Console.WriteLine("Chat server started. Expecting connections");
            serverSocket.Start();
 

            // continuously wait for new incoming connections and spawn a thread for each new client connection
            while (true)
            {
                //Accepts a connection from the client to the server
                clientSocket = serverSocket.AcceptTcpClient();

                //Creating Array of bytes (will be used in network stream
                byte[] bytesFrom = new byte[10000];

                //This is the string which 
                string dataFromClient = null;

                NetworkStream networkStream = clientSocket.GetStream();
                networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
                dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
                string username = dataFromClient.Substring(0, dataFromClient.IndexOf("/"));
                string password = dataFromClient.Substring(dataFromClient.IndexOf("/") + 1, dataFromClient.IndexOf("$") - dataFromClient.IndexOf("/") - 1);

                // If password is wrong, client should not continue
                if (Database.CheckLogin(username, password) == false)
                {
                    Console.WriteLine("Wrong password for username " + username);
                     Byte[] sendBuffer = Encoding.ASCII.GetBytes("Wrong password");
                     networkStream.Write(sendBuffer, 0, sendBuffer.Length);
                     networkStream.Flush();
                     networkStream.Close();
                     clientSocket.Close();
                    continue;
                }


                clientsList.Add(username, clientSocket);

                broadcast(username + " joined ", dataFromClient, false);

                Console.WriteLine(username + " joined chat room ");
                handleClinet client = new handleClinet();
                client.startClient(clientSocket, username, clientsList);
            }

            clientSocket.Close();
            serverSocket.Stop();
            Database.Disconnect();
            Console.WriteLine("exit");
            Console.ReadLine();
        }

        public static void broadcast(string msg, string uName, bool showusername)
        {
            foreach (DictionaryEntry Item in clientsList)
            {
                TcpClient broadcastSocket;
                broadcastSocket = (TcpClient)Item.Value;
                if (broadcastSocket.Connected == false)
                {
                    // do not send anything to disconnected socket
                    continue;
                }
                NetworkStream broadcastStream = broadcastSocket.GetStream();
                Byte[] broadcastBytes = null;

                if (showusername == true)
                {
                    broadcastBytes = Encoding.ASCII.GetBytes(uName + " says : " + msg);
                }
                else
                {
                    broadcastBytes = Encoding.ASCII.GetBytes(msg);
                }

                broadcastStream.Write(broadcastBytes, 0, broadcastBytes.Length);
                broadcastStream.Flush();
            }
        } 
    }

    public class handleClinet
    {
        TcpClient clientSocket;
        string clNo;
        Hashtable clientsList;

        // client initialization
        public void startClient(TcpClient inClientSocket, string clineNo, Hashtable cList)
        {
            this.clientSocket = inClientSocket;
            this.clNo = clineNo;
            this.clientsList = cList;
            Thread ctThread = new Thread(doChat);
            ctThread.Start();
        }

        private void doChat()
        {
            byte[] bytesFrom = new byte[10025];
            string dataFromClient = null;
            Byte[] sendBytes = null;
            string serverResponse = null;

            // chat loop
            while ((true))
            {
                try
                {
                    NetworkStream networkStream = clientSocket.GetStream();
                    networkStream.Read(bytesFrom, 0, (int)clientSocket.ReceiveBufferSize);
                    dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
                    dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$"));
                    if (dataFromClient == "/exit")
                    {
                        Console.WriteLine("Client exited");
                        Byte[] sendBuffer = Encoding.ASCII.GetBytes("Disconnected");
                        networkStream.Write(sendBuffer, 0, sendBuffer.Length);
                        networkStream.Flush();
                        networkStream.Close();
                        clientSocket.Close();
                        clientsList.Remove(clNo);
                        break;
                    }
                        
                    Console.WriteLine("From client - " + clNo + " : " + dataFromClient);
                    
                    Program.broadcast(dataFromClient, clNo, true);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }//end while
        }//end doChat
    } //end class handleClinet
}
