/******************************************************************
  
  Module:  debugoverlay.cpp
  
  Author: Borut Pfeifer
  
  Copyright 2005 Sony Online Entertainment.  All rights reserved.
  
*******************************************************************/

//-------------------------------------------------------- Includes
#include "gameError.h"
//---------------------------------------------- Class Declarations
//----------------------------------------- Functions Declarations
//------------------------------------ Member Functions Definitions
//------------------------------------ cLOSTable

#ifdef _GAME_ERROR_REPORTING

#include "SyRaster.h"
#include "SyESFParse.h"
#include "SyScene.h"
#include "InputConsole.h"
#include "SyMap.h"
#include "SyDebug.h"

#include "debugoverlay.h"

char* __GameErrorReport_file = NULL;
int __GameErrorReport_line = 0; 
bool* __GameErrorReport_skip = NULL;

static const char* LOGFILENAME = "gameerror.txt";

cGameErrorLog* cGameErrorLog::smpThis     = NULL;

class cGameErrorLog::Implementation
{
  public:
    enum Level
    {
      DISABLED,
      ENABLED,
      PROMOTE_TO_ASSERT,
      PROMOTE_TO_FATAL,
      DEMOTE_TO_WARNING
    };

    Implementation();
    ~Implementation();

    void Error(const char* channelName, int type, const char* format, va_list params);

    void Display(const char* info, const char* error, const SyColor32F& bkgdColor, bool bAllowExit);

    bool OpenLog(const char* fileName);
    bool WriteLog(const char* text);
    bool CloseLog();

    struct ChannelInfo
    {
      SyColor32F mColor;
      Level mLevel;
    };

    SyInputConsole* mpConsole;
    SyRaster*       mpRaster;
    SyScene*        mpScene;
    SyMap<int, ChannelInfo> mpChannelMap;

    static const int NUM_CONTROLLER_BUTTONS = 16;
    bool mControllerButtons[NUM_CONTROLLER_BUTTONS];
    FILE *mpLogFile;
};

cGameErrorLog::Implementation::Implementation()
: mpConsole(NULL),
  mpRaster(NULL),
  mpScene(NULL),
  mpLogFile(NULL)
{
  for (int i=0; i<NUM_CONTROLLER_BUTTONS; ++i)
  {
    mControllerButtons[i] = false;
  }
}

cGameErrorLog::Implementation::~Implementation()
{
  CloseLog();
}

void cGameErrorLog::Implementation::Error(const char* channelName,
                                          int type,
                                          const char* format, 
                                          va_list params)
{
  int channelID = SyHashResourceID(channelName);
  int index = mpChannelMap.Find(channelID);

  if (index == mpChannelMap.End())
  {
    ChannelInfo info;
    info.mColor(0.0f, 0.0f, 0.7f, 1.0f);
    info.mLevel = ENABLED;
    index = mpChannelMap.Insert(channelID, info);
  }

  char bufInfo[4096];
  char bufError[4096];

  sprintf(bufInfo, "GAME_%s - %s: file: %s line: %d", (type==0?"FATAL":(type==2?"WARN":"ASSERT")), channelName, __GameErrorReport_file, __GameErrorReport_line);
  vsprintf(bufError, format, params);


  OpenLog(LOGFILENAME);
  WriteLog(bufInfo);
  WriteLog(" ");
  WriteLog(bufError);
  WriteLog("\n");
  CloseLog();

  ChannelInfo info = mpChannelMap(index);
  
  if ((__GameErrorReport_skip != NULL && *__GameErrorReport_skip) ||
      DISABLED == info.mLevel)
  {
    return;
  }

  if (0 == type ||
      (1 == type && DEMOTE_TO_WARNING != info.mLevel) || 
      (2 == type && (PROMOTE_TO_ASSERT == info.mLevel || PROMOTE_TO_FATAL == info.mLevel)))
  {
    SyOutputString( bufInfo );
    SyOutputString( " " );
    SyOutputString( bufError );
    SyOutputString( "\n" );

    Display(bufInfo, bufError, info.mColor, 0 != type && PROMOTE_TO_FATAL != info.mLevel);
  }
}

