// ####################################
// #                                  #
// #       Ping of Death - Bot        #
// #                by                #
// #    Markus Klinge aka Count Floyd #
// #                                  #
// ####################################
//
// Started from the HPB-Bot Alpha Source
// by Botman so Credits for a lot of the basic
// HL Server/Client Stuff goes to him
//
// bot_chat.cpp
//
// Contains parsing stuff & chat selection for chatting Bots

#include "bot_globals.h"


inline void StripClanTags (char *pszTemp1, char *pszReturn, char *cTag1, char *cTag2)
{
   // Strips out Words between Chars like []

   memset (pszReturn, 0, sizeof (pszReturn));

   unsigned char ucLen = strlen (pszTemp1);
   char* pszEndPattern;
   char* pszStartPattern = strstr (pszTemp1, cTag1);

   if (pszStartPattern)
   {
      pszStartPattern++;

      if (*pszStartPattern != 0)
      {
         pszEndPattern = strstr (pszTemp1, cTag2);

         if (pszEndPattern)
         {
            if (pszEndPattern - pszStartPattern < ucLen)
            {
               if (pszStartPattern - 1 != pszTemp1)
                  strncpy (pszReturn,pszTemp1, (pszStartPattern - pszTemp1) - 1);

               if (pszEndPattern < pszTemp1 + ucLen)
                  strcat (pszReturn,pszEndPattern + 1);
            }
         }
      }
   }
   else
      strcpy (pszReturn,pszTemp1);
}


void ConvertNameToHuman (char *pszName, char *pszReturn)
{
   // Converts given Names to a more human like style for output

   char szTemp1[80];
   char szTemp2[80];
   unsigned char ucLen = 1;
   unsigned char ucActLen = 1;

   memset (szTemp1, 0, sizeof (szTemp1));
   memset (szTemp2, 0, sizeof (szTemp1));

   StripClanTags (pszName, szTemp1, "[", "]");
   StripClanTags (szTemp1, szTemp2, "(", ")");
   StripClanTags (szTemp2, szTemp1, "{", "}");

   char *pszToken = strtok (szTemp1, " ");

   while (pszToken)
   {
      ucActLen = strlen (pszToken);

      if (ucActLen > ucLen)
      {
         strcpy (szTemp2, pszToken);
         ucLen = ucActLen;
      }

      pszToken = strtok (NULL, " '");
   }

   strcpy (pszReturn, szTemp2);
}


void BotPrepareChatMessage (bot_t *pBot, char *pszText)
{
   // Parses Messages from the Botchat, replaces Keywords
   // and converts Names into a more human style

   int iLen;
   char szNamePlaceholder[80];

   memset (&pBot->szMiscStrings, 0, sizeof (pBot->szMiscStrings));

   char *pszTextStart = pszText;
   char *pszPattern = pszText;
   edict_t *pTalkEdict = NULL;

   while (pszPattern)
   {
      // all replacement placeholders start with a %
      pszPattern = strstr (pszTextStart, "%");

      if (pszPattern)
      {
         iLen = pszPattern - pszTextStart;
         if (iLen > 0)
            strncpy (pBot->szMiscStrings, pszTextStart, iLen);
         pszPattern++;

         // Player with most frags ?
         if (*pszPattern == 'f')
         {
            int iHighestFrags = -9000; // just pick some start value
            int iCurrFrags;
            int iIndex = 0;
            int i;

            for (i = 0; i < gpGlobals->maxClients; i++)
            {
               if (!clients[i].IsUsed
                   || (clients[i].pEdict == pBot->pEdict))
                  continue;

               iCurrFrags = clients[i].pEdict->v.frags;

               if (iCurrFrags > iHighestFrags)
               {
                  iHighestFrags = iCurrFrags;
                  iIndex = i;
               }
            }

            // fix fix fix, all day long...
            if (iIndex < gpGlobals->maxClients)
            {
               pTalkEdict = clients[iIndex].pEdict;

               if (!FNullEnt (pTalkEdict))
               {
                  ConvertNameToHuman ((char *) STRING (pTalkEdict->v.netname), szNamePlaceholder);
                  strcat (pBot->szMiscStrings, szNamePlaceholder);
               }
            }
         }

         // Mapname ?
         else if (*pszPattern == 'm')
            strcat (pBot->szMiscStrings, STRING (gpGlobals->mapname));

         // Roundtime ?
         else if (*pszPattern == 'r')
         {
            char szTime[] = "000:00";
            int iTime = (int) (g_fTimeRoundEnd - gpGlobals->time);

            sprintf (szTime, "%02d:%02d", iTime / 60, iTime % 60);
            strcat (pBot->szMiscStrings, szTime);
         }

         // Chat Reply ?
         else if (*pszPattern == 's')
         {
            // crash fixes, crash fixes...
            if ((pBot->SaytextBuffer.iEntityIndex > 0)
                && (pBot->SaytextBuffer.iEntityIndex <= gpGlobals->maxClients))
            {
               pTalkEdict = INDEXENT (pBot->SaytextBuffer.iEntityIndex);

               if (!FNullEnt (pTalkEdict))
               {
                  ConvertNameToHuman ((char *) STRING (pTalkEdict->v.netname), szNamePlaceholder);
                  strcat (pBot->szMiscStrings, szNamePlaceholder);
               }
            }
         }

         // Teammate alive ?
         else if (*pszPattern == 't')
         {
	         int i;

            for (i = 0; i < gpGlobals->maxClients; i++)
            {
               if (!clients[i].IsUsed
                   || !clients[i].IsAlive
                   || (clients[i].iTeam != pBot->bot_team)
                   || (clients[i].pEdict == pBot->pEdict))
                  continue;

               break;
            }

            if (i < gpGlobals->maxClients)
            {
               pTalkEdict = clients[i].pEdict;

               if (!FNullEnt (pTalkEdict))
               {
                  ConvertNameToHuman ((char *) STRING (pTalkEdict->v.netname), szNamePlaceholder);
                  strcat (pBot->szMiscStrings, szNamePlaceholder);
               }
            }
         }

         else if (*pszPattern == 'v')
         {
            pTalkEdict = pBot->pLastVictim;

            if (!FNullEnt (pTalkEdict))
            {
               ConvertNameToHuman ((char *) STRING (pTalkEdict->v.netname), szNamePlaceholder);
               strcat (pBot->szMiscStrings, szNamePlaceholder);
            }
         }

         pszPattern++;
         pszTextStart = pszPattern;
      }
   }

   strcat (pBot->szMiscStrings, pszTextStart);

   // removes trailing '\n'
   iLen = strlen (pBot->szMiscStrings);
   if (pBot->szMiscStrings[iLen - 1] == '\n')
      pBot->szMiscStrings[iLen - 1] = 0;

   return;
}


