/*
	Rogue Trader.
	Copyright (c) 2007, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:   PlayerHuman
// Purpose:      Class for a human player. Does the movement of the ship and
//               controller input for a human player.
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "PlayerHuman.h"

//------------------------------------------------------------------------------
// Static constants
//------------------------------------------------------------------------------

const float PlayerHuman::KEYBOARD_ACCELERATION  = 1500;         // Rate at which the players target can be changed speed per second using the keyboard
const float PlayerHuman::KEYBOARD_SENSITIVITY   = 1.5f;         // affects how much the keyboard turns the ship. higher values mean more turning.
const float PlayerHuman::MOUSE_SENSITIVITY      = 0.003f;       // determines how rotation occurs from mouse movement
const float PlayerHuman::TURN_SMOOTHING         = 0.2f;         // determines how much to smooth rotational movement
const float PlayerHuman::CAM_DIST               = 150.0f;       // distance of the camera from the players ship. external views only.

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     PlayerHuman::PlayerHuman
Purpose:  Constructor for a player. Initialises player data.
Params:   As commented
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

PlayerHuman::PlayerHuman
(
    const char * name           ,   // player name
    GameObjectHook * hookShip   ,   // hook to ship player will control
    double credits              ,   // credits the player will start with
    Game * game                     // game the player belongs to
)
: Player( hookShip , credits )
{
    //--------------------------------------------------------------------------
    // General stuff:
    //--------------------------------------------------------------------------

    // Save the players name:

    this->name = new char[ strlen(name) + 1 ]; memcpy(this->name,name,strlen(name) + 1);

    // Save the game the player belongs to

    this->game = game;

    // Set the number of player kills to zero:

    numKills = 0;

    // Set the current ship rotation amounts to zero

    rotationX = 0;
    rotationY = 0;

    // Get the current held state of all the buttons used by the human player

    rightMouseHeld      = GLWindow::mouseButtonPressed(GLWindow::MOUSE_BUTTON_RIGHT  );
    middleMouseHeld     = GLWindow::mouseButtonPressed(GLWindow::MOUSE_BUTTON_MIDDLE );
    deleteKeyHeld       = GLWindow::keyboardKeyPressed(GLWindow::KEYBOARD_DELETE     );
    backspaceKeyHeld    = GLWindow::keyboardKeyPressed(GLWindow::KEYBOARD_BACKSPACE  );

    // Set the current target of the player's ship as null

    target = 0;

    //--------------------------------------------------------------------------
    // Setup the camera angles for viewing the player's ship:
    //--------------------------------------------------------------------------

    // Create the cameras first of all:

    for ( unsigned i = 0 ; i < 8 ; i++ ) cameras[i] = new Camera();

    // Set the current camera as the first camera:

    cameraAngle = 0;

    // 1st camera is first person view:

    cameras[0]->rotateY( PI );

    // Setup the 3rd camera: From behind looking at ship:

    cameras[2]->rotateY( PI );
    cameras[2]->setPosition( 0, 0, -CAM_DIST );

    // Setup the 4th camera: In front looking at ship:

    cameras[3]->setPosition( 0, 0, CAM_DIST );

    // Setup the 5th camera: At left looking at ship

    cameras[4]->rotateY( -PI_2 );
    cameras[4]->setPosition( -CAM_DIST, 0, 0 );

    // Setup the 6th camera: At right looking at ship

    cameras[5]->rotateY( PI_2 );
    cameras[5]->setPosition( CAM_DIST, 0, 0 );

    // Setup the 7th camera: On top looking at ship

    cameras[6]->rotateX( -PI_2 );
    cameras[6]->setPosition( 0 , CAM_DIST , 0 );

    // Setup the 8th camera: On bottom looking at ship

    cameras[7]->rotateX( PI_2 );
    cameras[7]->setPosition( 0 , -CAM_DIST ,0);

    //--------------------------------------------------------------------------
    // Now update all the cameras to follow the ship:
    //--------------------------------------------------------------------------

    updateCameras();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     PlayerHuman::~PlayerHuman
Purpose:  Destructor. Frees all resources.
Params:   n/a
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

PlayerHuman::~PlayerHuman()
{
    // Delete all cameras

    for ( unsigned i = 0 ; i < 8 ; i++ ) delete cameras[i];

    // Remove the player name

    delete[] name;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     PlayerHuman::setName
Purpose:  Sets the name of the player
Params:   n/a
Returns:  n/a

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void PlayerHuman::setName( const char * name )
{
    // Abort if name is null:

    if ( name == 0 ) return;

    // Delete the old name:

    delete[] this->name;

    // Save the players name:

    this->name = new char[ strlen(name) + 1 ]; memcpy(this->name,name,strlen(name) + 1);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     PlayerHuman::makeMove
Purpose:  Does the players move. Checks for input from the user and moves ship
          accordingly.
Params:   time the last frame took in seconds
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void PlayerHuman::makeMove( float frameTime )
{
    //--------------------------------------------------------------------------
    // Get the players ship: if it doesn't exist anymore then abort
    //--------------------------------------------------------------------------

    GameObject_Ship * ship = (GameObject_Ship*) hookShip->getObject();

    // If the ship no longer exxists then abort

    if ( ship == 0 )
    {
        // Set the players current target to null first:

        target = 0;

        // Abort any further input processing

        return;
    }

    //--------------------------------------------------------------------------
    // Initialise the amount we are going to turn the ship this frame as zero
    // for both x and y axis rotations:
    //--------------------------------------------------------------------------

    float rotateX = 0;
    float rotateY = 0;

    //--------------------------------------------------------------------------
    // Check for increase / decrease speed keys
    //--------------------------------------------------------------------------

    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_RETURN ) )
    {
        // Increase ship target speed

        ship->accelerate( KEYBOARD_ACCELERATION * frameTime );
    }

    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_SHIFT ) )
    {
        // Decrease ship target speed

        ship->accelerate( -KEYBOARD_ACCELERATION * frameTime );
    }

    //--------------------------------------------------------------------------
    // Check for orientation changing keys:
    //--------------------------------------------------------------------------

    if ( GLWindow::keyboardKeyPressed( 'W' ) )
    {
        // W Pressed: rotate ship downwards

        rotateX += KEYBOARD_SENSITIVITY * frameTime;
    }

    if ( GLWindow::keyboardKeyPressed( 'S' ) )
    {
        // S Pressed: rotate ship upwards

        rotateX -= KEYBOARD_SENSITIVITY * frameTime;
    }

    if ( GLWindow::keyboardKeyPressed( 'A' ) )
    {
        // A Pressed: rotate ship left

        rotateY += KEYBOARD_SENSITIVITY * frameTime;
    }

    if ( GLWindow::keyboardKeyPressed( 'D' ) )
    {
        // D Pressed: rotate ship right

        rotateY -= KEYBOARD_SENSITIVITY * frameTime;
    }

    //--------------------------------------------------------------------------
    // Check for camera angle changes:
    //--------------------------------------------------------------------------

    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_F1 ) ) cameraAngle = 0;
    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_F2 ) ) cameraAngle = 1;
    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_F3 ) ) cameraAngle = 2;
    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_F4 ) ) cameraAngle = 3;
    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_F5 ) ) cameraAngle = 4;
    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_F6 ) ) cameraAngle = 5;
    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_F7 ) ) cameraAngle = 6;
    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_F8 ) ) cameraAngle = 7;

    //--------------------------------------------------------------------------
    // Check if the fire missile key has just been pressed:
    //--------------------------------------------------------------------------

    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_BACKSPACE ) )
    {
        // See if the backspace key has not been pressed before:

        if ( backspaceKeyHeld == false )
        {
            // Make sure the player has a target before firing missile

            if ( this->target != 0 )
            {
                if ( ship->getNumMissiles() != 0 )
                {
                    // Can launch a missile. Launch one.

                    ship->fireMissile(this->target);
                }
                else
                {
                    // Pause the game:

                    game->pause();

                    // Player has no missiles. cannot launch

                    GuiScreen::showMessageDialog("Cannot launch missile","Your ship is out of missiles");

                    // Unpause the game:

                    game->unpause();
                }
            }
            else
            {
                // Pause the game:

                game->pause();

                // Player has no target. issue a warning message:

                GuiScreen::showMessageDialog("Cannot launch missile","You do not have a current target");

                // Unpause the game:

                game->unpause();
            }


        }   // end if backspace key not already pressed before

        // Backspace key pressed: set key down flag to true

        backspaceKeyHeld = true;
    }
    else
    {
        // Backspace or fire missle key not pressed: set key down flag to false

        backspaceKeyHeld = false;
    }

    //--------------------------------------------------------------------------
    // Check if the deploy emp bomb key has been pressed
    //--------------------------------------------------------------------------

    if ( GLWindow::keyboardKeyPressed( GLWindow::KEYBOARD_DELETE ) )
    {
        // See if the delete key has not been pressed before:

        if ( deleteKeyHeld == false )
        {
            // See if the players ship has any emp bombs left:

            if ( ship->getNumEmpBombs() != 0 )
            {
                // Can deploy an emp bomb. do it:

                ship->deployEmp();
            }
            else
            {
                // Pause the game:

                game->pause();

                // No emp bombs left. issue a warning message:

                GuiScreen::showMessageDialog("Cannot deploy EMP bomb","You do not any EMP bombs left");

                // unpause the game:

                game->unpause();
            }
        }

        // Delete key pressed: set key down flag to true

        deleteKeyHeld = true;
    }
    else
    {
        // Delete or deploy emp key not pressed: set key down flag to false

        deleteKeyHeld = false;
    }

    //--------------------------------------------------------------------------
    // See if the right mouse button is currently held:
    //--------------------------------------------------------------------------

    if ( GLWindow::mouseButtonPressed( GLWindow::MOUSE_BUTTON_RIGHT ) )
    {
        //----------------------------------------------------------------------
        // See if the right mouse button was held the last time the player made its move:
        //----------------------------------------------------------------------

        if ( rightMouseHeld )
        {
            // Get the center of the client are where the mouse is when fully centered in screen coordinates:

            int centerX = GLWindow::getClientCenterX();
            int centerY = GLWindow::getClientCenterY();

            // Get the mouse position from the window in screen coordinates

            int mouseX = GLWindow::getMouseScreenX();
            int mouseY = GLWindow::getMouseScreenY();

            // Get how far from this position the mouse has moved:

            int moveX = centerX - mouseX;
            int moveY = centerY - mouseY;

            // Now update the current amounts to rotate the ship by using this info:

            rotateX += moveY * MOUSE_SENSITIVITY;
            rotateY += moveX * MOUSE_SENSITIVITY;

            // Re-center the mouse:

            GLWindow::centerMouse();

            // Make sure the mouse is invisible if it is in the client area of the window:

            if ( GLWindow::mouseInClientArea() )
            {
                GLWindow::setCursor( GLWindow::CURSOR_NONE );
            }
        }
        else
        {
            // See if the mouse is in the client area of the window first

            if ( GLWindow::mouseInClientArea() )
            {
                // Right mouse not held before. Make cursor invisible.

                GLWindow::setCursor( GLWindow::CURSOR_NONE );

                // Get and save the current position of the mouse for later restoration when dragging is over:

                POINT p; GetCursorPos(&p);

                mouseBeforeDragX = p.x;
                mouseBeforeDragY = p.y;

                // Center the mouse position in the windows client area:

                GLWindow::centerMouse();

                // Set the right mouse button to held

                rightMouseHeld = true;
            }
        }
    }
    else
    {
        // See if the right mouse button was held the last time the player made its move:

        if ( rightMouseHeld )
        {
            // Player has released the right mouse button. Set the cursor back to the normal arrow cursor:

            GLWindow::setCursor( GLWindow::CURSOR_ARROW );

            // Get the scaling amount to normalise screen coordinates to a 0-65535 range: needed for mouse_event function

            float normaliseX = ( 65535 ) / float( GLWindow::getScreenWidth()  );
            float normaliseY = ( 65535 ) / float( GLWindow::getScreenHeight() );

            // Restore the last mouse position by moving cursor to saved mouse coordinates:

            mouse_event
            (
                MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
                int( mouseBeforeDragX * normaliseX ),
                int( mouseBeforeDragY * normaliseY ),
                0,
                0
            );

            // Set the right mouse button as not being held any more

            rightMouseHeld = false;
        }
    }

    //--------------------------------------------------------------------------
    // See if the middle mouse button is being held:
    //--------------------------------------------------------------------------

    if ( GLWindow::mouseButtonPressed( GLWindow::MOUSE_BUTTON_MIDDLE ) )
    {
        //----------------------------------------------------------------------
        // If the right mouse button is held and the left mouse button was not
        // held the last time we checked then fire a missile if possible. But
        // only do this if the player is in first person mode...
        //----------------------------------------------------------------------

        if ( rightMouseHeld && middleMouseHeld == false )
        {
            // See if the players ship has missiles and the player has a target:

            if ( inFirstPersonMode() && this->target && ship->getNumMissiles() != 0 )
            {
                // Fire a missile at the selected target

                ship->fireMissile(target);
            }
        }

        // Set the middle mouse button to being held

        middleMouseHeld = true;
    }
    else
    {
        // middle mouse button not held. set the middle mouse button held flag to false.

        middleMouseHeld = false;
    }

    //--------------------------------------------------------------------------
    // Smooth the current rotation amount with the last rotation amount and save:
    //--------------------------------------------------------------------------

    // Get a frame rate independent turn smoothing amount.

    float smooth = powf( TURN_SMOOTHING , frameTime );  if ( smooth > 1 ) smooth = 1;

    // Now smooth out the movement

    this->rotationX = this->rotationX * smooth + ( 1.0f - smooth ) * rotateX;
    this->rotationY = this->rotationY * smooth + ( 1.0f - smooth ) * rotateY;

    //--------------------------------------------------------------------------
    // Now rotate the players ship by the turn amount:
    //--------------------------------------------------------------------------

    ship->rotateY( this->rotationY );
    ship->rotateX( this->rotationX );

    //--------------------------------------------------------------------------
    // Get the best target available for the player's ship and save: only if
    // in first person mode though...
    //--------------------------------------------------------------------------

    if ( inFirstPersonMode() )
    {
        // First get the scene the player's ship belongs to:

        GameObjectScene * scene = ship->getScene();

        // Now get the best target for the players ship:

        target = scene->getBestTargetForObject(ship);
    }
    else
    {
        // Not in first person mode: no target

        target = 0;
    }

    //--------------------------------------------------------------------------
    // If the left and right mouse buttons are held then fire the ships laser,
    // or if the space bar is pressed then fire it too:
    //--------------------------------------------------------------------------

    if (
            (
                GLWindow::mouseButtonPressed(GLWindow::MOUSE_BUTTON_LEFT)
                &&
                GLWindow::mouseButtonPressed(GLWindow::MOUSE_BUTTON_RIGHT)
            )
            ||
            GLWindow::keyboardKeyPressed(GLWindow::KEYBOARD_SPACE)
        )
    {
        // Player wants to fire: fire the lasers

        ship->fireLasers(frameTime);
    }
    else
    {
        // Not firing: cool down the ships lasers:

        ship->coolLasers(frameTime);
    }

    //--------------------------------------------------------------------------
    // Make all the players cameras follow the ship:
    //--------------------------------------------------------------------------

    updateCameras();

}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     PlayerHuman::updateCameras
Purpose:  Makes all of the cameras follow the players ship:
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void PlayerHuman::updateCameras()
{
    // If we have a valid game object hook to a ship then try and get the ship:

    if ( hookShip != 0 )
    {
        //----------------------------------------------------------------------
        // Get the ship:
        //----------------------------------------------------------------------

        GameObject_Ship * ship = (GameObject_Ship*) hookShip->getObject();

        if ( ship )
        {
            //------------------------------------------------------------------
            // Make all the cameras follow the ship:
            // 1st and 2nd views which are 1st person do not draw the ship:
            //------------------------------------------------------------------

            cameras[0]->follow( ship , false );
            cameras[1]->follow( ship , false );
            cameras[2]->follow( ship , true  );
            cameras[3]->follow( ship , true  );
            cameras[4]->follow( ship , true  );
            cameras[5]->follow( ship , true  );
            cameras[6]->follow( ship , true  );
            cameras[7]->follow( ship , true  );

            //------------------------------------------------------------------
            // For the two first person views, set the position of the camera in
            // ship coordinates based on the camera cockpit position of the ship,
            // which is also given in ship coordinates...
            //------------------------------------------------------------------

            float cockpitPosition[3]; ship->getCockitCameraPosition(cockpitPosition);

            cameras[0]->setPosition
            (
                cockpitPosition[0],
                cockpitPosition[1],
                cockpitPosition[2]
            );

            cameras[1]->setPosition
            (
                cockpitPosition[0],
                cockpitPosition[1],
                cockpitPosition[2]
            );
        }
    }
}



