/*
MainControl.cpp
Written by Matthew Fisher

MainControl includes everything that changes often between applications, such as what meshes to load,
and also determines what is rendered each frame.
*/

#include "Main.h"

float SphereFunction(const Vec3f &Pos)
{
    return Pos.x * Pos.x + Pos.y * Pos.y + Pos.z * Pos.z - 1.0f;
}

float RandomFunction(const Vec3f &Pos)
{
    Vec3f P = Pos * 10.0f;
    return sinf(P.Length()) * P.x + sinf(P.Length()) * P.y;
}

float CrazyFunction(const Vec3f &Pos)
{
    Vec3f P = Pos * 3.0f;
    return sinf(P.x * P.y + P.x * P.z + P.y * P.z) + sinf(P.x * P.y) + sinf(P.y * P.z) + sinf(P.x * P.z) - 1.0f;
}

enum MenuEntryType
{
    MenuEntryFile,
    MenuEntryFileQuit,
};

void Controller::FreeMemory()
{
    ReleaseAssets();
}

void Controller::InitMenu()
{
    _menu.Init(_state->window);
    
    Menu &FileMenu = _menu.MenuBar().AddSubMenu(_state->window, MenuEntryFile, "File");
    FileMenu.AddString(_state->window, MenuEntryFileQuit, "Quit\tCtrl+Q");
}

void Controller::ProcessEvents()
{
    _menu.DispatchKeyboardShortcuts(_state->input);

    while(_state->input.EventPresent())
    {
        InputEvent CurEvent = _state->input.PopEvent();
        if(CurEvent.Type == InputEventMenu)
        {
            switch(CurEvent.Id)
            {
            case MenuEntryFileQuit:
                PostQuitMessage(0);
                break;
            }
        }
    }
}

void Controller::ResetCamera()
{
    _camera.Reset(Vec3f(2.0f, 0.0f, 5.0f), Vec3f::eZ, Vec3f::Origin);
}

void Controller::ReleaseAssets()
{
    for(UINT assetIndex = 0; assetIndex < _assetList.Length(); assetIndex++)
    {
        _assetList[assetIndex]->ReleaseMemory();
    }
}

void Controller::ResetAssets()
{
    for(UINT assetIndex = 0; assetIndex < _assetList.Length(); assetIndex++)
    {
        _assetList[assetIndex]->Reset(*_state->graphics);
    }

    ReloadTextures();
}

void Controller::ReSize()
{
    _projection = Matrix4::PerspectiveFov(
                60.0f * Math::PIf / 180.0f,
                _state->window.AspectRatio(),
                0.01f,
                200.0f);
}

void Controller::UpdateIsoSurface()
{
    IsoSurface surfaceGenerator;
    surfaceGenerator.IsoApproximate(1.25f, 0.025f, _assets.surface, _functions[_functionIndex]);
}

void Controller::Init(AppState &state)
{
    _state = &state;
    AllocConsole();

    InitMenu();
}

void Controller::Init3D()
{
    InitAssets();
    ResetCamera();

    _functionIndex = 2;
    _functions[0] = SphereFunction;
    _functions[1] = RandomFunction;
    _functions[2] = CrazyFunction;

    UpdateIsoSurface();
}

void Controller::InitAssets()
{
    _assets.surface.SetGD(*_state->graphics);
    
    _assets.font.Init(*_state->graphics, "Verdana", 20, FW_BOLD);
    _assets.psBase.Init(*_state->graphics, "Assets\\NormalColor.ps");
    _assets.vsBase.Init(*_state->graphics, "Assets\\NormalColor.vs");
    
    _assetList.PushEnd(&_assets.font);
    _assetList.PushEnd(&_assets.psBase);
    _assetList.PushEnd(&_assets.vsBase);

    ReloadTextures();
}

void Controller::ReloadTextures()
{
    Bitmap white(2, 2);
    white.Clear(RGBColor::White);
}

void Controller::UpdateCamera()
{
    _camera.KeyboardWASD  (_state->input, _state->timer.SPF() * 5.0f);
    _camera.KeyboardNumpad(_state->input, _state->timer.SPF());
    _camera.Update();
}

void Controller::ProcessKeyboardInput()
{
    if(_state->input.KeyCheckOnce(KEY_F))
    {
        _state->graphics->ToggleWireframe();
    }
    if(_state->input.KeyCheckOnce(KEY_1))
    {
        _functionIndex = 0;
        UpdateIsoSurface();
    }
    if(_state->input.KeyCheckOnce(KEY_2))
    {
        _functionIndex = 1;
        UpdateIsoSurface();
    }
    if(_state->input.KeyCheckOnce(KEY_3))
    {
        _functionIndex = 2;
        UpdateIsoSurface();
    }
}

void Controller::Status(const String &s, RGBColor c)
{
    _assets.font.DrawString(s, Vec2i(2, _consoleY), c);
    _consoleY += 20;
}

void Controller::StatusText()
{
    _consoleY = 2;
    Status(String("FPS: ") + String(_state->timer.FPS()), RGBColor::Red);
    Status(String("Function ") + String(_functionIndex + 1) + " / " + String(FunctionCount) + String(" (use number keys to switch functions)"), RGBColor::Yellow);
}

void Controller::Render()
{
    _assets.vsBase.Set();
    _assets.psBase.Set();

    Matrix4 viewProj = _camera.Matrix() * _projection;
    _assets.vsBase.SetMatrix("WorldViewProj", viewProj);
    _assets.vsBase.SetMatrix("World", Matrix4::Identity());

    _assets.surface.Render();
}

void Controller::Frame()
{
    ProcessKeyboardInput();
    ProcessEvents();

    UpdateCamera();

    Render();

    StatusText();
}