/* Pathogen Warrior
 * Copyright 2004 Jetro Lauha - http://iki.fi/jetro/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: App.cpp,v 1.7 2004/07/14 15:37:09 tonic Exp $
 * $Revision: 1.7 $
 */

#include <time.h>
#include "main.h"


#define BLURMIX(a,b,n) ((a) * ((n) - 1) + (b)) / (n)


App * App::mSingleton = NULL;


App::App() :
    mState(AS_UNINITIALIZED),
    mScreen(NULL),
    mAlive(TRUE),
    mTime(0),
    mStartTime(0),
    mNextUpdateTime(0),
    mKeyState(NULL),
    mCurrentStateHandler(NULL),
    mCustomEventFilter(NULL)
{
}


App::~App()
{
}


void App::setInstance(App *singleton)
{
    assert(mSingleton == NULL);
    mSingleton = singleton;
}


static int sEventFilter(const SDL_Event * /*event*/)
{
    return 1;
}


BOOL App::init()
{
    setInstance(this);

    SDL_WM_SetCaption(getCaption(), "");

    mScreen = SDL_GetVideoSurface();
    assert(mScreen != NULL);

    SDL_SetEventFilter(sEventFilter);

    resetTime();
    srand(time(NULL));

    return TRUE;
}


void App::run()
{
    while (isAlive())
    {
        SDL_Event event;
        while (SDL_PollEvent(&event))
        {
            SDLKey keysym;
            switch (event.type)
            {
            case SDL_KEYDOWN:
                setKeyState(SDL_GetKeyState(NULL));

                if (mKeyState[SDLK_ESCAPE] == SDL_PRESSED)
                    setAlive(FALSE);
                keysym = event.key.keysym.sym;
                switch (keysym)
                {
                case SDLK_KP4: mKeyEvents.push_back(SDLK_LEFT); break;
                case SDLK_KP6: mKeyEvents.push_back(SDLK_RIGHT); break;
                case SDLK_KP8: mKeyEvents.push_back(SDLK_UP); break;
                case SDLK_KP2: mKeyEvents.push_back(SDLK_DOWN); break;
                default:
                    mKeyEvents.push_back(keysym);
                    break;
                }
                if (mCurrentStateHandler != NULL)
                    mCurrentStateHandler->onKeyDown(keysym);
                break;

            case SDL_KEYUP:
                setKeyState(SDL_GetKeyState(NULL));
                keysym = event.key.keysym.sym;
                if (mCurrentStateHandler != NULL)
                    mCurrentStateHandler->onKeyUp(keysym);
                break;

            case SDL_MOUSEMOTION:
                if (mCurrentStateHandler != NULL)
                    mCurrentStateHandler->onMouseMotion(event.motion);
                break;

            case SDL_MOUSEBUTTONUP:
                if (mCurrentStateHandler != NULL)
                    mCurrentStateHandler->onMouseUp(event.button);
                break;
                
            case SDL_MOUSEBUTTONDOWN:
                if (mCurrentStateHandler != NULL)
                    mCurrentStateHandler->onMouseDown(event.button);
                break;

            case SDL_QUIT:
                setAlive(FALSE);
                break;
            }
        }

        BOOL updated = FALSE;
        UINT32 time = SDL_GetTicks() - mStartTime;
        mTime = BLURMIX(mTime, time, 4);

        updated |= update();

        if (updated)
        {
            if (mCurrentStateHandler != NULL)
                mCurrentStateHandler->render(mScreen);
            //SDL_Flip(mScreen);
            glFlush();
            SDL_GL_SwapBuffers();
        }
        else
            SDL_Delay(1);

        GLenum error = glGetError();
        if (error != GL_NO_ERROR)
            errorMessage("GL Error", "GL Error");
    }
}


BOOL App::update()
{
    if (mTime < mNextUpdateTime)
        return FALSE;

    if (mCurrentStateHandler == NULL)
    {
        mNextUpdateTime = mTime;
        return FALSE;
    }

    while (mNextUpdateTime < mTime)
    {
        mNextUpdateTime += 1000 / mCurrentStateHandler->getUpdateFPS();
        
        mCurrentStateHandler->update(mNextUpdateTime, mKeyEvents);
    }

    return TRUE;
}


void App::resetTime()
{
    mNextUpdateTime = 0;
    mStartTime = SDL_GetTicks();
    mTime = 0;
}


void App::setState(INT state)
{
    if (mCurrentStateHandler != NULL)
        mCurrentStateHandler->onDeactivate();

    mCurrentStateHandler = getStateHandler(state);

    if (mCurrentStateHandler != NULL)
        mCurrentStateHandler->onActivate(mTime);
}
