#include "common.h"
#include "irrlicht/irrlicht.h"
#include "Object.h"
#include "DrawObject.h"
#include "PseuWoW.h"
#include "Scene.h"
#include "PseuGUI.h"

PseuGUIRunnable::PseuGUIRunnable()
{
    _gui = new PseuGUI();
}

void PseuGUIRunnable::run(void)
{
    _gui->Run();
}

PseuGUI *PseuGUIRunnable::GetGUI(void)
{
    return _gui;
}

PseuGUIRunnable::~PseuGUIRunnable()
{
    delete _gui;
}

PseuGUI::PseuGUI()
{
    _xres = 640;
    _yres = 480;
    _colordepth = 32;
    _shadows = false;
    _windowed = true;
    _vsync = false;
    _initialized = false;
    _mustdie = false;
    _driverType = video::EDT_BURNINGSVIDEO; // nulldevice makes not really a sense to display stuff
    _scenestate = SCENESTATE_NULL;
    _smgr = NULL;
    _device = NULL;
    _guienv = NULL;
    _scene = NULL;
    _passtime = _lastpasstime = _passtimediff = 0;
}

PseuGUI::~PseuGUI()
{
    this->Cancel();
    _instance->DeleteGUI(); // this makes the instance set its gui ptr to NULL
}

void PseuGUI::SetDriver(uint8 driverId)
{
    switch(driverId)
    {
    case DIRECTX9: _driverType = video::EDT_DIRECT3D9;break;
    case DIRECTX8: _driverType = video::EDT_DIRECT3D8;break;
    case OPENGL: _driverType = video::EDT_OPENGL;   break;
    case SOFTWARE:      _driverType = video::EDT_SOFTWARE; break;
    case BURNINGSVIDEO: _driverType = video::EDT_BURNINGSVIDEO;break;
    case NULLDEVICE:    _driverType = video::EDT_NULL;     break;
    default: _driverType = video::EDT_BURNINGSVIDEO; // if no valid driver detected, use software
    }

    // TODO: add support for changing driver during runtime?
}

void PseuGUI::SetResolution(uint16 x, uint16 y, uint16 depth)
{
    _xres = x;
    _yres = y;
    _colordepth = depth;
}

void PseuGUI::SetWindowed(bool b)
{
    _windowed = b;
    // see PseuGUI::Init(): !_windowed == fullscreen
}

void PseuGUI::SetVSync(bool b)
{
    _vsync = b;
}

void PseuGUI::UseShadows(bool b)
{
    _shadows = b;
}

// if this fuction is called from another thread the device will not work correctly. o_O
void PseuGUI::_Init(void)
{
    _device = createDevice(_driverType,dimension2d<s32>(_xres,_yres),_colordepth,!_windowed,_shadows,_vsync);
    if(!_device)
    {
        logerror("PseuGUI: Can't use specified video driver, trying software mode...");
        _device = createDevice(video::EDT_SOFTWARE,dimension2d<s32>(_xres,_yres),_colordepth,!_windowed,false,false);
        if(!_device)
        {
            logerror("ERROR: PseuGUI::_Init() failed, no video driver available!");
            return;
        }
        else
        {
            logerror("PseuGUI: Software mode OK!");
        }
    }
    DEBUG(logdebug("PseuGUI::Init() _device=%X",_device));
    _device->setWindowCaption(L"PseuWoW - Initializing");
    _device->setResizeAble(true);
    _driver = _device->getVideoDriver();
    _smgr = _device->getSceneManager();
    _guienv = _device->getGUIEnvironment();
    _timer = _device->getTimer();
    //...
    _initialized = true;
}

void PseuGUI::Cancel(void)
{
    DEBUG(logdebug("PseuGUI::Cancel()"));

    if(_scene)
    {
        _scene->OnDelete();
        delete _scene;
        _scene = NULL;
    }
    if(_device)
    {
        _device->drop();
        _device = NULL;

    }
    _mustdie = true;
}

void PseuGUI::Shutdown(void)
{
     DEBUG(logdebug("PseuGUI::Shutdown()"));
    _mustdie = true;
}

