//------------------------------------------------------------
// Copyright Sandlot Games, 2007
// author: Michael Felice
// file: client_messageHud.cs
// brief:
//    This file contains the implementation of the messages
// that are updated in the HUD.  This list of messages are
// descriptively brief and stack on top of each other when
// many different messages are displayed.  When the top-most
// message is done displaying, it disappears and the text
// below it rises up, continuing the cycle.  This messaging
// is used for mundane messages to the player, indicating that
// something special or out of the ordinary has happened.
//------------------------------------------------------------

// these values are used to position the message hud area that
// will display the test and the speed that the messages will
// slide at and the time it takes for the message to be destroyed
// and fade out
$MessageSlideSpeed = 20;
$MessageFadeTime = 0.5;
$MessageDestroyTime = 5;
$MessageSpacing = 5;
$MessageRegionBorder = 3;



//*************************************************************
//  HUD MESSAGES INITIALIZATION, DESTRUCTION, SAVING, LOADING
//*************************************************************

$HudMessageCount = 0;
$HudMessageList = 0;
$HudMessageSlideTimer = 0;
$HudMessageDestroyTimer = 0;
$HudMessageFadeTimer = 0;
$HudMessageHeight = 0;
$HudMessageNextVisible = 0;
$HudMessageRegionOffset = 0;
$HudMessageRegion = 0;

function InitHudMessages()
{
   DestroyHudMessages();
}

function DestroyHudMessages()
{
   // delete any remaining text messages
   for (%index = 0; %index < $HudMessageCount; %index++)
   {
      $HudMessageList[%index].delete();
   }
   
   // delete the region box
   if (isObject($HudMessageRegion) == true)
   {
      $HudMessageRegion.delete();
   }
   
   // delete the timers
   if (isObject($HudMessageSlideTimer) == true)
   {
      $HudMessageSlideTimer.delete();
   }
   if (isObject($HudMessageDestroyTimer) == true)
   {
      $HudMessageDestroyTimer.delete();
   }
   if (isObject($HudMessageFadeTimer) == true)
   {
      $HudMessageFadeTimer.delete();
   }
   
   // initialization of values
   $HudMessageCount = 0;
   $HudMessageList = 0;
   $HudMessageSlideTimer = 0;
   $HudMessageDestroyTimer = 0;
   $HudMessageFadeTimer = 0;
   $HudMessageHeight = 0;
   $HudMessageNextVisible = 0;
   $HudMessageRegionOffset = 0;
   $HudMessageRegion = 0;
   textRegion.visible = false;
}

function HudMessageSaveToFile()
{
   // save the list of text messages
   slgSaveInt($HudMessageCount);
   for (%index = 0; %index < $HudMessageCount; %index++)
   {
      %text = $HudMessageList[%index];
      slgSaveString(%text.stateUp);
      slgSaveString(%text.position);
      slgSaveString(%text.extent);
      %text.SaveToFile();
   }
   
   // save the slide timer
   %hasTimer = isObject($HudMessageSlideTimer);
   slgSaveBool(%hasTimer);
   if (%hasTimer == true)
   {
      %elapsedTime = $HudMessageSlideTimer.getElapsedTime();
      %totalTime = $HudMessageSlideTimer.getTotalTime();
      slgSaveFloat(%elapsedTime);
      slgSaveFloat(%totalTime);
      slgSaveFloat($HudMessageSlideTimer.lastTime);
      slgSaveFloat($HudMessageSlideTimer.lastDistance);
   }
   
   // save the destroy timer
   %hasTimer = isObject($HudMessageDestroyTimer);
   slgSaveBool(%hasTimer);
   if (%hasTimer == true)
   {
      %elapsedTime = $HudMessageDestroyTimer.getElapsedTime();
      %totalTime = $HudMessageDestroyTimer.getTotalTime();
      slgSaveFloat(%elapsedTime);
      slgSaveFloat(%totalTime);
   }
   
   // save the fade timer
   %hasTimer = isObject($HudMessageFadeTimer);
   slgSaveBool(%hasTimer);
   if (%hasTimer == true)
   {
      %elapsedTime = $HudMessageFadeTimer.getElapsedTime();
      %totalTime = $HudMessageFadeTimer.getTotalTime();
      slgSaveFloat(%elapsedTime);
      slgSaveFloat(%totalTime);
   }
   
   // save values for the message hud
   slgSaveInt($HudMessageHeight);
   slgSaveInt($HudMessageNextVisible);
   slgSaveInt($HudMessageRegionOffset);
   slgSaveString(textRegion.position);
   slgSaveString(textRegion.extent);
   slgSaveBool(textRegion.visible);
   
   // save the hud message region
   %hasRegion = isObject($HudMessageRegion);
   slgSaveBool(%hasRegion);
   if (%hasRegion == true)
   {
      slgSaveString($HudMessageRegion.position);
      slgSaveString($HudMessageRegion.extent);
      $HudMessageRegion.SaveToFile();
   }
}

