#include <winsock2.h>
#include <iostream.h>

#define MAXLEN_CHAT_NAME         256
#define CHAT_PORT                2345
#define MAX_CONNECTIONS          10


class ChatConnection
{
  public:
    ChatConnection();
    ~ChatConnection();
    int     getState();
    int     getID();
    char*   getUserChatName();
    char*   getUserIp();
    char*   getUserHostName();
    SOCKET* getServerSocket();
    HANDLE  getRecvThreadHandle();

    char* setUserChatName();
    void  setState(int newState);
    void  setID(int newID);
    void  setServerSocket(SOCKET newServerSocket);
    void  setRecvThreadHandle(HANDLE newHandle);

    bool  kickUser();


  private:
    HANDLE itsRecvThreadHandle;
    DWORD  itsRecvThreadID;
    int    itsID;
    int    itsState;
    char   itsUserChatName[MAXLEN_CHAT_NAME];
    SOCKET itsServerSocket;
};


ChatConnection::ChatConnection()
{
  itsState=0;
}

ChatConnection::~ChatConnection()
{
}

int ChatConnection::getState()
{
  return itsState;
}

void ChatConnection::setState(int newState)
{
  itsState = newState;
}

int ChatConnection::getID()
{
  return itsID;
}

void ChatConnection::setID(int newID)
{
  itsID = newID;
}

void ChatConnection::setServerSocket(SOCKET newServerSocket)
{
  itsServerSocket=newServerSocket;
}

void  ChatConnection::setRecvThreadHandle(HANDLE newHandle)
{
  itsRecvThreadHandle=newHandle;
}

HANDLE ChatConnection::getRecvThreadHandle()
{
  return itsRecvThreadHandle;
}

SOCKET* ChatConnection::getServerSocket()
{
  return &itsServerSocket;
}

bool ChatConnection::kickUser()
{
  long rc;
  DWORD ExitCode;

  cout << "Connection Control: Getting Exit Code..";
  rc = GetExitCodeThread(itsRecvThreadHandle, &ExitCode);
  if(rc == 0)
  {
    cout << "\t\t\t\tfail, Error: " << GetLastError() << endl;
    return false;
  }
  else
  {
    cout << "\t\t\t\tdone" << endl;
  }

  cout << "Connection Control: Terminating Recv Thread..";
  rc = TerminateThread(itsRecvThreadHandle, ExitCode);
  if(rc == 0)
  {
    cout << "\t\t\tfail, Error: " << GetLastError() << endl;
  }
  else
  {
    cout << "\t\t\tdone" << endl;
  }

  cout << "Connection Control: Closing Socket";
  rc = closesocket(itsServerSocket);
  if(rc == SOCKET_ERROR)
  {
    cout << "\t\t\t\tfail, Error: " << WSAGetLastError() << endl;
    return false;
  }
  else
  {
    cout << "\t\t\t\tdone" << endl;
  }
  return true;
}


// Globale Variablen
long gCountConnections=0;
ChatConnection  ChatConnections[MAX_CONNECTIONS];

// Prototypen
long WinsockStartup();
long GetNextFreeChatConnection(ChatConnection *ChatConnectionArray, int count);
// Thread Prototypen
unsigned long WINAPI AcceptConnection();
unsigned long WINAPI RecvThread(void* ChatConnectionToUse);


int main()
{
  long rc;
  char ServerCommand[256];

  HANDLE hAcceptThread;
  DWORD  idAcceptThread;

  for(int i=0; i<MAX_CONNECTIONS;i++)
  {
    ChatConnections[i].setState(0);
  }

  cout << "Main Control: Starting Winsock..";
  rc = WinsockStartup();
  if (rc == 0)
  {
    cout << "\t\t\t\tdone" << endl;
  }
  else
  {
    cout << "\t\t\t\tfail" << endl;
  }

  cout << "Main Control: Starting Connection-Accept-Thread..";
  hAcceptThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)AcceptConnection, NULL, NULL, &idAcceptThread);
  if (hAcceptThread == NULL)
  {
    cout << "\t\tfail" << endl;
  }
  else
  {
    cout << "\t\tdone" << endl;
  }
  cout << "Main Control: Waiting.." << endl;
  while(1)
  {
    cin >> ServerCommand;
    if(strncmp(ServerCommand,"/kick:",6) == 0)
    {
      cout << "Main Control: Kicking User " << ServerCommand[6] << endl;
      ChatConnections[atoi(&ServerCommand[6])].kickUser();
    }
  }
}

long GetNextFreeChatConnection(ChatConnection *ChatConnectionArray, int count)
{
  for(int i=0; i<count;i++,ChatConnectionArray++)
  {
    if (ChatConnectionArray->getState() == 0)
    {
      return i;
    }
  }
  return -1;
}


