#include <iostream>
#include <stdio.h>
#include <GL/glut.h>

#include "Globals.h"
#include "Config.h"
#include "Gui.h"
#include "GuiElements.h"
#include "Font.h"
#include "Listener.h"

#define RD_VERSION_TEXT "0.1"


using namespace std;
using namespace RD;
using namespace VRUT;

void IdleFunc();
void DisplayFunc();
void ReshapeFunc(int w, int h);
void KeyboardFunc(unsigned char key, int x, int y);
void SpecialFunc(int key, int x, int y);
void MouseFunc(int button, int state, int x, int y);

bool Init();
bool InitGLUT();
void Release();

int mainWindow;

Gui *gui = NULL;

Font *font = NULL;
Font *fontDigital = NULL;

GuiElementImage *guiElemImage = NULL;
GuiElementText *guiElemText = NULL;
GuiElementDialText *guiElemDialRpm = NULL;
GuiElementDial *guiElemDialSpeed = NULL;
GuiElementDial *guiElemDialWheel = NULL;
GuiElementTextNumber *guiElemTextDistance = NULL;
GuiElementTextNumber *guiElemTextGear= NULL;
GuiElementGauge *guiElemGaugeCharge = NULL;
GuiElementSlider *guiElemSliderGas = NULL;
GuiElementSlider *guiElemSliderBrake = NULL;
GuiElementSlider *guiElemSliderCoupling = NULL;
GuiElementTextVec3 *guiElemTextVec3Position = NULL;
GuiElementTextVec3 *guiElemTextVec3Orientation = NULL;
GuiElementTextInput *guiElemTextInputCommand = NULL;

Texture *texDialRpm = NULL;
Texture *texNeedle = NULL;
Texture *texDialSpeed = NULL;
Texture *texGaugeBase = NULL;
Texture *texGauge = NULL;
Texture *texGaugeCover = NULL;
Texture *texSliderBase = NULL;
Texture *texSliderThumb = NULL;
Texture *texWheel = NULL;

Listener *listener = NULL;

int main(int arg, char **argv)
{
    glutInit(&arg, argv);
    atexit(Release);

    if(Init()) glutMainLoop();
    else return -1;
    return 0;
}

