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

#include <vcl.h>
#pragma hdrstop

#include "ChatServerMainForm.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "IdBaseComponent"
#pragma link "IdComponent"
#pragma link "IdContext"
#pragma link "IdCustomTCPServer"
#pragma link "IdTCPServer"
#pragma resource "*.dfm"
TfrmChatServerMain *frmChatServerMain;

// For Unicode support
#define ENCODING
TEncoding* CharacterEncoding = TIdTextEncoding::UTF8;

//---------------------------------------------------------------------------
__fastcall TfrmChatServerMain::TfrmChatServerMain(TComponent* Owner) : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfrmChatServerMain::btnStartClick(TObject *Sender)
{
  tcpServer->Active = true;
}
//---------------------------------------------------------------------------
void __fastcall TfrmChatServerMain::FormCreate(TObject *Sender)
{
  mmoHistory->Align = alClient;
}
//---------------------------------------------------------------------------
void __fastcall TfrmChatServerMain::tcpServerExecute(TIdContext *AContext)
{
    // Check message queue
  String s = AContext->Connection->IOHandler->ReadLn("", 50);

    // If no message, nothing to do
  if (AContext->Connection->IOHandler->ReadLnTimedout)
    return;

    // Message received. What kind? (Currently only supports SEND and CMD)
  if (!CompareText(s.SubString(1, 4), "SEND"))
  {
    UpdateOutput(s, dirRECV);
    String msg = "MESG " + AContext->Binding()->PeerIP + ":" + AContext->Binding()->PeerPort;
    msg += s.SubString(5, s.Length());
    BroadcastMessage(Trim(msg));
  }
  else if (!CompareText(s.SubString(1, 3), "CMD"))
  {
    UpdateOutput(s, dirRECV);
    TStringList *list = new TStringList;
    list->DelimitedText = s;
    list->Delimiter = ' ';

      // Client wants to quit the session
    if (!CompareText(list->Strings[1].SubString(1, 4), "QUIT"))
      AContext->Connection->Disconnect();
      
    delete list;
  }
}
//---------------------------------------------------------------------------
void TfrmChatServerMain::UpdateOutput(const String& s, TDirection dir)
{
  String d = "";
  TColor attr = clBlack;
  if (dir == dirSEND)
  {
    d = "SEND --> ";
    attr = clBlue;
  }
  else if (dir == dirRECV)
  {
    d = "RECV <-- ";
    attr = clRed;
  }

  /*
      This works differently than I expected. If you set the attributes, and
      then Add() lines to the control, everything works fine. But, if you
      Insert() the lines, you need to insert them before setting the attribute.
      If you don't, you'll get the new attribute applied to the item at index
      0, which will be scrolled down to index 1 after the insert and will have
      the new attribute applied.
  */
  String str = d + s;
  mmoHistory->Lines->BeginUpdate();
  if (chkInsertLinesAtTop->Checked)
  {
      // Insert the line
    mmoHistory->Lines->Insert(0, str);

      // Select the line and then apply the color (attributes) to the selection
    mmoHistory->SelStart = 0;
    mmoHistory->SelLength = str.Length();
    mmoHistory->SelAttributes->Color = attr;
  }
  else
  {
    mmoHistory->SelAttributes->Color = attr;
    mmoHistory->Lines->Add(str);
  }
  mmoHistory->Lines->EndUpdate();
}
//---------------------------------------------------------------------------
void __fastcall TfrmChatServerMain::tcpServerConnect(TIdContext *AContext)
{
  String port = AContext->Binding()->PeerPort;
  String host = AContext->Binding()->PeerIP;
  UpdateOutput("Connected: " + host + ":" + port);
  #ifdef ENCODING
    AContext->Connection->IOHandler->DefStringEncoding = CharacterEncoding;
  #endif

  AContext->Connection->IOHandler->WriteLn("MESG Server Welcome to Simple Chat!");
}
//---------------------------------------------------------------------------
void __fastcall TfrmChatServerMain::tcpServerDisconnect(TIdContext *AContext)
{
  String port = AContext->Binding()->PeerPort;
  String host = AContext->Binding()->PeerIP;
  UpdateOutput("Disconnected: " + host + ":" + port);
  AContext->Connection->IOHandler->WriteLn("Goodbye.");
}
//---------------------------------------------------------------------------
void __fastcall TfrmChatServerMain::tcpServerAfterBind(TObject *Sender)
{
  for (int i = 0; i < tcpServer->Bindings->Count; i++)
  {
    String s = "Listening on ";
    s += tcpServer->Bindings->Items[0]->IP;
    s += ":";
    s += tcpServer->Bindings->Items[0]->Port;
    UpdateOutput(s);
  }
}
//---------------------------------------------------------------------------
void TfrmChatServerMain::BroadcastMessage(const String& msg)
{
    // Get list of clients (threads) and broadcast the message to each client.
  TList *clients = tcpServer->Contexts->LockList();
    for (int i = 0; i < clients->Count; i++)
    {
      TIdContext *context = reinterpret_cast<TIdContext *>(clients->Items[i]);
      context->Connection->IOHandler->WriteLn(Trim(msg));
    }
  tcpServer->Contexts->UnlockList();
}
//---------------------------------------------------------------------------
void __fastcall TfrmChatServerMain::btnBroadcastClick(TObject *Sender)
{
  String msg = "MESG Server " + ledtCommand->Text;
  UpdateOutput(msg, dirSEND);
  BroadcastMessage(msg);
}
//---------------------------------------------------------------------------