text: #include <windows.h>  
  #include <mmsystem.h> // multimedia timer (may need winmm.lib)
  #include "App.h"
  #include "ScriptSys.h"
  #include "GameObj.h"
  #include "ScriptObj.h"
  #include "gmCall.h"
  #include "InputKBWin32.h"
  
  App* App::s_instancePtr = NULL;
  
  
  App::App()
  {
    s_instancePtr = this;
  }
  
  
  
  App::~App()
  {
    s_instancePtr = NULL;
  }
  
  
  bool App::Init()
  {
    InputKBWin32::Get().Init();
  
    // Init console
    InitConsole();
  
    // Init script system for game objects
    ScriptSys::Init();
  
    // Register app bindings
    RegisterScriptBindings();
  
    // Init timer
    m_deltaTime = 0;
    m_lastTime = timeGetTime();
  
    // Compile and run script
    ScriptSys::Get()->ExecuteFile("TestGameObj.gm");
  
  //TEST REMOVE
  //  ClearScreen();
  //  SetColor(COLOR_YELLOW, COLOR_RED);
  //  SetCursor(10,10);
  //  Print("Hello");
  //  PrintAt(10,10,"Hello");
  
  
    return true;
  }
  
  
  void App::Destroy()
  {
    ScriptSys::Destroy();
  }
  
  
  bool App::Update()
  {
    int curTime = timeGetTime();
    m_deltaTime = curTime - m_lastTime;
    m_lastTime = curTime;
  
    // Update input
    InputKBWin32::Get().Update();
        
    // Execute some script
    ScriptSys::Get()->Execute(m_deltaTime);
  
    if(InputKBWin32::Get().IsKeyPressed('A'))
    {
      TestA();
    }
    else if(InputKBWin32::Get().IsKeyPressed('B'))
    {
      TestB();
    }
    else if(InputKBWin32::Get().IsKeyPressed('C'))
    {
      TestC();
    }
  
    if(InputKBWin32::Get().IsKeyPressed(VK_ESCAPE))
    {
      return false;
    }
  
    return true;
  }
  
  
  void App::InitConsole()
  {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
  
    m_console = GetStdHandle(STD_OUTPUT_HANDLE);
    
    GetConsoleScreenBufferInfo(m_console, &csbi);
  
    DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
  
    m_screenSizeX = csbi.dwSize.X;
    m_screenSizeY = csbi.dwSize.Y;
  }
  
  
  void App::TestA()
  {
    // Test create a game object and set member in script
    GameObj* newObj = new GameObj;
  
    printf("new GameObj = %x\n", newObj);
    printf("GameObj.m_scriptObj = %x\n", newObj->GetScriptObj());
  
    newObj->GetScriptObj()->SetMemberString("m_name", "MangoBoy");
    newObj->GetScriptObj()->ExecuteGlobalFunctionOnThis("WhatsMyName");
  
    delete newObj;
  
    gmCall call;
    if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), "RunGlobalObject"))
    {
      call.End();
    }
  }
  
  
  void App::TestB()
  {
    // Test call script function that keeps running for a bit
    gmCall call;
    if(call.BeginGlobalFunction(ScriptSys::Get()->GetMachine(), "ThreadYieldTest"))
    {
      call.End();
    }
  }
  
  
  void App::TestC()
  {
    // Quick test of collect garbage and cpp owned objects
  
    GameObj* newObj = new GameObj;
  
    printf("new GameObj = %x\n", newObj);
    printf("GameObj.m_scriptObj = %x\n", newObj->GetScriptObj());
  
    newObj->GetScriptObj()->SetMemberString("m_name", "PotatoHead");
    //newObj->GetScriptObj()->ExecuteGlobalFunctionOnThis("WhatsMyName");
  
    ScriptSys::Get()->GetMachine()->CollectGarbage(true);
  
    delete newObj;
  }
  
  
  void App::SetCursor(int a_x, int a_y)
  {
    ClipScreenCoordsi(a_x, a_y);
    
    COORD point;
  
    point.X = (short) a_x; 
    point.Y = (short) a_y;
    
    SetConsoleCursorPosition(m_console, point);
  }
  
  
  void App::ClearScreen()
  {
    COORD coordScreen = { 0, 0 };
    DWORD cCharsWritten;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    DWORD dwConSize;
    GetConsoleScreenBufferInfo(m_console, &csbi);
    dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
    FillConsoleOutputCharacter(m_console, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
    GetConsoleScreenBufferInfo(m_console, &csbi);
    FillConsoleOutputAttribute(m_console, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
    SetConsoleCursorPosition(m_console, coordScreen);
  }
  
  
  void App::Print(const char* a_string)
  {
    printf("%s", a_string);
  }
  
  
  void App::PrintAt(int a_x, int a_y, const char* a_string)
  {
    SetCursor(a_x, a_y);
    Print(a_string);
  }
  
  
  int App::GetAttribFromColIndex(int a_colorIndex, bool a_isForeground)
  {
    // WARNING These struct must match the color enums
    static int foreColors[COLOR_MAX]=
    {
      0, //COLOR_BLACK
      FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, //COLOR_WHITE,
      FOREGROUND_RED, //COLOR_RED
      FOREGROUND_GREEN, //COLOR_GREEN,
      FOREGROUND_BLUE, //COLOR_BLUE,
      FOREGROUND_RED | FOREGROUND_BLUE, //COLOR_MAGENTA,
      FOREGROUND_GREEN | FOREGROUND_BLUE, //COLOR_CYAN,
      FOREGROUND_RED | FOREGROUND_GREEN, //COLOR_YELLOW,
    };
    
    static int backColors[COLOR_MAX]=
    {
      0, //COLOR_BLACK
      BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, //COLOR_WHITE,
      BACKGROUND_RED, //COLOR_RED
      BACKGROUND_GREEN, //COLOR_GREEN,
      BACKGROUND_BLUE, //COLOR_BLUE,
      BACKGROUND_RED | BACKGROUND_BLUE, //COLOR_MAGENTA,
      BACKGROUND_GREEN | BACKGROUND_BLUE, //COLOR_CYAN,
      BACKGROUND_RED | BACKGROUND_GREEN, //COLOR_YELLOW,
    };
  
  
    if( (a_colorIndex < COLOR_MIN) && (a_colorIndex >= COLOR_MAX) )
    {
      if(a_isForeground)
      {
        return foreColors[COLOR_WHITE];
      }
      else
      {
        return backColors[COLOR_WHITE];
      }
    }
  
    if(a_isForeground)
    {
      return foreColors[a_colorIndex];
    }
    else
    {
      return backColors[a_colorIndex];
    }
  }
  
  
  void App::SetColor(int a_foreColorIndex, int a_backColorIndex)
  {
    int foreCol = GetAttribFromColIndex(a_foreColorIndex, true);
    int backCol = GetAttribFromColIndex(a_backColorIndex, false);
  
    int param = foreCol | backCol;
  
    SetConsoleTextAttribute(m_console, (short) param);  
  }
  
  
  bool App::ClipScreenCoordsf(float& a_posX, float& a_posY)
  {
    bool wasClipped = false;
  
    if(a_posX < 0.0f)
    {
      a_posX = 0.0f;
      wasClipped = true;
    }
    else if(a_posX >= (float)App::Get()->GetScreenSizeX())
    {
      a_posX = (float)(App::Get()->GetScreenSizeX() - 1);
      wasClipped = true;
    }
  
    if(a_posY < 0.0f)
    {
      a_posY = 0.0f;
      wasClipped = true;
    }
    else if(a_posY >= (float)App::Get()->GetScreenSizeY())
    {
      a_posY = (float)(App::Get()->GetScreenSizeY() - 1);
      wasClipped = true;
    }
  
    return wasClipped;
  }
  
  
  bool App::ClipScreenCoordsi(int& a_posX, int& a_posY)
  {
    bool wasClipped = false;
  
    if(a_posX < 0)
    {
      a_posX = 0;
      wasClipped = true;
    }
    else if(a_posX >= App::Get()->GetScreenSizeX())
    {
      a_posX = App::Get()->GetScreenSizeX() - 1;
      wasClipped = true;
    }
  
    if(a_posY < 0)
    {
      a_posY = 0;
      wasClipped = true;
    }
    else if(a_posY >= App::Get()->GetScreenSizeY())
    {
      a_posY = App::Get()->GetScreenSizeY() - 1;
      wasClipped = true;
    }
  
    return wasClipped;
  }
  
  
  //////////////////////////////////////////////////
  // Script bindings
  //////////////////////////////////////////////////
  
  
  int GM_CDECL App::Console_Print(gmThread* a_thread)
  {
    GM_CHECK_NUM_PARAMS(1);
    GM_CHECK_STRING_PARAM(a_string, 0);
  
    App::Get()->Print(a_string);
  
    return GM_OK;
  }
  
  
  int GM_CDECL App::Console_SetCursor(gmThread* a_thread)
  {
    GM_CHECK_NUM_PARAMS(2);
    GM_CHECK_INT_PARAM(a_curX, 0);
    GM_CHECK_INT_PARAM(a_curY, 1);
  
    App::Get()->ClipScreenCoordsi(a_curX, a_curY);
    App::Get()->SetCursor(a_curX, a_curY);
  
    return GM_OK;
  }
  
  
  int GM_CDECL App::Console_SetColor(gmThread* a_thread)
  {
    GM_CHECK_NUM_PARAMS(2);
    GM_CHECK_INT_PARAM(a_foreCol, 0);
    GM_CHECK_INT_PARAM(a_backCol, 1);
  
    App::Get()->SetColor(a_foreCol, a_backCol);
  
    return GM_OK;
  }
  
  
  int GM_CDECL App::Input_KeyPressed(gmThread* a_thread)
  {
    GM_CHECK_NUM_PARAMS(1);
    GM_CHECK_INT_PARAM(a_vKey, 0);
  
    if(InputKBWin32::Get().IsKeyPressed(a_vKey))
    {
      a_thread->PushInt(1);
    }
    else
    {
      a_thread->PushInt(0);
    }
  
    return GM_OK;
  }
  
  
  int GM_CDECL App::Input_KeyDown(gmThread* a_thread)
  {
    GM_CHECK_NUM_PARAMS(1);
    GM_CHECK_INT_PARAM(a_vKey, 0);
  
    if(InputKBWin32::Get().IsKeyDown(a_vKey))
    {
      a_thread->PushInt(1);
    }
    else
    {
      a_thread->PushInt(0);
    }
  
    return GM_OK;
  }
  
  
  void App::RegisterScriptBindings()
  {
    static gmFunctionEntry ConsoleLib[] = 
    { 
      /*gm
        \lib Console
        \brief Console Library
      */
      /*gm
        \function Print
        \brief Print string at current cursor position
        \param string a_string
      */
      {"Print", Console_Print},
      /*gm
        \function SetCursor
        \brief Set cursor position
        \param int a_curX Cursor x position
        \param int a_curY Cursor y position
      */
      {"SetCursor", Console_SetCursor},
  
      /*gm
        \function SetColor
        \brief Set text color
        \param int a_foreCol Foreground color
        \param int a_backCol Background color
      */
      {"SetColor", Console_SetColor},
    };
  
  
    static gmFunctionEntry InputLib[] = 
    { 
      /*gm
        \lib Console
        \brief Console Library
      */
      /*gm
        \function KeyPressed
        \brief Was this key pressed
        \param int a_vKey windows virtual key code (Most match ascii uppercase)
        \return true if key was pressed this frame
      */
      {"KeyPressed", Input_KeyPressed},
  
      /*gm
        \function KeyDown
        \brief Is this key down
        \param int a_vKey windows virtual key code (Most match ascii uppercase)
        \return true if is down
      */
      {"KeyDown", Input_KeyDown},
  
    };
  
    gmMachine* machine = ScriptSys::Get()->GetMachine();
  
    machine->RegisterLibrary(ConsoleLib, sizeof(ConsoleLib) / sizeof(ConsoleLib[0]), "Console");
    machine->RegisterLibrary(InputLib, sizeof(InputLib) / sizeof(InputLib[0]), "Input");
  
    // Make some global constants
    machine->GetGlobals()->Set(machine, "COLOR_BLACK", gmVariable(GM_INT, COLOR_BLACK));
    machine->GetGlobals()->Set(machine, "COLOR_WHITE", gmVariable(GM_INT, COLOR_WHITE));
    machine->GetGlobals()->Set(machine, "COLOR_RED", gmVariable(GM_INT, COLOR_RED));
    machine->GetGlobals()->Set(machine, "COLOR_GREEN", gmVariable(GM_INT, COLOR_GREEN));
    machine->GetGlobals()->Set(machine, "COLOR_BLUE", gmVariable(GM_INT, COLOR_BLUE));
    machine->GetGlobals()->Set(machine, "COLOR_MAGENTA", gmVariable(GM_INT, COLOR_MAGENTA));
    machine->GetGlobals()->Set(machine, "COLOR_CYAN", gmVariable(GM_INT, COLOR_CYAN));
    machine->GetGlobals()->Set(machine, "COLOR_YELLOW", gmVariable(GM_INT, COLOR_YELLOW));
  }
  