bool Init()
{
    Config *config = new Config();
    Config::instance = config;
    if(config->Init() != RES_OK) return false;
    if(config->Load("config.txt") != RES_OK) return false;

    if(!InitGLUT()) return false;

    Renderer *renderer = new Renderer();
    Renderer::instance = renderer;
    if(renderer->Init() != RES_OK) return false;

    gui = new Gui();
    if(gui->Init() != RES_OK) return false;

    gui->SetSize(vec2(100.0, 100.0));

    font = new Font();
    if(font->Init("data/fonts/basic.bff") != RES_OK) return false;

    fontDigital = new Font();
    if(fontDigital->Init("data/fonts/digital.bff") != RES_OK) return false;
    fontDigital->color = vec3(0.0, 0.0, 0.0);

    texDialRpm = new Texture();
    if(texDialRpm->Init("data/tex/dialrpm.tga") != RES_OK) return false;

    texNeedle = new Texture();
    if(texNeedle->Init("data/tex/needle.tga") != RES_OK) return false;

    texDialSpeed = new Texture();
    if(texDialSpeed->Init("data/tex/dialspeed.tga") != RES_OK) return false;

    texGaugeBase = new Texture();
    if(texGaugeBase->Init("data/tex/gaugebase.tga") != RES_OK) return false;

    texGauge = new Texture();
    if(texGauge->Init("data/tex/gauge.tga") != RES_OK) return false;

    texGaugeCover = new Texture();
    if(texGaugeCover->Init("data/tex/gaugecover.tga") != RES_OK) return false;

    texSliderBase = new Texture();
    if(texSliderBase->Init("data/tex/sliderbase.tga") != RES_OK) return false;

    texSliderThumb = new Texture();
    if(texSliderThumb->Init("data/tex/sliderthumb.tga") != RES_OK) return false;

    texWheel = new Texture();
    if(texWheel->Init("data/tex/wheel.tga") != RES_OK) return false;

    guiElemDialRpm = new GuiElementDialText();
    gui->RegisterElement(guiElemDialRpm);
    guiElemDialRpm->Init();
    guiElemDialRpm->baseElem->SetTexture(texDialRpm);
    guiElemDialRpm->needleElem->SetTexture(texNeedle);
    guiElemDialRpm->textElem->SetFont(fontDigital);

    guiElemDialRpm->SetSize(vec2(256.0, 256.0));
    guiElemDialRpm->SetPos(vec2(0.0, 10.0));

    guiElemDialRpm->needleElem->SetSize(vec2(0.7, 0.7));
    guiElemDialRpm->needleElem->SetPos(vec2(0.15, 0.0));
    guiElemDialRpm->needleElem->SetRotCenter(vec2(0.5, 0.75));

    guiElemDialRpm->textElem->SetSize(vec2(0.1, 0.1));
    guiElemDialRpm->textElem->SetPos(vec2(0.35, 0.78));

    guiElemDialRpm->SetRange(vec2(0.0, 7000.0));
    guiElemDialRpm->SetRotRange(vec2(-2.4, 1.80));
    guiElemDialRpm->SetValue(0.0);


    guiElemDialSpeed = new GuiElementDial();
    gui->RegisterElement(guiElemDialSpeed);

    guiElemDialSpeed->Init();

    guiElemDialSpeed->baseElem->SetTexture(texDialSpeed);
    guiElemDialSpeed->needleElem->SetTexture(texNeedle);

    guiElemDialSpeed->SetSize(vec2(256.0, 256.0));
    guiElemDialSpeed->SetPos(vec2(300.0, 10.0));

    guiElemDialSpeed->needleElem->SetSize(vec2(0.7, 0.7));
    guiElemDialSpeed->needleElem->SetPos(vec2(0.15, 0.0));
    guiElemDialSpeed->needleElem->SetRotCenter(vec2(0.5, 0.75));


    guiElemDialSpeed->SetRange(vec2(0.0, 200.0));
    guiElemDialSpeed->SetRotRange(vec2(-2.25, 2.25));
    guiElemDialSpeed->SetValue(0.0);

    guiElemTextDistance = new GuiElementTextNumber();
    guiElemDialSpeed->AddChild(guiElemTextDistance);
    guiElemTextDistance->SetFont(fontDigital);
    guiElemTextDistance->SetPrecision(1);

    guiElemTextDistance->SetSize(vec2(0.1, 0.1));
    guiElemTextDistance->SetPos(vec2(0.35, 0.78));


    guiElemGaugeCharge = new GuiElementGauge();
    gui->RegisterElement(guiElemGaugeCharge);
    guiElemGaugeCharge->Init();

    guiElemGaugeCharge->baseElem->SetTexture(texGaugeBase);
    guiElemGaugeCharge->gaugeElem->SetTexture(texGauge);
    guiElemGaugeCharge->coverElem->SetTexture(texGaugeCover);

    guiElemGaugeCharge->SetPos(vec2(120.0, 335.0));
    guiElemGaugeCharge->SetSize(vec2(256.0, 64.0));

    guiElemGaugeCharge->SetRange(vec2(0.0, 1.0));
    guiElemGaugeCharge->SetScaleRange(vec2(0.05, 0.95));
    guiElemGaugeCharge->SetValue(0.0);
    //guiElemDialSpeed->SetSize(128.0, 128.0);

    guiElemSliderGas = new GuiElementSlider();
    gui->RegisterElement(guiElemSliderGas);
    guiElemSliderGas->Init();
    guiElemSliderGas->baseElem->SetTexture(texSliderBase);
    guiElemSliderGas->thumbElem->SetTexture(texSliderThumb);

    guiElemSliderGas->thumbElem->SetSize(vec2(0.2, 2.0));
    guiElemSliderGas->thumbElem->SetPos(vec2(0.0, -0.5));

    float sliderLeft = 600.0;
    guiElemSliderGas->SetSize(vec2(16.0, 128.0));
    guiElemSliderGas->SetPos(vec2(sliderLeft, 138.0));
    guiElemSliderGas->SetRot(-M_PI_2);

    guiElemSliderGas->SetRange(vec2(0.0, 1.0));
    guiElemSliderGas->SetPosRange(vec2(-0.1, 0.9));
    guiElemSliderGas->SetValue(0.0);

    guiElemSliderBrake = (GuiElementSlider*) guiElemSliderGas->Clone();
    gui->RegisterElement(guiElemSliderBrake);
    guiElemSliderBrake->SetPos(vec2(sliderLeft + 32.0, 138.0));
    guiElemSliderBrake->SetValue(0.5);

    guiElemSliderCoupling = (GuiElementSlider*) guiElemSliderGas->Clone();
    gui->RegisterElement(guiElemSliderCoupling);
    guiElemSliderCoupling->SetPos(vec2(sliderLeft + 64.0, 138.0));
    guiElemSliderCoupling->SetValue(1.0);

    GuiElementText *guiElemTextStatic = new GuiElementText();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->Init();
    guiElemTextStatic->SetFont(font);
    guiElemTextStatic->SetSize(vec2(32.0, 32.0));

    guiElemTextStatic->SetText("RPM");
    guiElemTextStatic->SetPos(vec2(100.0, 270.0));


    guiElemTextStatic = (GuiElementText*) guiElemTextStatic->Clone();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->SetText("  Speed\nDistance");
    guiElemTextStatic->SetPos(vec2(380.0, 270.0));

    guiElemTextStatic = (GuiElementText*) guiElemTextStatic->Clone();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->SetText("G   B   C\na    r    l\ns    k   u");
    guiElemTextStatic->SetPos(vec2(sliderLeft, 148.0));


    guiElemTextStatic = (GuiElementText*) guiElemTextStatic->Clone();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->SetText("Charge:");
    guiElemTextStatic->SetPos(vec2(10.0, 350.0));

    guiElemTextStatic = (GuiElementText*) guiElemTextStatic->Clone();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->SetText("Position:");
    guiElemTextStatic->SetPos(vec2(10.0, 382.0));

    guiElemTextStatic = (GuiElementText*) guiElemTextStatic->Clone();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->SetText("Orientation:");
    guiElemTextStatic->SetPos(vec2(10.0, 414.0));

    guiElemTextVec3Position = new GuiElementTextVec3();
    gui->RegisterElement(guiElemTextVec3Position);
    guiElemTextVec3Position->Init();

    guiElemTextVec3Position->SetFont(font);
    guiElemTextVec3Position->SetPrecision(6);

    guiElemTextVec3Position->SetPos(vec2(150.0, 382.0));
    guiElemTextVec3Position->SetSize(vec2(32.0, 32.0));

    guiElemTextVec3Orientation = (GuiElementTextVec3*) guiElemTextVec3Position->Clone();
    gui->RegisterElement(guiElemTextVec3Orientation);
    guiElemTextVec3Orientation->SetPos(vec2(150.0, 414.0));
    guiElemTextVec3Orientation->SetPrecision(6);


    guiElemDialWheel = new GuiElementDial();
    gui->RegisterElement(guiElemDialWheel);
    guiElemDialWheel->Init();

    guiElemDialWheel->needleElem->SetTexture(texWheel);

    guiElemDialWheel->needleElem->SetRotCenter(vec2(0.5, 0.5));

    guiElemDialWheel->SetSize(vec2(200.0, 200.0));
    guiElemDialWheel->SetPos(vec2(550.0, 320.0));

    guiElemDialWheel->SetRange(vec2(-1.0, 1.0));
    guiElemDialWheel->SetRotRange(vec2(-3.0 * M_PI, 3.0 * M_PI));

    guiElemTextStatic = (GuiElementText*) guiElemTextStatic->Clone();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->SetText("Wheel");
    guiElemTextStatic->SetPos(vec2(615.0, 520.0));

    guiElemTextStatic = (GuiElementText*) guiElemTextStatic->Clone();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->SetText("Command>");
    guiElemTextStatic->SetPos(vec2(10.0, 568.0));

    guiElemTextInputCommand = new GuiElementTextInput();
    gui->RegisterElement(guiElemTextInputCommand);

    guiElemTextInputCommand->SetFont(font);

    guiElemTextInputCommand->SetPos(vec2(140.0, 568.0));
    guiElemTextInputCommand->SetSize(vec2(32.0, 32.0));


    guiElemTextGear = new GuiElementTextNumber();
    gui->RegisterElement(guiElemTextGear);

    guiElemTextGear->SetFont(fontDigital);

    guiElemTextGear->SetPos(vec2(720.0, 30.0));
    guiElemTextGear->SetSize(vec2(64.0, 64.0));
    guiElemTextGear->SetPrecision(0);
    guiElemTextGear->SetValue(0);

    guiElemTextStatic = (GuiElementText*) guiElemTextStatic->Clone();
    gui->RegisterElement(guiElemTextStatic);
    guiElemTextStatic->SetText("Gear");
    guiElemTextStatic->SetPos(vec2(710.0, 94.0));

    listener = new Listener();
    if(!listener->Init(Config::instance->GetInt("listener_Port", 3000))) return false;

    return true;
}


