#include "main.h"  
#include "GameLogic.h"
#include "ctime"

#ifdef WIN32
#include <direct.h> //used to switch dirs to the .exe dir at the start of the program
#endif

#define C_DELTA_DIVIDER 13.333f
#define C_MAX_DELTA_ALLOWED 3 //any higher than this and we'll just let the game run slow, better than physics screwing up.  Also helps with
//long jerks we don't plan for.

float LerpFloat(float f_origin, float f_target, float f_percent)
{
    return (f_origin - ((f_origin-f_target)*f_percent));
}

void Vector2Truncate(CL_Vector2 &vec, float max)
{
    if (vec.length() > max)
    {
        vec.unitize();
        vec *= max;
    } 
}

CL_Vector2 Vector2Perp(CL_Vector2 &vec)
{
    return CL_Vector2(-vec.y, vec.x);
}

float FRandomRange(float min, float max)
{ 
    return ((float)rand() / RAND_MAX) * (max-min) + min;
}
int Random(int num)
{
    return int(FRandomRange(0, num));
}


App MyApp; //declare the main app global
App * GetApp(){return &MyApp;}

 
App::App()
{
    m_pResourceManager = NULL;
    m_pGUIResourceManager = NULL;
    m_pWindow = NULL;
    m_pBackground = NULL;
    m_bWindowResizeRequest = false;
    m_bQuit = false;
    m_HaveFocus = true;
    m_bClipCursorWhenFullscreen = true;
    m_Hwnd = false;
 
    for (int i=0; i < C_FONT_COUNT; i++)
    {
       m_pFonts[i] = 0;
    }

}

App::~App()
{
   
}

void App::OneTimeDeinit()
{
    
    SAFE_DELETE(m_pGameLogic);
    SAFE_DELETE(m_pBackgroundCanvas);
    SAFE_DELETE(m_pBackground);

    for (int i=0; i < C_FONT_COUNT; i++)
    {
        SAFE_DELETE(m_pFonts[i]);
    }
     
 
    SAFE_DELETE(m_pWindow);
    SAFE_DELETE(m_pGUIResourceManager);
    SAFE_DELETE(m_pResourceManager);
}

        
void App::OneTimeInit()
{
  
   
    //initalize our main window
    srand( (unsigned)time( NULL ) );
   
    m_WindowDescription.set_fullscreen(false);
    m_WindowDescription.set_bpp(32);
    m_WindowDescription.set_title("Lighthouse Rescue");
    m_WindowDescription.set_allow_resize(false);
    m_WindowDescription.set_size(CL_Size(800,600));
    m_WindowDescription.set_flipping_buffers(2);
    m_WindowDescription.set_refresh_rate(60);
    
    m_pWindow = new CL_DisplayWindow(m_WindowDescription);
    
    m_pResourceManager = new CL_ResourceManager("media/resources.xml", false);
    m_pFonts[C_FONT_GRAY] = new CL_Font("font_gray", GetResourceManager());
    m_pFonts[C_FONT_BLACK] = new CL_Font("font_black", GetResourceManager());
    m_pFonts[C_FONT_YELLOW] = new CL_Font("font_yellow", GetResourceManager());
    m_pFonts[C_FONT_YELLOW]->set_alignment(origin_center);
  
    m_Hwnd = GetActiveWindow();
    if (m_Hwnd == NULL) throw CL_Error("Error getting a valid HWND."); //can that ever happen?  Doubt it. More likely to just be wrong, rather than NULL.
    SetupBackground(GetApp()->GetMainWindow()->get_width(), GetApp()->GetMainWindow()->get_height());
  
     m_pGameLogic = new GameLogic();
    if (!m_pGameLogic) throw CL_Error("Error initting game logic");


}
       
//load a pic and tile it to our background surface
void App::SetupBackground(int x, int y)
{
    
    SAFE_DELETE(m_pBackgroundCanvas);
    SAFE_DELETE(m_pBackground);
    
    m_pBackground = new CL_Surface("media/ocean.png");
    m_pBackgroundCanvas = new CL_Canvas(*m_pBackground);
    SetupMouseClipping();
    m_pGameLogic->RebuildBuffers();
    ClearTimingAfterLongPause();
}


void App::OnWindowResize(int x, int y)
{
    m_bWindowResizeRequest = true;
}

void App::OnWindowClose()
{
   m_bQuit = true; //quit the app
}

void App::SetupMouseClipping()
{
    // Confine cursor to fullscreen window
    if( m_bClipCursorWhenFullscreen )
    {
        if (m_pWindow->is_fullscreen() && m_HaveFocus)
        {
            std::cout << "Clipped curser.";
            RECT rcWindow;
            GetWindowRect(GetHWND(), &rcWindow );
            ClipCursor( &rcWindow );
        }
        else
        {
            std::cout << "Freed curser.";
           ClipCursor( NULL );
        }
    }
   
}

void App::OnLoseFocus()
{
    m_HaveFocus = false;
    SetupMouseClipping();
    if (m_pWindow->is_fullscreen())
    {
        //m_pWindow->
        SendMessage(m_Hwnd, WM_SYSCOMMAND, SC_MINIMIZE,0);
        ChangeDisplaySettings(NULL, 0);
    }

}

