// Utilitarian.cpp : Handler for utility commands
//
//  Private functions:
//    timenowProc
//    helpProc
//    newsProc
//    hoursProc
//    infoProc
//    inventoryProc
//    briefProc
//    fastProc
//    fullProc
//    findProc
//    quitProc
//    scoreProc
//    hintProc
//    saveProc
//    restoreProc
//

#include "stdafx.h"
#include <io.h>
#include <fcntl.h>
#include "string.h"
#include "sys\stat.h"

#include "AdvIO.h"
#include "AdvMain.h"
#include "AdvUtil.h"
#include "DoVerb.h"
#include "Retort.h"
#include "Utilitarian.h"

/////////////////////
// Forward references
void timenowProc (AdvGlobalContext& gc);
void helpProc (AdvGlobalContext& gc);
void newsProc (AdvGlobalContext& gc);
void hoursProc (AdvGlobalContext& gc);
void infoProc (AdvGlobalContext& gc);
void inventoryProc (AdvGlobalContext& gc);
void briefProc (AdvGlobalContext& gc);
void fastProc (AdvGlobalContext& gc);
void fullProc (AdvGlobalContext& gc);
void findProc (AdvGlobalContext& gc);
void quitProc (AdvGlobalContext& gc);
void scoreProc (AdvGlobalContext& gc);
void hintProc (AdvGlobalContext& gc);
void saveProc (AdvGlobalContext& gc);
void restoreProc (AdvGlobalContext& gc);

///////////////////
// Public functions

void utilitarian
  (AdvGlobalContext& gc)   // global context
//
//  Handler for utility commands
//
{
  switch (gc.m_nArg1)
  {
    case timenow:
      timenowProc (gc);
      break;
    case help:
      helpProc (gc);
      break;
    case news:
      newsProc (gc);
      break;
    case hours:
      hoursProc (gc);
      break;
    case info:
      infoProc (gc);
      break;
    case inventory:
      inventoryProc (gc);
      break;
    case brief:
      briefProc (gc);
      break;
    case fast:
      fastProc (gc);
      break;
    case full:
      fullProc (gc);
      break;
    case find:
      findProc (gc);
      break;
    case qquit:
      quitProc (gc);
      break;
    case score:
      scoreProc (gc);
      break;
    case hint:
      hintProc (gc);
      break;
    case save:
      saveProc (gc);
      break;
    case say:
      sayProc (gc);
      break;
    case restore:
      restoreProc (gc);
      break;
    default:
      printf ("<GLITCH!> Utility command not handled for arg1=%d, arg2=%d, context=%d\n",
              gc.m_nArg1, gc.m_nArg2, gc.m_nContext);
      printf ("Please report this bug to ravib@ravib.com.  Thanks!\n");
      break;
  }
}

void sayProc
  (AdvGlobalContext& gc)   // global context
//
//  Handles speaking.
//
{
  if (gc.m_nCmdWords == 2)
     if ((gc.m_nArg1 == fuck) || (gc.m_nArg2 == fuck))
        retort (gc);
     else
        sayMessageText (gc, said, gc.m_szArg2);
  else
     {
       sayMessageWord (gc, clarify, say);
       gc.m_nContext = say;
     }
}

////////////////////
// Private functions

void timenowProc
  (AdvGlobalContext& gc)   // global context
//
//  Tell him what time it is.
//
{
long  nHours;               // # hours
long  nMinutes;             // # minutes
long  nTimeNow;             // current time
char  szTimeString [255];   // current time

  // Display current time
  time (&nTimeNow);
  strcpy (szTimeString, ctime (&nTimeNow));
  szTimeString [strlen (szTimeString) - 1] = '\0';
  printf ("It's now %s.  You've been playing Adventure for\n", szTimeString);

  // Tell him how long he's been playing
  nMinutes = (nTimeNow - gc.m_nTimeStart) / 60;
  nHours = nMinutes / 60;
  nMinutes -= (60 * nHours);

  if (nHours > 0)
     {
       printf ("%d hour%s", nHours, (nHours > 1 ? "s" : ""));
       if (nMinutes > 0)
          printf (" and %d minute%s", nMinutes, (nMinutes > 1 ? "s" : ""));
     }
  else
     if (nMinutes > 0)
        printf ("%d minute%s", nMinutes, (nMinutes > 1 ? "s" : ""));
     else
        printf ("less than a minute");
  printf (".\n");
}

void helpProc
  (AdvGlobalContext& gc)   // global context
//
//  Offer help.
//
{
  sayMessage (gc, helpdata);
}

void newsProc
  (AdvGlobalContext& gc)   // global context
//
//  Display news.
//
{
  sayMessage (gc, newsdata);
}

void hoursProc
  (AdvGlobalContext& gc)   // global context
//
//  Display open hours.
//
{
  sayMessage (gc, hours_are);
}