void cGameErrorLog::Implementation::Display(const char* info, const char* error, const SyColor32F& bkgdColor, bool bAllowExit)
{
  bool bExit = false;
  if (mpRaster == NULL || mpScene == NULL)
  {
    // Commmand-line tool (e.g. WadTool) does not have a raster, skip error reporting.
    return;
  }

  int fontHandle = -1;

  if (!mpScene->GetDictionary()->FindTyped( SyHashResourceID("albertus27"), SYRESOURCETYPE_FONT, fontHandle))
  {
    SyESFParse parser;
    fontHandle = parser.Parse("programming/ui/albertus27.esf", *mpScene);
  }

  if (fontHandle == -1)
  {
    return;
  }

  int width = mpRaster->GetScreenWidth();
  int height = mpRaster->GetScreenHeight();

  int strIndex, strLength, lineIndex, lineStart;

  SyVect2I screenPos;
  screenPos.X = 30;
  screenPos.Y = 10;

  const int LINE_LENGTH = 50;
  char line[LINE_LENGTH+1];

  while (!bExit)
  {
    mpRaster->BeginScene();
    mpRaster->Begin2D();

    SyRect gridRect(0, 0, width, height);
    mpRaster->FillRect(gridRect, bkgdColor);

    screenPos.X = 10;
    screenPos.Y = 20;

    strIndex = 0;
    strLength = strlen(info);
    while (strIndex < strLength)
    {
      lineIndex = 0;
      lineStart = 0;
      while (lineIndex < LINE_LENGTH && strIndex < strLength)
      {
        line[lineIndex] = info[strIndex];
        ++lineIndex;
        ++strIndex;
      }

      line[lineIndex] = 0;
      mpRaster->DrawString(fontHandle, screenPos, SyColor32F( 1.0f, 1.0f, 1.0f, 1.0f ), line);
      screenPos.Y += 20;
    }

    screenPos.Y += 20;
    strIndex = 0;
    strLength = strlen(error);
    while (strIndex < strLength)
    {
      lineIndex = 0;
      lineStart = 0;
      while (lineIndex < LINE_LENGTH && strIndex < strLength)
      {
        line[lineIndex] = error[strIndex];
        ++lineIndex;
        ++strIndex;
      }

      line[lineIndex] = 0;
      mpRaster->DrawString(fontHandle, screenPos, SyColor32F( 1.0f, 1.0f, 1.0f, 1.0f ), line);
      screenPos.Y += 20;
    }

    screenPos.Y += 50;
    if (bAllowExit)
    {
      mpRaster->DrawString(fontHandle, screenPos, SyColor32F( 1.0f, 1.0f, 1.0f, 1.0f ), 
                           "Press any controller button to skip.");
      screenPos.Y += 20;
      mpRaster->DrawString(fontHandle, screenPos, SyColor32F( 1.0f, 1.0f, 1.0f, 1.0f ), 
                           "Press 'I' on the keyboard to ignore all.");
    }
    else
    {
      mpRaster->DrawString(fontHandle, screenPos, SyColor32F( 1.0f, 1.0f, 1.0f, 1.0f ), 
                           "Error is fatal - cannot be skipped.");
    }


    mpRaster->End2D();
    mpRaster->EndScene();

    mpConsole->Update();
    SyInputEvent *event;
    while ((event = mpConsole->PopEvent()) != NULL)
    {
      if (bAllowExit)
      {
        if (event->GetType() == SyInputEvent::EVENT_PADSTATE &&
            event->GetParam(SyInputEvent::PADSTATE_INDEX_CONTROLLERID) == 0)
        {
          for (int i=0; i<NUM_CONTROLLER_BUTTONS; ++i)
          {
            bool newButton = event->GetParam(SyInputEvent::PADSTATE_INDEX_BUTTON1 + i) > 0;

            if (mControllerButtons[i] != newButton)
            {                
              if (!newButton)
              {
                bExit = true;
              }

              mControllerButtons[i] = newButton;
            }
          }
        }
        else if (event->GetType() == SyInputEvent::EVENT_KEYDOWN)
        {
          if (event->GetParam(SyInputEvent::KEY_INDEX_KEY) == SyInputEvent::KEYCODE_I)
          {
            bExit= true;
            if (__GameErrorReport_skip != NULL)
            {
              *__GameErrorReport_skip = true;
            }
          }
        }
      }

      if (event->GetType() == SyInputEvent::EVENT_QUIT)
      {
        bExit = true;
      }

      delete event;
    }
    
    SyPlatform::Sleep( 20 );
  }

  mpRaster->BeginScene();
  mpRaster->Begin2D();

  SyRect gridRect(0, 0, width, height);
  mpRaster->FillRect(gridRect, SyColor32F(0.0f, 0.0f, 0.0f, 1.0f));

  mpRaster->End2D();
  mpRaster->EndScene();
}


bool cGameErrorLog::Implementation::OpenLog(const char* pFilename)
{
  if(mpLogFile != NULL)
  {
    return false;
  }

  mpLogFile = fopen(pFilename, "at");
  if(mpLogFile == NULL)
  {
    return false;
  }

  return true;  
}

bool cGameErrorLog::Implementation::WriteLog(const char* text)
{
  if(mpLogFile != NULL)
  {
    fputs( text, mpLogFile );
  }

  return true;
}

bool cGameErrorLog::Implementation::CloseLog()
{
  if(mpLogFile == NULL)
  {
    return false;
  }

  fclose( mpLogFile );
  mpLogFile = NULL;

  return true;  
}