void PseuGUI::Run(void)
{
    if(!_initialized)
        this->_Init();
    if(!_initialized) // recheck
    {
        logerror("PseuGUI: not initialized, using non-GUI mode");
        Cancel();
        return;
    }

    DEBUG(logdebug("PseuGUI::Run() _device=%X",_device));

    int lastFPS = -1, fps = -1;

    while(_device && _device->run() && !_mustdie)
    {
        _lastpasstime = _passtime;
        _passtime = _timer->getTime();
        _passtimediff = _passtime - _lastpasstime;
        // _HandleWindowResize(); // not yet used; doesnt work

        if (!_device->isWindowActive())
        {
            _device->sleep(10); // save cpu & gpu power if not focused
        }

        try
        {
            _UpdateSceneState();

            if(_scene && _initialized)
                _scene->OnUpdate(_passtimediff);

            _driver->beginScene(true, true, 0);

            DrawCurrentScene();

            _smgr->drawAll();
            _guienv->drawAll();

            _driver->endScene();
        }
        catch(...)
        {
            logerror("Unhandled exception in PseuGUI::Run() device=%X smgr=%X objects:%u", _device, _smgr, domgr.StorageSize());
        }

        fps = _driver->getFPS();

        if (lastFPS != fps)
        {
            core::stringw str = L"PseuWoW [";
            str += _driver->getName();
            str += "] FPS:";
            str += fps;

            _device->setWindowCaption(str.c_str());

            lastFPS = fps;
            DEBUG(logdebug("PseuGUI: Current FPS: %u",fps));
        }

    }
    DEBUG(logdebug("PseuGUI::Run() finished"));
    Cancel(); // already got shut down somehow, we can now safely cancel and drop the device
}

// called from ObjMgr::Remove(guid)
void PseuGUI::NotifyObjectDeletion(uint64 guid)
{
    domgr.Delete(guid);
}

// called from ObjMgr::Add(Object*)
void PseuGUI::NotifyObjectCreation(Object *o)
{
    DrawObject *d = new DrawObject(_smgr,o);
    domgr.Add(o->GetGUID(),d);
}

void PseuGUI::SetInstance(PseuInstance* in)
{
    _instance = in;
}

void PseuGUI::SetSceneState(SceneState state)
{
    _scenestate_new = state; // will be applied at next cycle
}

void PseuGUI::_UpdateSceneState(void)
{
    if(_scenestate != _scenestate_new && _smgr)
    {
        if(_scene)
        {
            _scene->OnDelete();
            delete _scene;
        }
        _smgr->clear();
        _guienv->clear();

        _scenestate = _scenestate_new;

        logdebug("PseuGUI: switched to SceneState %u", _scenestate);

        switch (_scenestate)
        {
            case SCENESTATE_GUISTART: _scene = new SceneGuiStart(this); break;
            case SCENESTATE_WORLD: _scene = new SceneWorld(this); break;
            default: _scene = new Scene(this); // will draw nothing, just yield the gui
        }

        logdebug("PseuGUI: scene created.");
    }
}

void PseuGUI::DrawCurrentScene(void)
{
    if(_scene && _initialized)
        _scene->OnDraw();
}

void PseuGUI::_HandleWindowResize(void)
{
    dimension2d<s32> scrn = _driver->getScreenSize();
    if(_screendimension.Width != scrn.Width)
    {
        scrn.Height = s32(scrn.Width * 0.8f); // for now use aspect ratio 5:4
        _screendimension = scrn;
        _driver->OnResize(scrn);
        DEBUG(logdebug("DEBUG: Width resize handled, Height adjusted"));

    }
    else if(_screendimension.Height != scrn.Height)
    {
        scrn.Width = s32(scrn.Height * 1.25); // 5:4 here too
        _screendimension = scrn;
        _driver->OnResize(scrn);
        DEBUG(logdebug("DEBUG: Height resize handled, Width adjusted"));

    }
    // TODO: how to set irrlicht window size ?!

}
