#include "CCamera.h"

/** This is the class constructor*/
CCamera::CCamera()
{
    Point3<float> vZero = Point3<float>(0.0, 0.0, 0.0);     // Init a vVector to 0 0 0 for our position
    Point3<float> vView = Point3<float>(1.0, 0, 0);     // Init a starting view vVector (looking up and out the screen) 
    Point3<float> vUp   = Point3<float>(0.0, 0.0, 1.0);     // Init a standard up vVector (Rarely ever changes)
     
    //determina il centro di rotazione della camera posizionato nel centro della mappa,serve per fare ruotare la mappa
    Point3<float> centro_di_rotazione = Point3<float>(25.0, 70.0, 25.0); 

    m_vcentrorotazione = centro_di_rotazione;
    m_vPosition = vZero;                    // Init the position to zero
    m_vView     = vView;                    // Init the view to a std starting view
    m_vUpVector = vUp;                      // Init the UpVector
}


/** This function sets the camera's position and view and up vVector.*/
void CCamera::PositionCamera(float positionX, float positionY, float positionZ,
                             float viewX,     float viewY,     float viewZ,
                             float upVectorX, float upVectorY, float upVectorZ)
{
    Point3<float> vPosition = Point3<float>(positionX, positionY, positionZ);
    Point3<float> vView     = Point3<float>(viewX, viewY, viewZ);
    Point3<float> vUpVector = Point3<float>(upVectorX, upVectorY, upVectorZ);

    // The code above just makes it cleaner to set the variables.
    // Otherwise we would have to set each variable x y and z.

    m_vPosition = vPosition;                    // Assign the position
    m_vView     = vView;                        // Assign the view
    m_vUpVector = vUpVector;                    // Assign the up vector
}




/** This rotates the view around the position using an axis-angle rotation */
void CCamera::RotateView(float angle, float x, float y, float z)
{
//  Point3<float> vNewView;

    // Get the view vector (The direction we are facing)
    Point3<float> vView = m_vView -m_vPosition;     

    // Calculate the sine and cosine of the angle once
    float cosTheta = (float)cos(angle);
    float sinTheta = (float)sin(angle);
    float newX,newY,newZ;
    // Find the new x position for the new rotated point
    newX  = (cosTheta + (1 -cosTheta) * x * x)  *   vView.x() ;
    newX += ((1 -cosTheta) * x * y -z * sinTheta)   * vView.y();
    newX += ((1 -cosTheta) * x * z + y * sinTheta)  * vView.z();

    // Find the new y position for the new rotated point
    newY  = ((1 -cosTheta) * x * y + z * sinTheta)  * vView.x();
    newY += (cosTheta + (1 -cosTheta) * y * y)      * vView.y();
    newY += ((1 -cosTheta) * y * z -x * sinTheta)   * vView.z();

    // Find the new z position for the new rotated point
    newZ  = ((1 -cosTheta) * x * z -y * sinTheta)   * vView.x();
    newZ += ((1 -cosTheta) * y * z + x * sinTheta)  * vView.y();
    newZ += (cosTheta + (1 -cosTheta) * z * z)      * vView.z();
    // Now we just add the newly rotated vector to our position to set
    // our new rotated view of our camera.
    Point3<float> vNewView = Point3<float>(newX,newY,newZ);


    m_vView = m_vPosition + vNewView;
}


/** This will move the camera forward or backward depending on the speed */
void CCamera::MoveCamera(float speed)
{
    // Get our view vector (The direciton we are facing)
    Point3<float> vVector = m_vView -m_vPosition;
    
    m_vPosition += vVector * speed;     // Add our acceleration to our position's X
    //m_vPosition += vVector * speed;       // Add our acceleration to our position's Z
    m_vView += vVector * speed;         // Add our acceleration to our view's X
    //m_vView += vVector * speed;           // Add our acceleration to our view's Z
}
/** Posizione la camera dell'ambiente OpenGl */
void CCamera::Draw()
{
    
    // Give openGL our camera position, then camera view, then camera up vector
    gluLookAt(m_vPosition.x(), m_vPosition.y(), m_vPosition.z(),    
              m_vView.x(),    m_vView.y(),     m_vView.z(), 
              m_vUpVector.x(), m_vUpVector.y(), m_vUpVector.z());


}