void infoProc
  (AdvGlobalContext& gc)   // global context
//
//  Display general information.
//
{
  sayMessage (gc, info_2);
}

void inventoryProc
  (AdvGlobalContext& gc)   // global context
//
//  Display inventory.
//
{
bool  bDescribedObject = false;   // flag: described at least one object

  // Display inventory descriptions
  for (long nObject=MINOBJECTS; (nObject <= MAXOBJECTS); nObject++)
      if (carrying(nObject) && (nObject != bear))
         {
           if (!bDescribedObject)
              {
                bDescribedObject = true;
                sayMessage (gc, younowhave);
              }
           describeObjectInven (gc, nObject);
         }

  // Describe bear
  if (carrying (bear))
     {
       sayMessage (gc, blank);
       sayMessage (gc, i_c_a_bear);
       bDescribedObject = true;
     }

  // Describe empty inventory if not carrying anything
  if (!bDescribedObject)
     sayMessage (gc, armsareempty);
  else
     printf ("\n");
}

void briefProc
  (AdvGlobalContext& gc)   // global context
//
//  Enable brief mode.
//
{
  gc.m_bBriefMode = true;
  gc.m_bFastMode = false;
  sayMessage (gc, brief_ok);
}

void fastProc
  (AdvGlobalContext& gc)   // global context
//
//  Enable fast mode.
//
{
  gc.m_bBriefMode = false;
  gc.m_bFastMode = true;
  sayMessage (gc, ok);
}

void fullProc
  (AdvGlobalContext& gc)   // global context
//
//  Enable full mode.
//
{
  gc.m_bBriefMode = false;
  gc.m_bFastMode = false;
  sayMessage (gc, ok);
}

void findProc
  (AdvGlobalContext& gc)   // global context
//
//  Looks for an object.
//
{
  if (gc.m_nCmdWords == 1)
     if (gc.m_lit [gc.m_nHere] || (near (lamp) && (gc.m_nState [lamp] == 1)))
        {
          sayMessageWord (gc, clarify, find);
          gc.m_nContext = find;
        }
     else
        sayMessage (gc, cant_see_anything);
  else
     if (!isObject (gc.m_nArg2))
        sayMessage (gc, what);
     else
        if (carrying (gc.m_nArg2))
           sayMessage (gc, youhaveit);
        else
           if ((gc.m_mortal [gc.m_nArg2]) || (!gc.m_portable [gc.m_nArg2]))
              sayMessage (gc, beserious);
           else
              if (gc.m_lit [gc.m_nHere] || (near (lamp) && (gc.m_nState [lamp] == 1)))
                 if (gc.m_nWhereIs [gc.m_nArg2] == gc.m_nHere)
                    describeObject (gc, gc.m_nArg2, gc.m_nState [gc.m_nArg2]);
                 else
                    sayMessageWord (gc, idontsee, gc.m_nArg2);
              else
                 {
                   long nProbability = (gc.m_nInventory - gc.m_nStrength)*50 + 60;
                   if (chance (nProbability))
                      {
                        sayMessageWord (gc, grope_fail, gc.m_nArg2);
                        coroner (gc);
                      }
                   else
                      if (chance (50) || !near (gc.m_nArg2))
                         sayMessageWord (gc, grope_miss, gc.m_nArg2);
                      else
                         sayMessageWord (gc, grope_miss, gc.m_nArg2);
                 }
}

void quitProc
  (AdvGlobalContext& gc)   // global context
//
//  He wants to end the game.
//
{
  if (yes (gc, wanttoquit))
     {
       gc.m_bQuitting = true;
       finis (gc);
     }
  sayMessage (gc, ok);
}

void scoreProc
  (AdvGlobalContext& gc)   // global context
//
//  Displays the player's current score.
//
{
  getScore (gc);
  sayMessageValue (gc, ifyouquit, gc.m_nScore);
  sayMessageValue (gc, ifyouquit2, gc.m_nMaxScore);
}

void hintProc
  (AdvGlobalContext& gc)   // global context
//
//  Player has asked for a hint.
//
{
  // Can't offer a hint if it's too early in the game or no hint exists
  // for the current location
  if (gc.m_nTurns < 5)
     {
       sayMessage (gc, earlyhint);
       return;
     }
  else
     if (!gc.m_hintable [gc.m_nHere])
        {
          sayMessage (gc, nohinthere);
          return;
        }

  // Display hint
  hintLogic (gc);
}

void saveProc
  (AdvGlobalContext& gc)   // global context
