/*
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.

See the appropriate header file for a description of what this class does. 
*/

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

const int ShapeCount = 7;   //the number of shapes in the shape ring

void MainControl::InitShapes(GraphicsDevice &GD)
{
    Shapes.Allocate(ShapeCount);    //allocate room for 7 meshes

    int i;
    for(i=0;i<ShapeCount;i++)
        Shapes[i].SetGD(GD);        //associate the 7 meshes with the GD

    //See BaseMesh.h for a full description of each shape creation functions

    Shapes[0].CreatePlane(1.0f, Refinement, Refinement);
    Shapes[0].Rotate(Vec3(1.0f,1.0f,1.0f));                 //create and rotate a plane (rotation is not necessary.)

    Shapes[1].CreateSphere(1.0f,Refinement/10);             //create a sphere starting with an icosahedron and refining
    Shapes[2].CreateSphere(1.0f, Refinement, Refinement);   //create a sphere using longitude/latitude slicing
    Shapes[3].CreateCylinder(0.3f, 1.0f, Refinement, Refinement);
    Shapes[4].CreateBox(1.0f,1.0f,1.0f,Refinement/10);
    Shapes[5].CreateLozenge(0.3f, -eY*0.5f, eY*0.5f, Refinement, Refinement);   //a lozenge is just a rounded cylinder
    Shapes[6].CreateTorus(1.0f, 0.3f, Refinement, Refinement);                  //a torus is just a donut

    //we're going to arrange these 7 shapes on a ring.
    for(i=0;i<ShapeCount;i++)
    {
        float Theta = i / float(ShapeCount) * 2.0f * PIf;
        float Radius = 3.0f;                                                    //angle and radius around the ring
        Shapes[i].Translate(Vec3(cosf(Theta)*Radius,sinf(Theta)*Radius,0.0f));  //translate using polar coordinates
        Shapes[i].ColorNormals();                                               //apply some color
    }
}

void MainControl::ReInit(GraphicsDevice &GD, WindowManager &WM)
{
    if(GD.GetFullScreen()) C.Mouse(0.0f);   //move the mouse to the middle of the screen

    MC.Perspective.PerspectiveFov(60.0f * PIf / 180.0f,                             //the field of view = 60 degrees
                                    float(WM.GetWidth()) / float(WM.GetHeight()),   //the aspect ratio
                                    0.1f,                                           //near Z-plane
                                    20.0f);                                         //far Z-plane
}

void MainControl::Init(GraphicsDevice &GD, WindowManager &WM)
{
    ReInit(GD, WM);

    //initalize the camera
    C.Reset(eX*8.0f,        //the eye vector
            eY,             //the up vector
            Origin);        //the vector we're looking at

    MC.World.Identity();    //zero the world and view matrices
    MC.View.Identity();

    Refinement = 20;        //inital refinement
    InitShapes(GD);         //initalize the shapes

    TotalRotation = Vec3(1.0f, 1.0f, 1.0f); //start with some simple rotation
    CurrentVariable = 2;                    //start by rotating around the y-axis
    SecondsUntilSwitch = 3.0f;              //3 seconds until we switch the axis of rotation

    Time = 0.0f;            //reset the time to 0
}

void MainControl::Render(GraphicsDevice &GD, WindowManager &WM)
{
    Time += T.SPF();                                //advance the current time
    if(KeyCheckOnce(KEY_F)) GD.ToggleWireframe();   //toggle wireframe if the user presses "F"

    C.WindowKeyboard(1.0f,1.0f);                    //move based upon the keyboard
    if(GD.GetFullScreen()) C.Mouse(0.001f);         //move based upon the mouse, if we're in full-screen mode
    C.Update(MC, GD);                               //update the camera and load it into the current graphics device

    SecondsUntilSwitch -= T.SPF();      //decrement seconds left until we switch
    if(SecondsUntilSwitch < 0.0f)       //if we should switch this frame,
    {
        SecondsUntilSwitch = 3.0f;      //reset the switch timer
        CurrentVariable = rand() % 3;   //change the variable
    }

    if(CurrentVariable == 0) TotalRotation.x += T.SPF();
    if(CurrentVariable == 1) TotalRotation.y += T.SPF();
    if(CurrentVariable == 2) TotalRotation.z += T.SPF();    //based upon the variable, update the rotation vector

    Matrix RotationX, RotationY, RotationZ;         //the 3 rotation matrices for the ring
    RotationX.RotationX(TotalRotation.x);
    RotationY.RotationY(TotalRotation.y);
    RotationZ.RotationZ(TotalRotation.z);           //load the rotation matrices
    MC.World = RotationZ * RotationX * RotationY;   //load the product of the 3 matrices as the world matrix,
    GD.LoadMatrix(MC);                              //load this new matrix set into the graphics device

    int i;
    for(i=0;i<ShapeCount;i++)
        Shapes[i].Render();                         //draw all the shapes

    //alter the refinement based upon the keyboard input
    if(KeyCheckOnce(KEY_NUMPADADD))
    {
        Refinement++;
        InitShapes(GD); //if refinement changed, update the shapes accordingly
    }

    if(KeyCheckOnce(KEY_NUMPADSUBTRACT))
    {
        Refinement--;
        if(Refinement <= 3) Refinement = 3; //minimum refinement
        InitShapes(GD); //if refinement changed, update the shapes accordingly
    }

    GD.DrawVector("Camera - > ", C.VecEye, 0, 20);  //draw the camera position as text on the screen
    GD.DrawText("Use the + or - keys on the number pad to alter the refinement.", " ", 0, 40);  //help
    GD.DrawNumber("Refinement -> ", Refinement, 0, 60); //draw the current refinement
}

void MainControl::FreeMemory()
{

}