function HudMessageLoadFromFile()
{
   // clear any existing hud messages
   DestroyHudMessages();
   
   // load the list of text messages
   $HudMessageCount = slgLoadInt();
   for (%index = 0; %index < $HudMessageCount; %index++)
   {
      // create the text and add it to the gui control
      %text = new SLText()
      {
         stateUp = slgLoadString();
         textVerticalCenter = false;
         color = textMessage.color;
         visible = true;
         disabled = false;
         selected = false;
         input = false;
         profile = textMessage.profile;
         horizSizing = "relative";
         vertSizing = "relative";
         position = slgLoadString();
         extent = slgLoadString();
      };
      %text.LoadFromFile();
      $HudMessageList[%index] = %text;
      MessageHud.addGuiControl(%text);
   }
   
   // load the slide timer
   %hasTimer = slgLoadBool();
   if (%hasTimer == true)
   {
      %elapsedTime = slgLoadFloat();
      %totalTime = slgLoadFloat();
      $HudMessageSlideTimer = new SLTimer()
      {
         time = %totalTime;
      };
      $HudMessageSlideTimer.setElapsedTime(%elapsedTime);
      $HudMessageSlideTimer.lastTime = slgLoadFloat();
      $HudMessageSlideTimer.lastDistance = slgLoadFloat();
      $HudMessageSlideTimer.notifyOnUpdate(HudMessageSlide, $HudMessageSlideTimer);
   }
   
   // load the destroy timer
   %hasTimer = slgLoadBool();
   if (%hasTimer == true)
   {
      %elapsedTime = slgLoadFloat();
      %totalTime = slgLoadFloat();
      $HudMessageDestroyTimer = new SLTimer()
      {
         time = %totalTime;
      };
      $HudMessageDestroyTimer.setElapsedTime(%elapsedTime);
      $HudMessageDestroyTimer.notifyOnFire(HudMessageDestroy, $HudMessageDestroyTimer);
   }
   
   // load the fade timer
   %hasTimer = slgLoadBool();
   if (%hasTimer == true)
   {
      %elapsedTime = slgLoadFloat();
      %totalTime = slgLoadFloat();
      $HudMessageFadeTimer = new SLTimer()
      {
         time = %totalTime;
      };
      $HudMessageFadeTimer.setElapsedTime(%elapsedTime);
      $HudMessageFadeTimer.notifyOnFire(HudMessageFade, $HudMessageFadeTimer);
   }
   
   // load values for the message hud
   $HudMessageHeight = slgLoadInt();
   $HudMessageNextVisible = slgLoadInt();
   $HudMessageRegionOffset = slgLoadInt();
   textRegion.position = slgLoadString();
   textRegion.extent = slgLoadString();
   textRegion.visible = slgLoadBool();
   
   // load the hud message region
   %hasRegion = slgLoadBool();
   if (%hasRegion == true)
   {
      $HudMessageRegion = new SLImage()
      {
         stateUp = textRegion.stateUp;
         color = textRegion.color;
         renderColor = textRegion.renderColor;
         visible = "1";
         input = "0";
         profile = textRegion.profile;
         horizSizing = "relative";
         vertSizing = "relative";
         position = slgLoadString();
         extent = slgLoadString();
      };
      $HudMessageRegion.LoadFromFile();
      MessageHud.addGuiControl($HudMessageRegion);
   }
}

function UpdateHudMessagePosition(%index)
{
   if (%index > $HudMessageCount)
   {
      return;
   }
   
   %text = $HudMessageList[%index];
   if (%index == 0)
   {
      %xPos = getWord(textMessage.position, 0);
      %yPos = getWord(textMessage.position, 1) +
         getWord(textMessage.extent, 1);
      %text.position = %xPos @ " " @ %yPos;
      return;
   }
   
   %prevText = $HudMessageList[%index - 1];
   %xPos = getWord(%prevText.position, 0);
   %yPos = getWord(%prevText.position, 1) + getWord(%prevText.extent, 1);
   %text.position = %xPos @ " " @ %yPos;
}

