#include "stdafx.h"
#include "../Config/Config.h"
#include "../BWAPI_Addon.h"
#include "Logger.h"
#include <sstream>
#include <io.h>
#include <fcntl.h>
#include <boost/bind.hpp>

/*
FOREGROUND_BLUE	Text color contains blue.
FOREGROUND_GREEN	Text color contains green.
FOREGROUND_RED	Text color contains red.
FOREGROUND_INTENSITY	Text color is intensified.
BACKGROUND_BLUE	Background color contains blue.
BACKGROUND_GREEN	Background color contains green.
BACKGROUND_RED	Background color contains red.
BACKGROUND_INTENSITY	Background color is intensified.
COMMON_LVB_LEADING_BYTE	Leading byte.
COMMON_LVB_TRAILING_BYTE	Trailing byte.
COMMON_LVB_GRID_HORIZONTAL	Top horizontal.
COMMON_LVB_GRID_LVERTICAL	Left vertical.
COMMON_LVB_GRID_RVERTICAL	Right vertical.
COMMON_LVB_REVERSE_VIDEO	Reverse foreground and background attributes.
COMMON_LVB_UNDERSCORE	Underscore.
*/

namespace LogColors
{
  static const WORD
    DARK_RED      = FOREGROUND_RED,
    DARK_GREEN    = FOREGROUND_GREEN,
    DARK_BLUE     = FOREGROUND_BLUE,
    LIGHT_RED     = FOREGROUND_INTENSITY | DARK_RED,
    LIGHT_GREEN   = FOREGROUND_INTENSITY | DARK_GREEN,
    LIGHT_BLUE    = FOREGROUND_INTENSITY | DARK_BLUE,

    DARK_YELLOW   = DARK_RED | DARK_GREEN,
    DARK_MAGENTA  = DARK_RED | DARK_BLUE,
    DARK_CYAN     = DARK_BLUE | DARK_GREEN,
    LIGHT_YELLOW  = FOREGROUND_INTENSITY | DARK_YELLOW,
    LIGHT_MAGENTA = FOREGROUND_INTENSITY | DARK_MAGENTA,
    LIGHT_CYAN    = FOREGROUND_INTENSITY | DARK_CYAN,

    BLACK         = 0,
    GREY          = DARK_RED | DARK_GREEN | DARK_BLUE,
    WHITE         = FOREGROUND_INTENSITY | GREY;
};

const WORD LoggingTypes::EventType::DEFAULT_COLOR = LogColors::GREY;
const unsigned int LoggingTypes::EventType::DEFAULT_HTML_COLOR = 0xcccccc;

LoggingTypes::LoggingTypes() 
  : ON_FRAME(0x1)
  , ON_MORPH(0x2)
  , ON_RENEGADE(0x4)
  , ON_DESTROY(0x8)
  , ON_DISCOVER(0x10)
  , ON_EVADE(0x20)
  , ON_SHOW(0x40)
  , ON_HIDE(0x80)
  , ON_CREATE(0x100)
  , ON_COMPLETE(0x200)
  , ON_NUKE_DETECT(0x400)
  , GENERAL(0x800)
  , LAYER_1_DECISION(0x1000, LogColors::LIGHT_BLUE, "layer1") //0x3333cc
  , LAYER_2_DECISION(0x2000, LogColors::LIGHT_YELLOW, "layer2") //0xffff66
  , LAYER_3_DECISION(0x4000)
  , LAYER_4_DECISION(0x8000)
  , LAYER_1_SITUATION_ANALYSIS(0x10000)
  , LAYER_2_SITUATION_ANALYSIS(0x20000)
  , LAYER_3_SITUATION_ANALYSIS(0x40000)
  , LAYER_4_SITUATION_ANALYSIS(0x80000)
  , PERCEPTUAL_STATE(0x100000, LogColors::DARK_GREEN, "percep") //0x339933
  , GENERAL_INFO(0x200000, LogColors::LIGHT_GREEN, "info") //0xcc3333
  , GENERAL_WARNING(0x40000, LogColors::DARK_MAGENTA, "warning") //0xcc3333
  , GENERAL_ERROR(0x800000, LogColors::LIGHT_RED, "error") //0xcc3333
  , CALLBACKS(ON_FRAME | ON_DESTROY | ON_MORPH | ON_RENEGADE | ON_DISCOVER | 
              ON_EVADE | ON_SHOW | ON_HIDE | ON_CREATE | ON_COMPLETE | ON_NUKE_DETECT)
  , LAYERS(LAYER_1_DECISION | LAYER_2_DECISION | LAYER_3_DECISION | LAYER_4_DECISION |
           LAYER_1_SITUATION_ANALYSIS | LAYER_2_SITUATION_ANALYSIS | LAYER_3_SITUATION_ANALYSIS | LAYER_4_SITUATION_ANALYSIS)
  , ALL(CALLBACKS | LAYERS | GENERAL | PERCEPTUAL_STATE | GENERAL_INFO | GENERAL_WARNING | GENERAL_ERROR)
{
}