//
//  Saves the game.
//
{
long    nIndex;             // generic index
int     fhSave = 0;         // file handle
char    szFilespec [256];   // save filespec

  // Get filename
  printf ("Please type a filename in which to save the game, or press\n");
  printf ("RETURN to cancel.\n");
  printf ("> ");
  if ((gets (szFilespec) == NULL) || (strlen (szFilespec) == 0))
     {
       printf ("\nYour game was not saved..\n");
       return;
     }

  // Warn if file already exists
  struct _stat  fileInfo;
  memset (&fileInfo, 0, sizeof (fileInfo));
  if (_stat (szFilespec, &fileInfo) == 0)
     {
       sayMessageText (gc, file_exists, szFilespec);
       if (!yes (gc, overwrite_file))
          {
            sayMessage (gc, ok);
            return;
          }
     }
  else
     printf ("\n");

  // Create file
  fhSave = _open (szFilespec, _O_RDWR | O_CREAT | _O_BINARY, _S_IREAD | _S_IWRITE);
  if (fhSave == (-1))
     {
       sayMessage (gc, cant_save);
       printf ("%s.\n", szFilespec);
       return;
     }

  // Write signature, version and timestamp
  long nFoo = _write (fhSave, &gc.m_szSignature, 256);
  _write (fhSave, &gc.m_nVerMajor, sizeof (long));
  _write (fhSave, &gc.m_nVerMinor, sizeof (long));
  time_t tmNow = time (NULL);
  _write (fhSave, &tmNow, sizeof (time_t));

  // Save game state
#define writeBool( foo )  _write (fhSave, &gc.foo, sizeof (bool));
#define writeLong( foo )  _write (fhSave, &gc.foo, sizeof (long));

  // Save array sizes
  writeLong (m_nObjects);
  writeLong (m_nMessages);
  writeLong (m_nPlaces);
  writeLong (m_nVocab);

  // Save location properties
  for (nIndex=0; (nIndex < MAXPLACES); nIndex++)
    {
      writeBool (m_hintable[nIndex])
      writeBool (m_inMaze[nIndex])
      writeBool (m_lit[nIndex])
      writeBool (m_noBack[nIndex])
      writeBool (m_noDwarf[nIndex])
      writeBool (m_notInCave[nIndex])
      writeBool (m_oneExit[nIndex])
      writeBool (m_thrower[nIndex])
      writeBool (m_waterHere[nIndex])
      writeLong (m_visited[nIndex])
    }

  // Save object properties
  for (nIndex=0; (nIndex < MAXOBJECTS); nIndex++)
    {
      writeBool (m_firstDesc[nIndex])
      writeBool (m_invisible[nIndex])
      writeBool (m_mortal[nIndex])
      writeBool (m_noDesc[nIndex])
      writeBool (m_openable[nIndex])
      writeBool (m_portable[nIndex])
      writeBool (m_seen[nIndex])
      writeBool (m_special1[nIndex])
      writeBool (m_special2[nIndex])
      writeBool (m_valued[nIndex])
      writeBool (m_weightless[nIndex])
      writeLong (m_nState[nIndex])
      writeLong (m_nWhereIs[nIndex])
    }

  // Save game context (booleans)
  writeBool (m_bBriefMode);
  writeBool (m_bExtendedVersion);
  writeBool (m_bFastMode);
  writeBool (m_bJustDied);
  writeBool (m_bMoved);
  writeBool (m_bNoMagic);
  writeBool (m_bOk2Describe);
  writeBool (m_bPanicked);
  writeBool (m_bRanOut);
  writeBool (m_bSaidXyzzyPlugh);
  writeBool (m_bTicker);
  writeBool (m_bQuitting);
  writeBool (m_bWizard);

  // Save game context (integers)
  writeLong (m_nCameoTime);
  writeLong (m_nClockTick);
  writeLong (m_nClosure);
  writeLong (m_nDeaths);
  writeLong (m_nDragonTime);
  writeLong (m_nDwarfCount);
  writeLong (m_nDwarvesInRoom);
  writeLong (m_nEscape);
  writeLong (m_nFooBar);
  writeLong (m_nHere);
  writeLong (m_nHintTime);
  writeLong (m_nInventory);
  writeLong (m_nLampLife);
  writeLong (m_nLastClock);
  writeLong (m_nMaxScore);
  writeLong (m_nMoves);
  writeLong (m_nMushTime);
  writeLong (m_nTimeStart);
  writeLong (m_nPassword);
  writeLong (m_nPenalties);
  writeLong (m_nPrevLoc);
  writeLong (m_nRetort);
  writeLong (m_nSafeExit);
  writeLong (m_nScore);
  writeLong (m_nStrength);
  writeLong (m_nWaterPhugg);
  writeLong (m_nTurns);

  // Close file
  _close (fhSave);
  sayMessageText (gc, game_saved_to, szFilespec);
}

void restoreProc
  (AdvGlobalContext& gc)   // global context