function GetHudMessageRegion()
{
   // return no region if no messages are in the list
   if ($HudMessageCount == 0)
   {
      return "0 0 0 0";
   }
   
   // get the min position of the hud region
   %text = $HudMessageList[0];
   %xMinPos = getWord(%text.position, 0) - $MessageRegionBorder;
   %yMinPos = getWord(%text.position, 1) + $HudMessageRegionOffset - $MessageRegionBorder;
   
   // get the max position of the hud region
   %text = $HudMessageList[$HudMessageCount - 1];
   %xMaxPos = getWord(%text.position, 0) + getWord(%text.extent, 0) + 2 * $MessageRegionBorder;
   %yMaxPos = getWord(%text.position, 1) + getWord(%text.extent, 1);
   
   %yMax = getWord(textMessage.position, 1) + getWord(textMessage.extent, 1) + $MessageSpacing;
   if (%yMaxPos > %yMax) %yMaxPos = %yMax - $MessageSpacing + 2 * $MessageRegionBorder;
   else if ($HudMessageRegionOffset == 0 || $HudMessageCount > 1) %yMaxPos += 2 * $MessageRegionBorder - $MessageSpacing;
   else return "0 0 0 0";
   
   // return the region found
   return %xMinPos @ " " @ %yMinPos @ " " @
      %xMaxPos - %xMinPos @ " " @ %yMaxPos - %yMinPos;
}

function UpdateHudMessageRegion()
{
   %region = GetHudMessageRegion();
   if (%region $= "0 0 0 0")
   {
      textRegion.visible = false;
      return;
   }
   
   textRegion.visible = true;
   textRegion.position = getWords(%region, 0, 1);
   textRegion.extent = getWords(%region, 2, 3);
}

// this sends a message to the hud message window
function SendHudMessage(%message)
{
   // do not send messages if we are still loading objects
   if ($DoneLoading == false)
   {
      return;
   }
   
   // check if the message is already in the list of messages
   // (if it is, do not add it to the message list, and if it's
   // the first message, keep the first message present longer)
   for (%index = 0; %index < $HudMessageCount; %index++)
   {
      // the message has been found
      %text = $HudMessageList[%index];
      if (%text.stateUp $= %message)
      {
         %cancel = true;
         if (%index == 0)
         {
            if (isObject(%text.fadeTimer) == true)
            {
               %cancel = false;
            }
            else
            {
               %baseTime = $HudMessageDestroyTimer.getElapsedTime();
               $HudMessageFadeTimer.time = %baseTime + $MessageDestroyTime;
               $HudMessageDestroyTimer.time = %baseTime +
                  $MessageDestroyTime + $MessageFadeTime;
            }
         }
         
         if (%cancel == true)
         {
            return;
         }
      }
   }
   
   // add the message to the list of messages available
   %xExtent = getWord(textMessage.extent, 0);
   %text = new SLText()
   {
      stateUp = %message;
      textVerticalCenter = false;
      color = textMessage.color;
      visible = true;
      disabled = false;
      selected = false;
      input = false;
      profile = textMessage.profile;
      horizSizing = "relative";
      vertSizing = "relative";
      extent = %xExtent @ " 20";
   };
   
   // set the height of the dialog to the text height and the spacing
   // that will be used after the text is displayed (the dialog extent
   // is what clips the text and because its bounds will be 0 at first,
   // no text is initially displayed
   %yExtent = %text.getTextHeight() + $MessageSpacing;
   %text.extent = %xExtent @ " " @ %yExtent;

   // add the dialog to the list of dialogs that are in the text stack
   // and increase the size (number of dialogs)
   $HudMessageList[$HudMessageCount] = %text;
   $HudMessageCount++;
   UpdateHudMessagePosition($HudMessageCount - 1);
   MessageHud.addGuiControl(%text);
   
   // figure out scolling
   %yMax = getWord(textMessage.extent, 1);
   if ($HudMessageHeight < %yMax)
   {
      // update the extents thave have been handled
      $HudMessageHeight += %yExtent;
      $HudMessageNextVisible++;
      
      // determine if the scrolling needs to stop short, so
      // the first messages is not removed from the queue and
      // get the time that the text will be scrolling
      %extra = $HudMessageHeight - %yMax;
      if (%extra < 0) %extra = 0;
      %textTime = (%yExtent - %extra) / $MessageSlideSpeed;
      if (%textTime < 0) %textTime = 0;
      
      // update the existing hud message slide timer time
      if (isObject($HudMessageSlideTimer) == true)
      {
         $HudMessageSlideTimer.time += %textTime;
      }
      // or create a new timer if one does not exist
      else
      {
         $HudMessageSlideTimer = new SLTimer()
         {
            time = %textTime;
         };
         $HudMessageSlideTimer.notifyOnUpdate(HudMessageSlide, $HudMessageSlideTimer);
      }
      
      // if no destroy timer has been created, create one along
      // with a fade timer
      if (isObject($HudMessageDestroyTimer) == false)
      {
         // the destroy timer
         $HudMessageDestroyTimer = new SLTimer()
         {
            time = $MessageDestroyTime + $MessageFadeTime + %textTime;
         };
         $HudMessageDestroyTimer.notifyOnFire(HudMessageDestroy, $HudMessageDestroyTimer);
         
         // the fade timer
         $HudMessageFadeTimer = new SLTimer()
         {
            time = $MessageDestroyTime + %textTime;
         };
         $HudMessageFadeTimer.notifyOnFire(HudMessageFade, $HudMessageFadeTimer);
      }
   }
}