cGameErrorLog::cGameErrorLog()
: mpImp(NULL)
{
  smpThis = this;
  mpImp = new Implementation();
}

cGameErrorLog::~cGameErrorLog()
{
  delete mpImp;
  mpImp = NULL;
}

void cGameErrorLog::Init(SyInputConsole* pConsole, SyRaster* pRaster, SyScene* pScene)
{
  mpImp->mpConsole = pConsole;
  mpImp->mpRaster = pRaster;
  mpImp->mpScene = pScene;

  FILE* pClearFile = fopen(LOGFILENAME, "wt");
  if (pClearFile != NULL)
  {
    fclose(pClearFile);
  }
}

void cGameErrorLog::SetChannelBackgroundColor(const char* channelName, const SyColor32F& color)
{
  int channelID = SyHashResourceID(channelName);
  int index = mpImp->mpChannelMap.Find(channelID);

  if (index == mpImp->mpChannelMap.End())
  {
    Implementation::ChannelInfo info;
    info.mColor = color;
    info.mLevel = Implementation::ENABLED;
    index = mpImp->mpChannelMap.Insert(channelID, info);
  }
  else
  {
    mpImp->mpChannelMap(index).mColor = color;
  }
}

void cGameErrorLog::PromoteToFatal(const char* channelName)
{
  int channelID = SyHashResourceID(channelName);
  int index = mpImp->mpChannelMap.Find(channelID);

  if (index == mpImp->mpChannelMap.End())
  {
    Implementation::ChannelInfo info;
    info.mColor(0.0f, 0.0f, 0.7f, 1.0f);
    info.mLevel = Implementation::PROMOTE_TO_FATAL;
    index = mpImp->mpChannelMap.Insert(channelID, info);
  }
  else
  {
    mpImp->mpChannelMap(index).mLevel = Implementation::PROMOTE_TO_FATAL;
  }
}

void cGameErrorLog::PromoteToAssert(const char* channelName)
{
  int channelID = SyHashResourceID(channelName);
  int index = mpImp->mpChannelMap.Find(channelID);

  if (index == mpImp->mpChannelMap.End())
  {
    Implementation::ChannelInfo info;
    info.mColor(0.0f, 0.0f, 0.7f, 1.0f);
    info.mLevel = Implementation::PROMOTE_TO_ASSERT;
    index = mpImp->mpChannelMap.Insert(channelID, info);
  }
  else
  {
    mpImp->mpChannelMap(index).mLevel = Implementation::PROMOTE_TO_ASSERT;
  }
}

void cGameErrorLog::DemoteToWarning(const char* channelName)
{
  int channelID = SyHashResourceID(channelName);
  int index = mpImp->mpChannelMap.Find(channelID);

  if (index == mpImp->mpChannelMap.End())
  {
    Implementation::ChannelInfo info;
    info.mColor(0.0f, 0.0f, 0.7f, 1.0f);
    info.mLevel = Implementation::DEMOTE_TO_WARNING;
    index = mpImp->mpChannelMap.Insert(channelID, info);
  }
  else
  {
    mpImp->mpChannelMap(index).mLevel = Implementation::DEMOTE_TO_WARNING;
  }
}


#endif


void _GameErrorReportFatal(const char* channel, bool bCondition, const char* format, ...)
{
#ifdef _GAME_ERROR_REPORTING
  if (bCondition)
  {
    return;
  }

  if (cGameErrorLog::Get())
  {
    va_list params;
    va_start(params, format);  
    cGameErrorLog::Get()->mpImp->Error(channel, 0, format, params);
    va_end(params);             
  }
  else
  {
    SyAssertf(false, "Game Error log not initialized yet");
  }
#endif
}

void _GameErrorReportAssert(const char* channel, bool bCondition, const char* format, ...)
{
#ifdef _GAME_ERROR_REPORTING
  if (bCondition)
  {
    return;
  }

  if (cGameErrorLog::Get())
  {
    va_list params;
    va_start(params, format);  
    cGameErrorLog::Get()->mpImp->Error(channel, 1, format, params);
    va_end(params);             
  }
  else
  {
    SyAssertf(false, "Game Error log not initialized yet");
  }
#endif
}

void _GameErrorReportWarning(const char* channel, bool bCondition, const char* format, ...)
{
#ifdef _GAME_ERROR_REPORTING
  if (bCondition)
  {
    return;
  }

  if (cGameErrorLog::Get())
  {
    va_list params;
    va_start(params, format);  
    cGameErrorLog::Get()->mpImp->Error(channel, 2, format, params);
    va_end(params);             
  }
  else
  {
    SyAssertf(false, "Game Error log not initialized yet");
  }
#endif
}


// EOF
