/*
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 = 3;   //the number of shapes in the shape ring
const int TextureCount = 4; //the number of textures we're loading

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

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

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

    Shapes[0].CreatePlane(2.0f, 20, 20);
    Shapes[0].Rotate(Vec3(1.0f,1.0f,1.0f));     //create and rotate a plane (rotation is not necessary.)
    Shapes[1].CreateSphere(1.0f, 20, 20);       //create a sphere using longitude/latitude slicing
    Shapes[2].CreateCylinder(0.5f, 1.5f, 20, 20);   //create a cylinder

    //There is a problem with texture coordinates on the boundary of meshes.  An edge may contain one edge
    //with texture coordinates 0.99, and another edge with texture coordinates 0.0 (or something just above
    //zero.)  The graphics will interpret this as you want to go from 0.99 very, very rapidly to 0.0,
    //while in fact you want to go from 0.99 to 1.0.  A plane doesn't wrap around so this problem doesn't occur,
    //and for a sphere or cylinder, the region where this occurs is well defined.  Thus, to fix the problem
    //we need to split the mesh along this edge, forcing it to duplicate vertices.  More examples of the
    //splitting functions will come in later examples.  Comment out the lines up till "Shapes[2] += Temp2"
    //if you want to see what the problem is.

    Mesh Temp1, Temp2;                          //temporary meshes to store the intermediate splits
    Plane p;                                    //plane to split around

    Temp1.SetGD(GD); Temp2.SetGD(GD);           //associate the meshes with the graphics device
    p.LoadPointNormal(Origin + eY*0.0001f, eY); //the plane to split about
                                                //the eY*0.0001f is just some small epsilon to avoid equalities.

    Shapes[1].PerfectSplit(p, Temp1, Temp2);
    Shapes[1] = Temp1;
    Shapes[1] += Temp2;

    Shapes[2].PerfectSplit(p, Temp1, Temp2);
    Shapes[2] = Temp1;
    Shapes[2] += Temp2;

    //we're going to arrange these 3 shapes on a ring.
    for(i=0;i<ShapeCount;i++)
    {
        float Theta = i / float(ShapeCount) * 2.0f * PIf;
        float Radius = 1.5f;                                                    //angle and radius around the ring
        if(i != 0)                                                      //the plane already has a natural paramaterization
            Shapes[i].TextureSphericalMap(0);                           //automatically generate texture coordinates for everything else;
                                                                        //note that the cylinder should really use cylindrical coordinates
                                                                        //but I haven't written such a function (although it's rather simple.)

        Shapes[i].Translate(Vec3(cosf(Theta)*Radius,sinf(Theta)*Radius,0.0f));  //translate using polar coordinates
    }
}

void MainControl::InitTextures(GraphicsDevice &GD)
{
    Textures.Allocate(TextureCount);
    Textures[0].Load(GD, "C:\\Code\\MyApps All2\\BaseCodeExamples\\texture1.bmp");
    Textures[1].Load(GD, "C:\\Code\\MyApps All2\\BaseCodeExamples\\texture2.bmp");
    Textures[2].Load(GD, "C:\\Code\\MyApps All2\\BaseCodeExamples\\texture3.bmp");
    Textures[3].Load(GD, "C:\\Code\\MyApps All2\\BaseCodeExamples\\texture4.bmp");  //load the 4 textures
}

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

    InitTextures(GD);       //inialize the textures.  Since textures can be lost when the device loses focus or resizes,
                            //this goes inside ReInit, instead of Init.
}

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

    //initalize the camera
    C.Reset(eX*4.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();

    InitShapes(GD);         //initalize the shapes

    CurTexture = 0;         //start with the first texture

    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

    Textures[CurTexture].Set();

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

    //update CurTexture based upon keyboard input
    if(KeyCheckOnce(KEY_NUMPADADD))
    {
        CurTexture++;
        if(CurTexture == TextureCount) CurTexture = 0;
    }

    if(KeyCheckOnce(KEY_NUMPADSUBTRACT))
    {
        CurTexture--;
        if(CurTexture < 0) CurTexture = TextureCount - 1;
    }

    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 change the texture.", " ", 0, 40);    //help
    GD.DrawNumber("Current Texture -> ", CurTexture, 0, 60);    //display the texture we're rendering
}

void MainControl::FreeMemory()
{

}