// this function fades messages
function SLTimer::HudMessageFade(%timer)
{
   // if there are no messages to fade, we are done
   if ($HudMessageCount == 0)
   {
      return;
   }
   
   %text = $HudMessageList[0];
   %text.FadeOut($MessageFadeTime);

   // the hud message region changes (have the region
   // ignore the text message that is fading out)
   $HudMessageRegionOffset = getWord(%text.extent, 1);
   UpdateHudMessageRegion();
   
   // create a region that will display region that was
   // removed and have this region fade out
   %xPos = getWord(%text.position, 0) - $MessageRegionBorder;
   %yPos = getWord(%text.position, 1) - $MessageRegionBorder;
   %xExtent = getWord(textRegion.extent, 0);
   %yExtent = getWord(textRegion.position, 1) - %yPos;
   if (%yExtent == 0) %yExtent = getWord(textRegion.extent, 1);
   $HudMessageRegion = new SLImage()
   {
      stateUp = textRegion.stateUp;
      color = textRegion.color;
      renderColor = textRegion.renderColor;
      visible = "1";
      input = "0";
      profile = textRegion.profile;
      horizSizing = "relative";
      vertSizing = "relative";
      position = %xPos @ " " @ %yPos;
      extent = %xExtent @ " " @ %yExtent;
   };
   MessageHud.addGuiControl($HudMessageRegion);
   %peakColor = getWords(textRegion.color, 0, 2) @ " 0";
   $HudMessageRegion.Blink($MessageFadeTime, 0.5, textRegion.color, %peakColor);
}

// this function destroys messages
function SLTimer::HudMessageDestroy(%timer)
{
   // if there are no messages to destroy, we are done
   $HudMessageDestroyTimer = 0;
   if ($HudMessageCount == 0)
   {
      return;
   }
   
   // delete the first message
   $HudMessageRegionOffset = 0;
   $HudMessageRegion.delete();
   $HudMessageRegion = 0;
   
   %text = $HudMessageList[0];
   %yText = getWord(%text.extent, 1);
   $HudMessageHeight -= getWord(%text.extent, 1);
   %text.delete();
   
   // slide all of the messages up a slot in the message queue
   for (%index = 1; %index < $HudMessageCount; %index++)
   {
      $HudMessageList[%index - 1] = $HudMessageList[%index];
   }
   $HudMessageNextVisible--;
   $HudMessageCount--;
   
   // if there are no other hud messages, we are done
   if ($HudMessageCount == 0)
   {
      // update the text region to display nothing
      UpdateHudMessageRegion();
      return;
   }
   
   // we need to slide the messages, now (check if there is
   // overflow that needs to contribute to the messages sliding
   %yMax = getWord(textMessage.extent, 1);
   %leftover = $HudMessageHeight - %yMax;
   $HudMessageHeight -= %yText;
   %extra = $HudMessageHeight - %yMax;
   if (%extra < 0) %extra = 0;
   %leftover -= %extra;
   if (%leftover < 0) %leftover = 0;
   
   // determine the total time the should be added to the slide
   // timer to continue pushing text up
   %totalTime = %leftOver / $MessageSlideSpeed;
   while (%extra == 0 && $HudMessageNextVisible != $HudMessageCount)
   {
      %text = $HudMessageList[$HudMessageNextVisible];
      $HudMessageNextVisible++;

      %yExtent = getWord(%text.extent, 1);
      $HudMessageHeight += %yExtent;
      %extra = $HudMessageHeight - %yMax;
      if (%extra < 0) %extra = 0;
      %totalTime += (%yExtent - %extra) / $MessageSlideSpeed;
   }
   
   // update the existing hud message slide timer time
   if (isObject($HudMessageSlideTimer) == true)
   {
      $HudMessageSlideTimer.time += %totalTime;
   }
   // or create a new timer if one does not exist
   else
   {
      $HudMessageSlideTimer = new SLTimer()
      {
         time = %totalTime;
      };
      $HudMessageSlideTimer.notifyOnUpdate(HudMessageSlide, $HudMessageSlideTimer);
   }
   
   // the destroy timer
   $HudMessageDestroyTimer = new SLTimer()
   {
      time = $MessageDestroyTime + $MessageFadeTime + %totalTime;
   };
   $HudMessageDestroyTimer.notifyOnFire(HudMessageDestroy, $HudMessageDestroyTimer);
   
   // the fade timer
   $HudMessageFadeTimer = new SLTimer()
   {
      time = $MessageDestroyTime + %totalTime;
   };
   $HudMessageFadeTimer.notifyOnFire(HudMessageFade, $HudMessageFadeTimer);
   
   // update the background region
   UpdateHudMessageRegion();
}

