// interlocutor.cpp
 
 
 
#include "common.h"
 
 
 
 
 
// global variables used in this module only
 
static const wchar_t rtf_header[] = L"{\\rtf1\\ansi"
 
                                       L"{\\fonttbl\\f0\\f{Comic Sans MS};}"
 
                                       L"{\\colortbl;\\red0\\green0\\blue0;\\red192\\green192\\blue192;\\red0\\green0\\blue100;\\red160\\green160\\blue160;}"
 
                                       L"\\f0\\fs18\\cf3\\pard\n"
 
                                    L"}\n";
 
static wchar_t text_buffer[1024 * 1024];
 
 
 
 
 
void Interlocutors_Init (void)
 
{
 
   // this function resets the interlocutors array, preparing it for a new use
 
 
 
   interlocutors = NULL; // reset the array pointer
 
   interlocutor_count = 0; // and set the element count to zero
 
 
 
   return; // that's all there is
 
}
 
 
 
 
 
void Interlocutors_Shutdown (void)
 
{
 
   // this function empties the interlocutors array, closing windows and freeing allocated resources
 
 
 
   int interlocutor_index;
 
 
 
   // for each player card we know...
 
   for (interlocutor_index = 0; interlocutor_index < interlocutor_count; interlocutor_index++)
 
   {
 
      if (IsWindow (interlocutors[interlocutor_index].hWnd))
 
         DestroyWindow (interlocutors[interlocutor_index].hWnd); // destroy any eventual opened chat window
 
      SAFE_free ((void **) &interlocutors[interlocutor_index].dialogtext); // free the interlocutor's dialog text buffer
 
      interlocutors[interlocutor_index].dialogtext_size = 0; // and reset this buffer's size
 
   }
 
   SAFE_free ((void **) &interlocutors); // free the chat interlocutors array
 
   interlocutor_count = 0; // and reset the interlocutors count
 
 
 
   return; // finished
 
}
 
 
 
 
 
interlocutor_t *Interlocutor_FindOrCreate (const wchar_t *nickname)
 
{
 
   // this function returns a pointer to the interlocutor whose nickname is specified,
 
   // creating a new slot for him in the interlocutors array if he can't be found.
 
 
 
   interlocutor_t *interlocutor;
 
   int interlocutor_index;
 
 
 
   // see if we already know this interlocutor, loop through all of them...
 
   for (interlocutor_index = 0; interlocutor_index < interlocutor_count; interlocutor_index++)
 
      if (_wcsicmp (nickname, interlocutors[interlocutor_index].nickname) == 0)
 
         break; // break as soon as we find it
 
 
 
   // have we found it ?
 
   if (interlocutor_index < interlocutor_count)
 
   {
 
      // get a quick access to interlocutor
 
      interlocutor = &interlocutors[interlocutor_index];
 
 
 
      // is this interlocutor NO LONGER active ?
 
      if (!interlocutor->is_active)
 
      {
 
         interlocutor->hWnd = NULL; // reset some values for this interlocutor
 
         interlocutor->current_displaypicture = 0;
 
         interlocutor->is_active = true; // remember it's becoming active again
 
 
 
         Window_Chat (interlocutor_index); // his chat window has been closen, so fire it up again
 
      }
 
      else
 
      {
 
         // does the chat window exist AND is it currently NOT the foreground window ?
 
         if (IsWindow (interlocutor->hWnd) && (GetForegroundWindow () != interlocutor->hWnd))
 
         {
 
            interlocutor->update_dialog = true; // update this interlocutor's window
 
            ShowWindow (interlocutor->hWnd, SW_RESTORE); // restore it from the taskbar
 
            SetForegroundWindow (interlocutor->hWnd); // bring the window to front if necessary
 
         }
 
      }
 
   }
 
 
 
   // else we haven't found it
 
   else
 
   {
 
      // so resize our interlocutors array and add this interlocutor to it
 
      interlocutors = (interlocutor_t *) SAFE_realloc (interlocutors, interlocutor_count, interlocutor_count + 1, sizeof (interlocutor_t), false);
 
      interlocutor_count++; // we know now one interlocutor more
 
 
 
      // get a quick access to interlocutor. Only do it after the realloc, as the array may have moved.
 
      interlocutor = &interlocutors[interlocutor_index];
 
 
 
      // reset some values for this new interlocutor
 
      wcscpy_s (interlocutor->nickname, WCHAR_SIZEOF (interlocutor->nickname), nickname); // save nickname
 
      interlocutor->hWnd = NULL;
 
      interlocutor->dialogtext = NULL; // no dialog text yet
 
      interlocutor->dialogtext_size = 0;
 
      interlocutor->current_displaypicture = 0;
 
      interlocutor->is_active = true;
 
 
 
      // and fire up the new chat window
 
      Window_Chat (interlocutor_index);
 
   }
 
 
 
   return (interlocutor); // return pointer to interlocutor
 
}
 
 
 
 
 
