/*
God.cpp
Written by Matthew Fisher

The God class glues the operating system, the graphics device, the application interface,
and the window manager together.  It is essentially the first and the last code that is executed.
See God.h for a description of the class and member functions.
*/

//All source files include Main.h
#include "Main.h"

const int GraphicsDeviceType = GD_D3D;
//GraphicsDeviceType tells God what type of graphics device/API to use.
//possible choices are:
//GD_D3D        -> DirectX API
//GD_OpenGL     -> OpenGL API
//GD_Software   -> Software rendering
//DirectX offers more functionality, but many people find it harder to understand and it's Windows only
//OpenGL is more widely supported but doesn't have nice things like N-Patching built in
//Software is written by me, by hand, and is slow and has very limited functionality
//you'll need #define USE_DIRECTX or #define USE_OPENGL (in Main.h) to use DirectX or OpenGL

const int ScreenWidth = 1280;
const int ScreenHeight = 800;   //only one full screen mode is supported; this is the full screen width/height

const int WindowWidth = 720 + WindowBorderWidth;
const int WindowHeight = 640 + WindowBorderHeight;  //the default windowed-mode window width and height.  The window can be manually resized.

const bool FullScreenStart = false; //true to start in full screen mode.  If you're debugging, windowed mode is usually better
const bool VSync = true;            //true to wait for the monitor to finish the entire display (vertical retracing) before
                                    //the front buffer is flipped.  Setting this to false results in higher frame rate, but may
                                    //induce flickering.

const bool CapturingScreen = false; //true if capturing of the screen to an AVI file is requested.  press "C" to start capturing.
const int CaptureWidth = 800;       //width and height of the captured video file images
const int CaptureHeight = 600;
const char AVIFileName[] = "C:\\test.avi";  //file name to save the captured images to
const float AvgFrameRate = 20.0f;           //target frame rate to advance the scene at when capturing files
const int MilliSecondsPerFrame = int(1000.0f / AvgFrameRate);   //milliseconds elapsed per frame in the target frame rate provided above
const int SleepBetweenFrames = 0;                               //time to sleep between frame, in milliseconds.

void God::Init(HINSTANCE hInstance, int nCmdShow)
{
#ifdef USE_VIDEO_TEXTURES
    CoInitialize(NULL);                     //initalize COM objects.  So far I use only one COM object, DirectX video textures.
                                            //so this line is skipped if they are not used.
#endif

    for(UINT i=0;i<256;i++)
    {
        key[i] = 0; //by default all keys are not pressed
    }

    //create a new GraphicsDevice object based upon the type of graphics device requested in GraphicsDeviceType
    if(GraphicsDeviceType == GD_Software) GD = new SoftwareGraphicsDevice;
#ifdef USE_DIRECTX
    else if(GraphicsDeviceType == GD_D3D) GD = new D3DGraphicsDevice;
#endif

#ifdef USE_OPENGL
    else if(GraphicsDeviceType == GD_OpenGL) GD = new OpenGLGraphicsDevice;
#endif
    else FatalMB("Invalid choice of graphics device type.");

    CaptureStarted = false;         //don't capture from the very beginning, wait for the user to give the signal
    FullScreen = FullScreenStart;   //start in the requested full screen mode
    IgnoreReSize = true;            //when we create a new window we want to ignore Win32's resize command

    if(FullScreen)
        WM.InitAll(hInstance, nCmdShow, FullScreen, ScreenWidth, ScreenHeight); //create a new full screen sized window
    else
        WM.InitAll(hInstance, nCmdShow, FullScreen, WindowWidth, WindowHeight); //create a new windowed window

    if(CapturingScreen) Capture.OpenFile(AVIFileName,MilliSecondsPerFrame,CaptureWidth,CaptureHeight);  //open the AVI if requested

    GD->SetVSync(VSync);    //set the GD vertical synchronization property
    GD->Init3D(WM);         //initalize the graphics device's 3D properties

    if(FullScreen) ShowCursor(FALSE);   //hide the cursor if we're in full screen

    GD->SetScreenColor(RGBColor(0,0,0));    //default clear color is black
    GD->SetWireframe();                     //wireframe is on by default

    T.Start(60.0f);     //start at a guessed frame rate of 60 fps; this will be fixed in a few seconds to the "real" value
    T.Pause();          //ignore the first few frames from causing an abnormally low FPS

    App.Init(*GD, WM);  //initalize this application's graphics objects
}