SparLogger::SparLogger()
{
  m_enabledLogger = Config::getInstance().getDefaultLoggerEvents();

  std::string path = "bwapi-data/logs/SPAR.log";
  time_t rawtime; 
  time(&rawtime);
  struct tm timeinfo;
  localtime_s(&timeinfo, &rawtime);
  char buffer[80]; 
  strftime(buffer, sizeof(buffer), "_%Y_%m_%d_%H_%M_%S", &timeinfo);
  path += buffer;
  path += ".html";
  if(fopen_s(&m_logFile, path.c_str(), "w") != 0)
    throw std::runtime_error("Cannot open file \"" + path + "\".");
  fprintf(m_logFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n");
  fprintf(m_logFile, "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
  fprintf(m_logFile, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
  fprintf(m_logFile, "<head>\n<title>SPAR AI Log</title><style type=\"text/css\">\n");
  fprintf(m_logFile, "body{background:#000;color:#%x;margin-top:2em;font:10pt Verdana;}\n", LoggingTypes::EventType::DEFAULT_HTML_COLOR);
  fprintf(m_logFile, "div.layer1{color:#33c;}\n");
  fprintf(m_logFile, "div.layer2{color:#ff6;}\n");
  fprintf(m_logFile, "div.percep{color:#393;}\n");
  fprintf(m_logFile, "div.warning{color:#c3c;}\n");
  fprintf(m_logFile, "div.error{color:#c33;}\n");
  fprintf(m_logFile, "div._{color:#%x;}\n", LoggingTypes::EventType::DEFAULT_HTML_COLOR);
  fprintf(m_logFile, "#btns.hidden button{display:none;}\n");
  fprintf(m_logFile, "#btns.hidden button#min{display:inline;}\n");
  fprintf(m_logFile, "</style>\n");
  fprintf(m_logFile, "<script type=\"text/javascript\">\n");
  fprintf(m_logFile, "function toggle(sheet,i,sender){\n");
  fprintf(m_logFile, "  css = sheet.cssRules[i].style;\n");
  fprintf(m_logFile, "  if(!css){css=sheet.rules[i].style;}\n");
  fprintf(m_logFile, "  if(css.display=='none'){\n");
  fprintf(m_logFile, "    css.display='block';\n");
  fprintf(m_logFile, "    sender.style.color='black';\n");
  fprintf(m_logFile, "  }else{\n");
  fprintf(m_logFile, "    css.display='none';\n");
  fprintf(m_logFile, "    sender.style.color='grey';\n");
  fprintf(m_logFile, "  }\n");
  fprintf(m_logFile, "}\n");
  fprintf(m_logFile, "</script>\n");
  fprintf(m_logFile, "</head>\n<body>\n");
  fprintf(m_logFile, "<div id=\"btns\" style=\"background:#666;padding:0.25em;position:fixed;top:0px;right:0px;\">\n");
  fprintf(m_logFile, "<button onclick=\"toggle(document.styleSheets[0],1,this);\">Layer 1</button>\n");
  fprintf(m_logFile, "<button onclick=\"toggle(document.styleSheets[0],2,this);\">Layer 2</button>\n");
  fprintf(m_logFile, "<button onclick=\"toggle(document.styleSheets[0],3,this);\">Perceptual State</button>\n");
  fprintf(m_logFile, "<button onclick=\"toggle(document.styleSheets[0],4,this);\">Warnings</button>\n");
  fprintf(m_logFile, "<button onclick=\"toggle(document.styleSheets[0],5,this);\">Errors</button>\n");
  fprintf(m_logFile, "<button onclick=\"toggle(document.styleSheets[0],6,this);\">Others</button>\n");
  fprintf(m_logFile, "<button id=\"min\" onclick=\"this.parentNode.className=this.parentNode.className=='hidden'?'':'hidden';\">&times;</button>\n");
  fprintf(m_logFile, "</div>\n");

  m_console = CreateConsoleLog("SPAR Logger");

  start("Logger");
}

SparLogger::~SparLogger()
{
  stopThread();

  fprintf(m_logFile, "</body>\n</html>");
  fclose(m_logFile);
}

HANDLE SparLogger::CreateConsoleLog(const char *winTitle)
{
  AllocConsole();
  SetConsoleTitleA(winTitle);
  int hConHandle;
  HANDLE lStdHandle;
  FILE *fp;	// redirect unbuffered STDOUT to the m_console	
  lStdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
  hConHandle = _open_osfhandle((long)lStdHandle, _O_TEXT);
  fp = _fdopen( hConHandle, "w" );
  *stdout = *fp;
  setvbuf( stdout, NULL, _IONBF, 0 );	// redirect unbuffered STDIN to the m_console	
  lStdHandle = GetStdHandle(STD_INPUT_HANDLE);
  hConHandle = _open_osfhandle((long)lStdHandle, _O_TEXT);
  fp = _fdopen( hConHandle, "r" );
  *stdin = *fp;
  setvbuf( stdin, NULL, _IONBF, 0 );	// redirect unbuffered STDERR to the m_console	
  lStdHandle = GetStdHandle(STD_ERROR_HANDLE);
  hConHandle = _open_osfhandle((long)lStdHandle, _O_TEXT);
  fp = _fdopen( hConHandle, "w" );	
  *stderr = *fp;
  setvbuf( stderr, NULL, _IONBF, 0 );
  HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
  SetConsoleTextAttribute(console, LoggingTypes::EventType::DEFAULT_COLOR);
  return console;
}

void SparLogger::post(const std::string &text, const LoggingTypes::EventType &type)
{
  time_t rawtime; 
  time(&rawtime);
  struct tm timeinfo;
  localtime_s(&timeinfo, &rawtime);
  char buffer[80]; 
  strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo);
  std::ostringstream info;
  info << buffer;
  if (BWAPI::isOnMainThread())
    info << " (" << BWAPI::Broodwar->getFrameCount() << ')';
  info << " - ";
  Log::Event ev(text, type, info.str());
  if(isRunning())
  {
    push(ev);
  }
  else
  {
    handle(ev);
  }
}

void SparLogger::handle(const Log::Event& ev) const
{
  DWORD out;
  WriteFile(m_console, ev.info.c_str(), ev.info.length(), &out, 0);
  SetConsoleTextAttribute(m_console, ev.type->color); 
  WriteFile(m_console, ev.text.c_str(), ev.text.length(), &out, 0); 
  SetConsoleTextAttribute(m_console, LoggingTypes::EventType::DEFAULT_COLOR);
  WriteFile(m_console, "\n", 1, 0, 0);
  fprintf(m_logFile, "<div class=\"%s\">%s<span>%s</span></div>\n", ev.type->cssClass.c_str(), ev.info.c_str(), ev.text.c_str());
}

void SparLogger::stopThread()
{
  stop();

  clearQueue();
}