interlocutor_t *Interlocutor_FindByWindowHandle (HWND window_handle)
 
{
 
   // this function returns a pointer to the interlocutor whose chat window handle
 
   // matches the one specified. If no such chat window exists, NULL is returned.
 
 
 
   int interlocutor_index;
 
 
 
   // cycle through all interlocutors and find the one we want...
 
   for (interlocutor_index = 0; interlocutor_index < interlocutor_count; interlocutor_index++)
 
      if (interlocutors[interlocutor_index].hWnd == window_handle)
 
         return (&interlocutors[interlocutor_index]); // found it, return its pointer
 
 
 
   return (NULL); // we couldn't find that interlocutor, return FALSE
 
}
 
 
 
 
 
void Interlocutor_Chat (interlocutor_t *interlocutor, const wchar_t *sender, bool is_localsender, const wchar_t *fmt, ...)
 
{
 
   // helper function to quickly add an entry to an interlocutor's RTF chat window
 
 
 
   // {\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard
 
   // This is some {\b bold} text.\par
 
   // }
 
   // NOTE: color table indices are 1-based
 
 
 
   static const wchar_t rtf_template[] = L"{\\cf%d\\b %s}\\par\n"
 
                                         L"   %s\\par\n"
 
                                         L"}\n";
 
   static const int template_length = WCHAR_SIZEOF (rtf_template) - 6 - 2; // don't take in account closing brace, it's counted in the header
 
   va_list argptr;
 
   int sender_length;
 
   int text_length;
 
   int smiley_index;
 
   smiley_t *smiley;
 
   wchar_t *smiley_text;
 
   wchar_t *after_smiley;
 
 
 
   // if dialog is just starting, we need to insert a RTF header
 
   if (interlocutor->dialogtext == NULL)
 
   {
 
      interlocutor->dialogtext = (wchar_t *) SAFE_malloc (WCHAR_SIZEOF (rtf_header), sizeof (wchar_t), false); // allocate space for it
 
      wcscpy_s (interlocutor->dialogtext, WCHAR_SIZEOF (rtf_header), rtf_header); // copy RTF header
 
      interlocutor->dialogtext_size = WCHAR_SIZEOF (rtf_header); // save RTF header length
 
   }
 
 
 
   // concatenate all the arguments in one string, but have a space before and after it
 
   wcscpy_s (text_buffer, WCHAR_SIZEOF (text_buffer), L" ");
 
   va_start (argptr, fmt);
 
   vswprintf_s (&text_buffer[1], WCHAR_SIZEOF (text_buffer), fmt, argptr);
 
   va_end (argptr);
 
   wcscat_s (text_buffer, WCHAR_SIZEOF (text_buffer), L" ");
 
 
 
   // as long as there are smilies in this text, replace them with their equivalent data
 
   for (smiley_index = 0; smiley_index < smiley_count; smiley_index++)
 
   {
 
      smiley = &smilies[smiley_index]; // quick access to smiley
 
 
 
      while ((smiley_text = wcsistr (text_buffer, smiley->name)) != NULL)
 
      {
 
         after_smiley = smiley_text + wcslen (smiley->name);
 
         memmove (smiley_text + smiley->rtf_len, after_smiley, (wcslen (after_smiley) + 1) * sizeof (wchar_t));
 
         memcpy (smiley_text, smiley->rtf_data, smiley->rtf_len * sizeof (wchar_t));
 
      }
 
   }
 
 
 
   // get sender and text lengths
 
   sender_length = wcslen (sender);
 
   text_length = wcslen (text_buffer);
 
 
 
   // resize the chat text buffer
 
   interlocutor->dialogtext = (wchar_t *) SAFE_realloc (interlocutor->dialogtext,
 
                                                        interlocutor->dialogtext_size,
 
                                                        interlocutor->dialogtext_size + template_length + 1 + sender_length + text_length,
 
                                                        sizeof (wchar_t), false);
 
 
 
   // append the message to the chat text and save the new text length (overwrite the closing brace)
 
   swprintf_s (&interlocutor->dialogtext[wcslen (interlocutor->dialogtext) - 2],
 
               template_length + 1 + sender_length + text_length + 2 + 1, // +1 for null terminator
 
               rtf_template, (is_localsender ? 2 : 1), sender, text_buffer);
 
   interlocutor->dialogtext_size += template_length + 1 + sender_length + text_length; // save new string length
 
 
 
   // and tell our chat window there's a new message to display
 
   interlocutor->update_dialog = true;
 
 
 
   return; // finished
 
}
 
 
 
 
 