unsigned long WINAPI RecvThread(void* Parameter)
{
  ChatConnection* ChatConnectionToUse;
  ChatConnectionToUse = (ChatConnection*) Parameter;

  char recvBuf[256];
  char sendBuf[256];

  int BytesReceived=0;

  SOCKET  *ConnectionToUseSocket;
  SOCKET  *ptrSocket;

  long rc;

  while(1)
  {
    ConnectionToUseSocket = ChatConnectionToUse->getServerSocket();
    BytesReceived = recv(*ConnectionToUseSocket,recvBuf,sizeof(recvBuf),NULL);
    if (BytesReceived == 0)
    {
      cout <<  "Recv Thread of User " << ChatConnectionToUse->getID() << ": User has closed the Connection !" << endl;
      ChatConnectionToUse->setState(0);
      closesocket(*ConnectionToUseSocket);
      gCountConnections -=1;
      cout << "Recv Thread of User " << ChatConnectionToUse->getID() << ": Number of connections changed to " << gCountConnections << endl;
      return -2;

    }
    recvBuf[BytesReceived] = '\0';
    cout << "Recv Thread of User " << ChatConnectionToUse->getID() << ": " << rc << " Bytes Data received: " << recvBuf << endl;
    cout << "Recv Thread of User " << ChatConnectionToUse->getID() << ": " << "Sending it to the other Chat Users.." << endl;
    for(int i=0;i<MAX_CONNECTIONS;i++)
    {
      if(ChatConnections[i].getState() == 1)
      {
        strcpy(sendBuf,recvBuf);
        ptrSocket = ChatConnections[i].getServerSocket();
        rc = send(*ptrSocket, sendBuf, BytesReceived ,NULL);
        if (rc == 0)
        {
          cout << "Recv Thread of User " << ChatConnectionToUse->getID() << ": " << " Cannot send, Error: " << WSAGetLastError() << endl;
        }
        cout << "Recv Thread of User " << ChatConnectionToUse->getID() << ": " << rc << " Bytes sent to User " << ChatConnections[i].getID() << endl;
      }
    }
  }
}


unsigned long WINAPI AcceptConnection()
{

  long rc;
  int addrlen;

  int NextFreeConnection;

  HANDLE hNewRecvThread;
  DWORD  idNewRecvThread;

  SOCKET AcceptSocket;
  SOCKADDR_IN AcceptSocketAddr;


  cout << "Accept Connection Thread: Starting..";
  cout << "\t\t\t\tdone" << endl;

  AcceptSocket = socket(AF_INET, SOCK_STREAM, 0);
  AcceptSocketAddr.sin_family = AF_INET;
  AcceptSocketAddr.sin_port = htons(CHAT_PORT);
  AcceptSocketAddr.sin_addr.s_addr = 0;

  cout << "Accept Connection Thread: Binding Socket..";
  rc = bind (AcceptSocket,(SOCKADDR*) &AcceptSocketAddr, sizeof(AcceptSocketAddr));
  if (rc == 0)
  {
    cout << "\t\t\tdone" << endl;
  }
  else
  {
    cout << "\t\t\tfail, Error: " << WSAGetLastError() << endl;
  }

  cout << "Accept Connection Thread: Changing to Listen Mode..";
  rc = listen(AcceptSocket,NULL);
  if (rc == 0)
  {
    cout << "\t\tdone" << endl;
  }
  else
  {
    cout << "\t\tfail, Error: " <<  WSAGetLastError() << endl;
  }


  while(1)
  {
    SOCKET *AcceptedConnection = new SOCKET;

    addrlen = sizeof(AcceptSocketAddr);
    cout << "Accept Connection Thread: Waiting for a Connection.." << endl;
    *AcceptedConnection = accept(AcceptSocket ,(SOCKADDR*)&AcceptSocketAddr, &addrlen);
    if (*AcceptedConnection == INVALID_SOCKET)
    {
      cout << "Accept Connection Thread: Error by accepting connections, Error: " << WSAGetLastError() << endl;
      return -2;
    }
    else
    {
      cout << "Accept Connection Thread: New Connection accepted !" << endl;
      cout << "Accept Connection Thread: Entering the Chat.." << endl;
      if(GetNextFreeChatConnection(ChatConnections, MAX_CONNECTIONS) == -1)
      {
        cout << "Accept Connection Thread: Error, cannot accept User into Chat, maximum reached !" << endl;
        return -2;
      }
      else
      {
        NextFreeConnection = GetNextFreeChatConnection(ChatConnections, MAX_CONNECTIONS);
        ChatConnections[NextFreeConnection].setState(1);
        ChatConnections[NextFreeConnection].setID(NextFreeConnection);
        ChatConnections[NextFreeConnection].setServerSocket(*AcceptedConnection);
        cout << "Accept Connection Thread: User got ID " << NextFreeConnection << endl;
        cout << "Accept Connection Thread: Starting Recv Thread for new User..";
        hNewRecvThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)RecvThread, (void*)&ChatConnections[NextFreeConnection], NULL, &idNewRecvThread);

        if (hNewRecvThread == NULL)
        {
          cout << "\tfail" << endl;
        }
        else
        {
          cout << "\tdone" << endl;
        }
        ChatConnections[NextFreeConnection].setRecvThreadHandle(hNewRecvThread);
        gCountConnections +=1;
        cout << "Accept Connection Thread: Number of connections changed to " << gCountConnections << endl;
      }
    }
  }
}


long WinsockStartup()
{
  long rc;

  WORD wVersionRequested;
  WSADATA wsaData;
  wVersionRequested = MAKEWORD( 2, 2 );

  rc = WSAStartup( wVersionRequested, &wsaData );
  return rc;
}