void App::OnGotFocus()
{
    m_HaveFocus = true;
  // m_bWindowResizeRequest = true; //draw background again
   SetupMouseClipping();

   if (m_pWindow->is_fullscreen())
   {
        m_pWindow->set_fullscreen(m_WindowDescription.get_size().width, m_WindowDescription.get_size().height, m_WindowDescription.get_bpp(), m_WindowDescription.get_refresh_rate());
   }

}

void App::ToggleWindowedMode()
{
    
    if (m_pWindow->is_fullscreen())
    {
        m_pWindow->set_windowed();
        m_pWindow->set_size(m_WindowDescription.get_size().width,m_WindowDescription.get_size().height );
   

    }   else
    {
        m_pWindow->set_fullscreen(m_WindowDescription.get_size().width, m_WindowDescription.get_size().height, m_WindowDescription.get_bpp(), m_WindowDescription.get_refresh_rate());
        //surfaces are now invalid.  Rebuild them ?
        SetupBackground(GetApp()->GetMainWindow()->get_width(), GetApp()->GetMainWindow()->get_height());
        

    }

  ClearTimingAfterLongPause();  
}


void App::OnKeyUp(const CL_InputEvent &key)
{
  //  if (GetMainWindow()->left_alt_down) || key.left_alt_down)
    if(CL_Keyboard::get_keycode(CL_KEY_MENU))
    {
        //system message?  We should really process these somewhere else
        switch (key.id)
        {
            
        case  CL_KEY_ENTER:
            ToggleWindowedMode();
            break;
            
        case CL_KEY_F4:
            OnWindowClose();
            
            break;
            
        }
        return;
    }
}

void App::ClearTimingAfterLongPause()
{
    m_lastFrameTime = CL_System::get_time()-C_DELTA_DIVIDER;
    m_delta = 1;
    m_deltaTarget = 1;
}


int App::main(int argc, char **argv)
{
    //first move to our current dir
#ifdef WIN32
    chdir(CL_System::get_exe_path().c_str());
#endif

    try
    {
       
        CL_SetupCore setup_core;
        CL_SetupDisplay setup_display;
        CL_SetupGL setup_gl;
        CL_SetupSound setup_sound;
        CL_SoundOutput output(44100);
        
     
    // Create a console window for text-output if in debug mode
#ifdef _DEBUG
    CL_ConsoleWindow console("Console");
    console.redirect_stdio();
#endif      
    try
    {
        OneTimeInit();
     
       	CL_Slot slot_quit = m_pWindow->sig_window_close().connect(this, &App::OnWindowClose);
       	CL_Slot slot_on_resize = m_pWindow->sig_resize().connect(this, &App::OnWindowResize);
        CL_Slot slot_on_lose_focus = m_pWindow->sig_lost_focus().connect(this, &App::OnLoseFocus);
        CL_Slot slot_on_get_focus = m_pWindow->sig_got_focus().connect(this, &App::OnGotFocus);
        CL_Slot m_slot_button_up = CL_Keyboard::sig_key_up().connect(this, &App::OnKeyUp);
  
        // Class to give us the framerate
        CL_FramerateCounter framerate;
 
        ClearTimingAfterLongPause();
         
        // Run until someone presses escape
        while (!CL_Keyboard::get_keycode(CL_KEY_ESCAPE) && !m_bQuit)
        {
            if (m_HaveFocus)
            {
                
                if (
                    (GetMainWindow()->get_width() != 0)
                    )
                {
                    if (m_bWindowResizeRequest) m_bWindowResizeRequest = false;
                    
                    //figure out our average delta frame
                    
                    m_deltaTarget= (CL_System::get_time() -  m_lastFrameTime)/C_DELTA_DIVIDER;
                    if (m_deltaTarget > C_MAX_DELTA_ALLOWED) 
                    {
                        m_deltaTarget = C_MAX_DELTA_ALLOWED;
                    }
                    
                    m_delta = LerpFloat(m_delta, m_deltaTarget, 0.05f);
                    m_lastFrameTime = CL_System::get_time();
                    
                    m_pGameLogic->Update(m_delta);

                    m_pGameLogic->Render();
                    
//                    GetFont(C_FONT_BLACK)->draw(GetScreenX-150,10, "FPS: " + CL_String::from_int(framerate.get_fps()));
                    // Flip the display, showing on the screen what we have drawed
                    // since last call to flip_display()
                    CL_Display::flip(2);
                    
                    // This call updates input and performs other "housekeeping"
                    // call this each frame
                    CL_System::keep_alive();
                } 
                
            } else
            {
                //don't currently have focus   
                CL_System::sleep(1);
                CL_System::keep_alive();
            }
         }
        
    }
 
    catch(CL_Error error)
    {
        std::cout << "Exception caught : " << error.message.c_str() << std::endl;			
        
        // Display console close message and wait for a key
        MessageBox(NULL, error.message.c_str(), "Error!", MB_ICONEXCLAMATION);
#ifdef _DEBUG
        console.display_close_message();
#endif
        PostQuitMessage(0);
    }
    
    OneTimeDeinit();
    }    catch(CL_Error error)
    {
        MessageBox(NULL, error.message.c_str(), "Early Error!", MB_ICONEXCLAMATION);
        return 0;
    }
    
    return 0;
}