void Interlocutor_Notify (interlocutor_t *interlocutor, const wchar_t *fmt, ...)
 
{
 
   // helper function to quickly add an entry to an interlocutor's RTF chat window
 
 
 
   // {\rtf1\ansi{\fonttbl\f0\fswiss Helvetica;}\f0\pard
 
   // This is some {\b bold} text.\par
 
   // }
 
   // NOTE: color table indices are 1-based
 
 
 
   static const wchar_t rtf_template[] = L"{\\cf4\\fs16\\i  %s}\\par\n"
 
                                         L"}\n";
 
   static const int template_length = WCHAR_SIZEOF (rtf_template) - 2 - 2; // don't take in account closing brace, it's counted in the header
 
   va_list argptr;
 
   int text_length;
 
 
 
   // if dialog is just starting, we need to insert a RTF header
 
   if (interlocutor->dialogtext == NULL)
 
   {
 
      interlocutor->dialogtext = (wchar_t *) SAFE_malloc (WCHAR_SIZEOF (rtf_header), sizeof (wchar_t), false); // allocate space for it
 
      wcscpy_s (interlocutor->dialogtext, WCHAR_SIZEOF (rtf_header), rtf_header); // copy RTF header
 
      interlocutor->dialogtext_size = WCHAR_SIZEOF (rtf_header); // save RTF header length
 
   }
 
 
 
   // concatenate all the arguments in one string
 
   va_start (argptr, fmt);
 
   vswprintf_s (text_buffer, WCHAR_SIZEOF (text_buffer), fmt, argptr);
 
   va_end (argptr);
 
 
 
   // get text length
 
   text_length = wcslen (text_buffer);
 
 
 
   // resize the chat text buffer
 
   interlocutor->dialogtext = (wchar_t *) SAFE_realloc (interlocutor->dialogtext,
 
                                                        interlocutor->dialogtext_size,
 
                                                        interlocutor->dialogtext_size + template_length + text_length,
 
                                                        sizeof (wchar_t), false);
 
 
 
   // append the message to the chat text and save the new text length (overwrite the closing brace)
 
   swprintf_s (&interlocutor->dialogtext[wcslen (interlocutor->dialogtext) - 2],
 
               template_length + text_length + 2 + 1, // +1 for null terminator
 
               rtf_template, text_buffer);
 
   interlocutor->dialogtext_size += template_length + text_length; // save new string length
 
 
 
   // and tell our chat window there's a new message to display
 
   interlocutor->update_dialog = true;
 
 
 
   return; // finished
 
}