// this function slides messages
function SLTimer::HudMessageSlide(%timer)
{
   // if there are no messages to slide, we are done
   if ($HudMessageCount == 0)
   {
      %timer.time = 0;
      return;
   }
   
   // check if the last text is in position (which is the max
   // movement allowed for the slide)
   %text = $HudMessageList[$HudMessageCount - 1];
   %yLast = getWord(%text.position, 1) + getWord(%text.extent, 1);
   %yMin = getWord(textMessage.position, 1) + getWord(textMessage.extent, 1);
   %maxSlide = %yLast - %yMin - 1;
   
   // set up time calculations (how much time has passed
   %text = $HudMessageList[0];
   %elapsedTime = %timer.getElapsedTime();
   %diff = %elapsedTime - %timer.lastTime;
   %timer.lastTime = %elapsedTime;
   
   // determine the offset for the position and update the
   // first text position
   %timer.lastDistance += %diff * $MessageSlideSpeed;
   if (%timer.lastDistance > %maxSlide) %timer.lastDistance = %maxSlide;
   %lastPos = getWord(%text.position, 1);
   %text.position = getWord(%text.position, 0) @ " " @ %lastPos - %timer.lastDistance;
   %nextPos = getWord(%text.position, 1);
   %timer.lastDistance -= (%lastPos - %nextPos);
   
   // update the rest of the text positions
   for (%index = 1; %index < $HudMessageCount; %index++)
   {
      UpdateHudMessagePosition(%index);
   }
   
   // update the background region
   UpdateHudMessageRegion();
}

// message from server is received and ready to be added to the
// list of hud messages that will be displayed
function clientCmdSendHudMessage(%message)
{
   SendHudMessage(%message);
}
function clientCmdSendHudMessageId(%messageID, %formatID)
{
   %message = slgGetUIString(%messageID);
   if(%formatID !$= "") {
      %message = slgFormatUIString(%message, slgGetObjectString(%formatID));
   }
   SendHudMessage(%message);
}



// sending building construction message
function clientCmdSendHudBuildMessage(%ghostID)
{
   %object = ServerConnection.resolveGhostId(%ghostID);
   if (isObject(%object) && %object.canGetToDoor() == false)
   {
      %selected = gSelection.getSelectedGroup($SELECT_ID);
      %object = %selected.getId(0);
      %message = slgGetUIString("id_error_blocked_door");
      %message = slgFormatUIString(%message, %object.name);
      SendHudMessage(%message);
   }
   else if (isObject(%object) && %object.isOnRamp() == true)
   {
      %message = slgGetUIString("id_error_on_ramp");
      SendHudMessage(%message);
   }
   else
   {
      %message = slgGetUIString("id_error_obstructed");
      SendHudMessage(%message);
   }
}

function SendCombatQuickJobErrorHudMessage()
{
   %message = slgGetUIString("id_error_combat_quick_job");
   SendHudMessage(%message);
}