bool BotCheckKeywords(char *pszMessage, char *pszReply)
{
   replynode_t *pReply = pChatReplies;
   char szKeyword[128];
   char *pszCurrKeyword;
   char *pszKeywordEnd;
   int iLen, iRandom;
   char cNumRetries;

   while (pReply != NULL)
   {
      pszCurrKeyword = (char *) &pReply->szKeywords;

      while (pszCurrKeyword)
      {
         pszKeywordEnd = strstr (pszCurrKeyword, "@");

         if (pszKeywordEnd)
         {
            iLen = pszKeywordEnd - pszCurrKeyword;
            strncpy (szKeyword, pszCurrKeyword, iLen);
            szKeyword[iLen] = 0x0;

            // Parse Text for occurences of keywords
            char *pPattern = strstr (pszMessage, szKeyword);

            if (pPattern)
            {
               STRINGNODE *pNode = pReply->pReplies;

               if (pReply->cNumReplies == 1)
                  strcpy (pszReply, pNode->szString);
               else
               {
                  cNumRetries = 0;
                  do
                  {
                     iRandom = RANDOM_LONG (1, pReply->cNumReplies);
                     cNumRetries++;
                  } while ((iRandom == pReply->cLastReply) && (cNumRetries <= pReply->cNumReplies));

                  pReply->cLastReply = iRandom;
                  cNumRetries = 1;

                  while (cNumRetries < iRandom)
                  {
                     pNode = pNode->Next;
                     cNumRetries++;
                  }

                  strcpy (pszReply, pNode->szString);
               }

               return (TRUE);
            }

            pszKeywordEnd++;

            if (*pszKeywordEnd == 0)
               pszKeywordEnd = NULL;
         }

         pszCurrKeyword = pszKeywordEnd;
      }

      pReply = pReply->pNextReplyNode;
   }

   // Didn't find a keyword ? // 50% of the time use some universal reply
   if (RANDOM_LONG (1, 100) < 50)
   {
      strcpy (pszReply, szNoKwChat[RANDOM_LONG (0, iNumNoKwChats - 1)]);
      return (TRUE);
   }

   return (FALSE);
}


bool BotParseChat (bot_t *pBot, char *pszReply)
{
   char szMessage[512];
   int iMessageLen;
   int i = 0;

   // Copy to safe place
   strcpy (szMessage, pBot->SaytextBuffer.szSayText);

   // Text to uppercase for Keyword parsing
   iMessageLen = strlen (szMessage);
   for (i = 0; i < iMessageLen; i++)
      szMessage[i] = (char) toupper ((int) szMessage[i]);
   szMessage[i] = 0;

   // Find the : char behind the name to get the start of the real text
   while (i <= iMessageLen)
   {
      if (szMessage[i] == ':')
         break;
      i++;
   }

   return (BotCheckKeywords (&szMessage[i], pszReply));
}


bool BotRepliesToPlayer (bot_t *pBot)
{
   char szText[256];

   if ((pBot->SaytextBuffer.iEntityIndex > 0)
       && (pBot->SaytextBuffer.iEntityIndex <= gpGlobals->maxClients)
       && (pBot->SaytextBuffer.szSayText[0] != 0))
   {
      if (pBot->SaytextBuffer.fTimeNextChat < gpGlobals->time)
      {
         if ((RANDOM_LONG (1, 100) < pBot->SaytextBuffer.cChatProbability)
             && BotParseChat (pBot, (char *) &szText))
         {
            BotPrepareChatMessage (pBot, (char *) &szText);
            BotPushMessageQueue (pBot, MSG_CS_SAY);
            pBot->SaytextBuffer.iEntityIndex = -1;
            pBot->SaytextBuffer.szSayText[0] = 0x0;
            pBot->SaytextBuffer.fTimeNextChat = gpGlobals->time + pBot->SaytextBuffer.fChatDelay;
            return (TRUE);
         }

         pBot->SaytextBuffer.iEntityIndex = -1;
         pBot->SaytextBuffer.szSayText[0] = 0x0;
      }
   }

   return (FALSE);
}