//
//  Restores a saved game.
//
{
int     fhRestore = 0;      // file handle
long    nIndex;             // generic index
long    nVerMajor;          // major version
long    nVerMinor;          // major version
char    szFilespec [256];   // restore filespec
char    szSignature [256];  // signature

  // Get filename
  printf ("Please type the name of the file to restore, or press\n");
  printf ("RETURN to cancel.\n");
  printf ("> ");
  if ((gets (szFilespec) == NULL) || (strlen (szFilespec) == 0))
     {
       printf ("\nYour game was not restored.\n");
       return;
     }
  printf ("\n");

  // Open file - return if unable
  fhRestore = _open (szFilespec, _O_RDONLY | _O_BINARY);
  if (fhRestore == (-1))
     {
       sayMessageText (gc, file_not_found, szFilespec);
       return;
     }

  // Read signature and version - complain if invalid
  bool  bReadStatus = true;
  _read (fhRestore, &szSignature, 256);
  if (strcmp (szSignature, gc.m_szSignature) == 0)
     {
       _read (fhRestore, &nVerMajor, sizeof (long));
       if (nVerMajor > gc.m_nVerMajor)
          bReadStatus = false;
       else
          _read (fhRestore, &nVerMinor, sizeof (long));
     }
  else
     bReadStatus = false;
  if (!bReadStatus)
     {
       sayMessageText (gc, invalid_saved_game, szFilespec);
       _close (fhRestore);
       return;
     }

  // Read timestamp
  time_t tmSaved;
  _read (fhRestore, &tmSaved, sizeof (tmSaved));

  // Restore game state
#define readBool( foo )  _read (fhRestore, &gc.foo, sizeof (bool));
#define readLong( foo )  _read (fhRestore, &gc.foo, sizeof (long));

  // Restore array sizes
  readLong (m_nObjects);
  readLong (m_nMessages);
  readLong (m_nPlaces);
  readLong (m_nVocab);

  // Restore location properties
  for (nIndex=0; (nIndex < MAXPLACES); nIndex++)
    {
      readBool (m_hintable[nIndex])
      readBool (m_inMaze[nIndex])
      readBool (m_lit[nIndex])
      readBool (m_noBack[nIndex])
      readBool (m_noDwarf[nIndex])
      readBool (m_notInCave[nIndex])
      readBool (m_oneExit[nIndex])
      readBool (m_thrower[nIndex])
      readBool (m_waterHere[nIndex])
      readLong (m_visited[nIndex])
    }

  // Restrore object properties
  for (nIndex=0; (nIndex < MAXOBJECTS); nIndex++)
    {
      readBool (m_firstDesc[nIndex])
      readBool (m_invisible[nIndex])
      readBool (m_mortal[nIndex])
      readBool (m_noDesc[nIndex])
      readBool (m_openable[nIndex])
      readBool (m_portable[nIndex])
      readBool (m_seen[nIndex])
      readBool (m_special1[nIndex])
      readBool (m_special2[nIndex])
      readBool (m_valued[nIndex])
      readBool (m_weightless[nIndex])
      readLong (m_nState[nIndex])
      readLong (m_nWhereIs[nIndex])
    }

  // Restore game context (booleans)
  readBool (m_bBriefMode);
  readBool (m_bExtendedVersion);
  readBool (m_bFastMode);
  readBool (m_bJustDied);
  readBool (m_bMoved);
  readBool (m_bNoMagic);
  readBool (m_bOk2Describe);
  readBool (m_bPanicked);
  readBool (m_bRanOut);
  readBool (m_bSaidXyzzyPlugh);
  readBool (m_bTicker);
  readBool (m_bQuitting);
  readBool (m_bWizard);

  // Restore game context (integers)
  readLong (m_nCameoTime);
  readLong (m_nClockTick);
  readLong (m_nClosure);
  readLong (m_nDeaths);
  readLong (m_nDragonTime);
  readLong (m_nDwarfCount);
  readLong (m_nDwarvesInRoom);
  readLong (m_nEscape);
  readLong (m_nFooBar);
  readLong (m_nHere);
  readLong (m_nHintTime);
  readLong (m_nInventory);
  readLong (m_nLampLife);
  readLong (m_nLastClock);
  readLong (m_nMaxScore);
  readLong (m_nMoves);
  readLong (m_nMushTime);
  readLong (m_nTimeStart);
  readLong (m_nPassword);
  readLong (m_nPenalties);
  readLong (m_nPrevLoc);
  readLong (m_nRetort);
  readLong (m_nSafeExit);
  readLong (m_nScore);
  readLong (m_nStrength);
  readLong (m_nWaterPhugg);
  readLong (m_nTurns);

  // Close file
  _close (fhRestore);
  char szTimestamp [256];
  strcpy (szTimestamp, ctime (&tmSaved));
  szTimestamp [strlen (szTimestamp) - 1] = '\0';
  sayMessageText (gc, game_restored_from, szTimestamp);

  // Describe current location
  lookProc (gc);
}