bool InitGLUT()
{
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);

    glutInitWindowSize(Config::instance->GetInt("g_WindowWidth", 800), Config::instance->GetInt("g_WindowHeight", 600));
    mainWindow = glutCreateWindow((string("VRUT Remote dashboard v") + string(RD_VERSION_TEXT)).c_str());
    if(Config::instance->GetBool("g_FullScreen", false))
    {
        glutFullScreen();
    }
    glutDisplayFunc(DisplayFunc);
    glutKeyboardFunc(KeyboardFunc);
    glutMouseFunc(MouseFunc);
    glutIdleFunc(IdleFunc);
    glutReshapeFunc(ReshapeFunc);
    glutSpecialFunc(SpecialFunc);

    return true;
}


void Release()
{
    if(listener) listener->Release();
    delete listener;

    if(texSliderBase) texSliderBase->Release();
    delete texSliderBase;

    if(texSliderThumb) texSliderThumb->Release();
    delete texSliderThumb;

    if(texGaugeBase) texGaugeCover->Release();
    delete texGaugeCover;

    if(texGauge) texGauge->Release();
    delete texGauge;

    if(texGaugeBase) texGaugeBase->Release();
    delete texGaugeBase;

    if(texDialSpeed) texDialSpeed->Release();
    delete texDialSpeed;

    if(texNeedle) texNeedle->Release();
    delete texNeedle;

    if(texDialRpm) texDialRpm->Release();
    delete texDialRpm;

    if(fontDigital) fontDigital->Release();
    delete fontDigital;

    if(font) font->Release();
    delete font;

    if(gui) gui->Release();
    delete gui;

    if(Renderer::instance) Renderer::instance->Release();
    delete Renderer::instance;

    if(Config::instance) Config::instance->Release();
    delete Config::instance;
}