/** This rotates the position around a given point */
void CCamera::RotateAroundPoint(Point3<float> vCenter, float angle, float x, float y, float z)
{
    //Point3<float> vNewPosition;           

    // To rotate our position around a point, what we need to do is find
    // a vector from our position to the center point we will be rotating around.
    // Once we get this vector, then we rotate it along the specified axis with
    // the specified degree.  Finally the new vector is added center point that we
    // rotated around (vCenter) to become our new position.  That's all it takes.

    // Get the vVector from our position to the center we are rotating around
    Point3<float> vPos = m_vPosition -vCenter;
    float newX,newY,newZ;
    // Calculate the sine and cosine of the angle once
    float cosTheta = (float)cos(angle);
    float sinTheta = (float)sin(angle);

    // Find the new x position for the new rotated point
    newX  = (cosTheta + (1 -cosTheta) * x * x)      * vPos.x();
    newX += ((1 -cosTheta) * x * y -z * sinTheta)   * vPos.y();
    newX += ((1 -cosTheta) * x * z + y * sinTheta)  * vPos.z();

    // Find the new y position for the new rotated point
    newY  = ((1 -cosTheta) * x * y + z * sinTheta)  * vPos.x();
    newY += (cosTheta + (1 -cosTheta) * y * y)      * vPos.y();
    newY += ((1 -cosTheta) * y * z -x * sinTheta)   * vPos.z();

    // Find the new z position for the new rotated point
    newZ  = ((1 -cosTheta) * x * z -y * sinTheta)   * vPos.x();
    newZ += ((1 -cosTheta) * y * z + x * sinTheta)  * vPos.y();
    newZ += (cosTheta + (1 -cosTheta) * z * z)      * vPos.z();
    Point3<float> vNewPosition = Point3<float>(newX,newY,newZ);
    // Now we just add the newly rotated vector to our position to set
    // our new rotated position of our camera.
    m_vPosition = vCenter + vNewPosition;
}


/////////////////////////////////////////////////////////////////////////////////
//
// * QUICK NOTES * 
//
// Now we have the ability to look around our world with the mouse!  It's a great
// addition to our camera class isn't it?  Not much code either.  
// 
// Let me further explain what makes this work.  What we are doing is continuously
// putting the cursor in the middle of the screen every time we move the mouse, but
// before we do this, we get the new position that it moved.  Now that we have the
// position it moved from the center, we can do some simple math to find out which
// way we need to move/rotate the camera.  Lets do an example:
// 
// Lets say we changed our SCREEN_WIDTH and SCREEN_HEIGHT to 640 480.  
// That would make our middle point at (320, 240).  THe '*' is the cursor position.
// We will call it P1 (Point 1)
//
//   -----------------------------------
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//  |                * P1 (320, 240)    |
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//   -----------------------------------
// 
//  Now, when we move the mouse, we store that position of where we moved.
//  Let's say we moved the mouse diagonally up and left a little bit.  Our new
//  point would be, let's say  P2 (319, 239)
//
//   -----------------------------------
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//  |              * P2 (319, 239)      |   (Figure is not pixel perfect :) )
//  |               \                   |
//  |                * (320, 240)       |
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//  |                                   |
//   -----------------------------------
//
//  Well, logic tells us that we need to rotate the camera to the left a bit,
//  and move the camera up a bit as well right???  Of course right.
// 
//  Well, the math is simple to find that out.  We just subtract P1 - P2.
//  That will give us a new x and y.  That is called the vector.
// 
//  What will the vector of those 2 points be then?  
//
//  (320 - 319, 240 - 239)          =         (1, 1)
// 
//  Now we have a vector of (1, 1).  In our case, those numbers are too high
//  when we are dealing with radians, not degrees.  To remedy this, we need to
//  make the number a bit smaller, I just divide by a number like 1000.  Now
//  we have a vector of (0.001, 0.001).  We want to pass the X value in as our
//  Y-axis rotation, BUT we need to negate it.  Because if we rotate by a positive number
//  it will move the camera to the RIGHT, and we want to go to the left.  This will take care
//  of when we move the mouse right too.  If the vector is negative, it will make it positive.
//
//  You'll notice we added some math functions at the top of this file.  These help us
//  determine the axis that we need to rotate around for up and down.  By grabbing
//  a 90 degree penpendicular vector to the side of the camera, we can then use
//  that as our axis to rotate around.  This took a bit to figure out at first.
//  It wasn't as simple as just rotating around the x-axis, because we are walking
//  around the x and z axis all the time, so the rotations need to change axises.
//
//  Notice that with the new Point3<float> additions, we can't say:
//
//  Point3<float> vVector = {0, 0, 0};
//  
//  It must be:
//
//  Point3<float> vVector = CVector(0, 0, 0);
//
//  This is because we have a default constructor, so there is no more {}'s.
//  We can now create a Point3<float> on the fly, without having to directory assing
//  a variable name to it.  The default copy constructor does all the work.
//