void God::RenderFrame()
{
    GD->StartRender(WM);    //have the Graphics device prepare a new frame

    if(GD->GetSystemActive())   //if we are good to render (we wouldn't be if say we were in the middle of a window change)
    {
        T.Frame();              //update the FPS counter
        App.Render(*GD, WM);    //have the application render its objects through the graphics device
        GD->DrawDouble("fps - > ", T.GetFramesPerSec(), 0, 0);  //draw the FPS
        if(SleepBetweenFrames) Sleep(SleepBetweenFrames);
    } else {
        T.Pause();              //if we aren't good to render, don't update the FPS counter
    }

    GD->FinishRender(WM, App);  //put the rendered scene onto the screen
}

void God::CaptureFrame()
{
    if(key[KEY_C]) CaptureStarted = true;   //if the user requests it, begin capturing frames to the AVI file
    if(CaptureStarted)
    {
        Capture.ScreenFrame();  //get the current frame and put it into the file
        T.SetFPS(AvgFrameRate); //control the frame rate directly; otherwise we'd factor in the time it took to compress
                                //the scene, which would be bad.
    }
}

void God::FullScreenMouseChange()
{
    if(FullScreen)
    {
        ShowCursor(FALSE);
        SetCursor(NULL);    //hide the cursor if full screen
    }
    else
    {
        ShowCursor(TRUE);
        SetCursor(LoadCursor(NULL, IDC_ARROW)); //show the cursor if windowed
    }
}

void God::ResetGraphicsObjects()
{
    T.Pause();
    App.ReInit(*GD, WM);    //reinialize all the scene objects
}

void God::HandleAltEnter()
{
    T.Pause();
    key[KEY_ALT_ENTER] = 0;
    FullScreen = !FullScreen;

    IgnoreReSize = true;
    if(FullScreen)
        WM.ResetWindow(FullScreen, SW_SHOW, ScreenWidth, ScreenHeight);
    else
        WM.ResetWindow(FullScreen, SW_SHOW, WindowWidth, WindowHeight); //toggle full screen mode

    GD->ReSizeFullScreen(WM, App);      //tell the graphics device what we did

    ShowWindow(WM.GetHWND(),SW_SHOW);
    SetForegroundWindow(WM.GetHWND());
    SetFocus(WM.GetHWND());             //make sure our window is visible and has focus

    FullScreenMouseChange();            //update the mouse
}

void God::HandleReSize()
{
    T.Pause();                              //avoid jerkiness in the frame timer
    ReSizeDevice = false;
    if(IgnoreReSize) IgnoreReSize = false;  //if this is a new resize event to us,
    else GD->ReSize(WM, App);               //tell the graphics device to resize
}

void God::MessageLoop(HINSTANCE hInstance, int nCmdShow)
{
    //the main Windows message loop

    MSG msg;
    BOOL bGotMsg;
    PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE ); //get a new message

    while( WM_QUIT != msg.message  )    //while we don't have the quit message
    {
        bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ); //get a new message
        if( bGotMsg )                   //if we have a message
        {
            TranslateMessage( &msg );   //send it out
            DispatchMessage( &msg );
        }
        else
        {
            //if we have no messages we decide it's a good time to render 
            RenderFrame();                                  //render a new frame
            if(CapturingScreen) CaptureFrame();             //if we're capturing the screen, do so
            if(key[KEY_ALT_ENTER]) HandleAltEnter();        //toggle full screen display if the user Alt+Enters
            if(ReSizeDevice && !FullScreen) HandleReSize(); //if we're in a window and the user resizes it
            if(GD->NeedToRestoreDevices())                  //if we've lost devices,
            {
                T.Pause();                                  //pause the timer for a short bit,
                ResetGraphicsObjects();                     //reset the graphics objects,
                GD->DevicesRestored();                      //tell the GD everything's okay
            }
        }
    }
}

void God::FreeMemory()
{
    App.End();          //free application data

    WM.FreeMemory();    //free the window
    GD->FreeMemory();   //free the graphics API data
    delete GD;          //delete the graphics device

    if(CapturingScreen) Capture.CloseFile();    //if we were capturing the screen, finalize the file

#ifdef USE_VIDEO_TEXTURES
    CoUninitialize();   //frees COM objects, if we're using them
#endif
}

Top