void IdleFunc()
{
    listener->Update();

    guiElemDialRpm->SetValue(listener->statistics.engineRPM);
    guiElemDialSpeed->SetValue(vec3(listener->statistics.velocity.f).length() * 3.6);
    guiElemTextDistance->SetValue(listener->statistics.distance);
    guiElemGaugeCharge->SetValue(listener->statistics.charge);
    guiElemTextVec3Position->SetValue(listener->statistics.position.f);
    guiElemTextVec3Orientation->SetValue(listener->statistics.orientation.f);

    guiElemSliderGas->SetValue(listener->controls.gas);
    guiElemSliderBrake->SetValue(listener->controls.brake);
    guiElemSliderCoupling->SetValue(listener->controls.coupling);
    guiElemDialWheel->SetValue(listener->controls.wheel);
    guiElemTextGear->SetValue(listener->controls.gear);

    gui->Update();

    glutPostRedisplay();
}


void DisplayFunc()
{
    glClearColor(0.5, 0.5, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    gui->Render();

    glutSwapBuffers();
}

void ReshapeFunc(int w, int h)
{
    glViewport(0, 0, w, h);
    gui->SetSize(vec2(w, h));
}


void KeyboardFunc(unsigned char key, int x, int y)
{
    switch(key)
    {
        case 0x1B: //escape
            exit(0);
            break;

        case 0x0D: //enter
            listener->SendCommand(guiElemTextInputCommand->GetText());
            guiElemTextInputCommand->SetText("");
            break;

        default:
            guiElemTextInputCommand->ProcessKey(key);
    }
}


void SpecialFunc(int key, int x, int y)
{
}

void MouseFunc(int button, int state, int x, int y)
{

}

