// CameraStateEngine.cpp ********************************************//
// Copyright Microsoft Corporation, 2001                             //
// Created     Owner     Modification                                //
// --------    ------    -------------                               //
// 3/7/2001  sdemar    class definition for spectator state engine   //
//*******************************************************************//
#include "CameraStateEngine.h"
using namespace MSRSpectator;
#include "SpectatorDebug.h"
#include <stdlib.h>
#include <string>
#include <iostream>
using namespace std;

#include "MW4.hpp"
#include "MWApplication.hpp"
#include "GameOS\GameOS.hpp"
#include "Mech.hpp"



CCameraStateEngine::CCameraStateEngine()
	: 
		m_cameraState(STATE_STD_OBSERVERMODE),
        m_cameraDist(NO_RANGE),
		m_fSelectState(false),
		m_distanceBetween(0.0f),
		m_fNewShot(true),
		m_fPrevWasOverShoulder(false)

{
	m_stateTimer.Initialize(3);
	m_dampTimer.Expire();
}


CCameraStateEngine::~CCameraStateEngine()
{
	// stubbed
}


void CCameraStateEngine::TestInstance(void) const
{
	// stubbed, required for Check_Object()
}


void CCameraStateEngine::ForceStateChange()
{
	m_stateTimer.Expire();
	m_fSelectState = true;
	NullSpectatorState();
}


void CCameraStateEngine::NullSpectatorState()
{
    if (m_stateTimer.CheckExpired())
	    m_stateTimer.Initialize(1.5);
	m_cameraState = STATE_STD_OBSERVERMODE;
    m_cameraDist = NO_RANGE;
}


void CCameraStateEngine::BumpStateTimer(double addAmt)
{
	double timeRemaining = m_stateTimer.GetTimeRemaining();
	if (timeRemaining > 0.0 && timeRemaining < addAmt)
		m_stateTimer.Initialize(timeRemaining + addAmt);
	else
		m_stateTimer.Initialize(addAmt);

	
	m_fSelectState = false;
}



double CCameraStateEngine::GetDampTimerRemaining()
{
	return m_dampTimer.GetTimeRemaining();
}


void CCameraStateEngine::InitDampTimer(double inTime)
{
	m_dampTimer.Initialize(inTime);
}


void CCameraStateEngine::ExpireDampTimer()
{
	m_dampTimer.Expire();
}


int CCameraStateEngine::GetCameraStateToExecute(bool& stateChanged, int& cameraDist)
{
	// NOTE, normally in a state engine, code would execute here per state.
	// However we want to keep the game specific code in the game components.
	// In our case that is camera work.  So all we do here is monitor the
	// state timer condition as to whether its time for a new state.

		if (m_stateTimer.CheckExpired())
		{
			m_fSelectState = true;
		}

		stateChanged = m_fSelectState;

	// And we return the camera state to the game engine so it can deal with
	// executing the current MW4 Spectator specific cameras.
	// NOTE: We execute once more on current camera because we may need to store
	// game parameters to transition between this and the upcoming camera.
    cameraDist = m_cameraDist;
	return m_cameraState;
}


void CCameraStateEngine::StateTransitionExecute(bool fIsSamePairing, float distanceBetween)
{
	// Here is where we do some basic camera choosing based on distance.  We also
	// randomly choose between cameras good at a given distance to break things up 
	// a little more.  Since we are depending on indeterminate action you will adjust this
	// section as appropriate for the kind of action you will encounter in your game.

	// If the spectator mode ever becomes a cached-time process this section will change
	// dramatically as you will be able to include logic for predictive decision making.  
	// Such as being able to look ahead in the data stream to see an event of interest about to 
	// happen somewhere in the world.  In those situations you may want to deliberately move
	// or place a camera before the event occurs to get a more interesting shot.

	if (m_fSelectState == true)
	{
		m_fSelectState = false;

		if (distanceBetween <= MAX_SIDE_DIST)
		{
			if (rand() % 3 == 0)
            { // 33.3%
				m_stateTimer.Initialize(1.5);
				m_cameraState = STATE_FIXED_TRACKING_SHOT;
                m_cameraDist = MEDIUMSHOT;
			}
			else
			{ // 66.6%
				m_stateTimer.Initialize(2.0);
				m_cameraState = STATE_SIDE_SHOT;
                m_cameraDist = NO_RANGE;
			}
		}
		else if ((MAX_SIDE_DIST < distanceBetween) && (distanceBetween <= 210.0f))
		{
			// over shoulder shots require some attention. Its possible for our hero
			// to be engaging two or more enemys in random order.  To keep the camera
			// from radically jogging from one to another enemy, we use a transitioning
			// shot.
			if (m_fPrevWasOverShoulder || ((rand() % 3) != 0))
			{ // 66.6%
				m_fPrevWasOverShoulder = false;
				m_stateTimer.Initialize(1.0);
				m_cameraState = STATE_FRONT_SHOT;
                m_cameraDist = CLOSESHOT;
			}
			else
			{ // 33.3%
				m_fPrevWasOverShoulder = true;
				m_stateTimer.Initialize(3.0);
				m_cameraState = STATE_OVERSHOULDER_SHOT;
                m_cameraDist = MEDIUMSHOT;
			}
		}
		else if ((210.0f < distanceBetween) && (distanceBetween <= 290.0f))
		{
			int cameraState, cameraDist;
            do {
                switch(rand() % 3) {
                case 0:
                    cameraState = STATE_FRONT_SHOT;
                    break;
                case 1:
                    cameraState = STATE_OVERSHOULDER_SHOT;
                    break;
                default:
                    cameraState = STATE_FIXED_TRACKING_SHOT;
                    break;
                }
                switch(rand() % 3) {
                case 0:
                    cameraDist = CLOSESHOT;
                    break;
                case 1:
                    cameraDist = MEDIUMSHOT;
                    break;
                default:
                    cameraDist = LONGSHOT;
                    break;
                }
            } while(((cameraState == m_cameraState) && (cameraDist == m_cameraDist)) || ((cameraState == STATE_OVERSHOULDER_SHOT) && m_fPrevWasOverShoulder));
            switch(cameraState) {
            default:
                m_fPrevWasOverShoulder = false;
                m_stateTimer.Initialize(2.0);
                break;
            case STATE_STD_OBSERVERMODE:
                m_fPrevWasOverShoulder = true;
				m_stateTimer.Initialize(3.0);
                break;
            }
            m_cameraState = cameraState;
            //m_cameraDist = cameraDist;
            if (cameraState == STATE_FRONT_SHOT) {
                m_cameraDist = MEDIUMSHOT;
            } else {
                m_cameraDist = LONGSHOT;
            }
        }
        else
		{
			int cameraState, cameraDist;
            do {
                switch(rand() % 3) {
                case 0:
                    cameraState = STATE_FRONT_SHOT;
                    break;
                case 1:
                    cameraState = STATE_OVERSHOULDER_SHOT;
                    break;
                case 2:
                    cameraState = STATE_FIXED_TRACKING_SHOT;
                default:
                    cameraState = STATE_STD_OBSERVERMODE;
                    break;
                }
                if (cameraState == STATE_STD_OBSERVERMODE) {
                    cameraDist = NO_RANGE;
                } else {
                    switch(rand() % 3) {
                    case 0:
                        cameraDist = CLOSESHOT;
                        break;
                    case 1:
                        cameraDist = MEDIUMSHOT;
                        break;
                    default:
                        cameraDist = LONGSHOT;
                        break;
                    }
                }
            } while(((cameraState == m_cameraState) && (cameraDist == m_cameraDist)) || ((cameraState == STATE_OVERSHOULDER_SHOT) && m_fPrevWasOverShoulder));
            switch(cameraState) {
            default:
                m_fPrevWasOverShoulder = false;
				m_stateTimer.Initialize(1.5);
                break;
            case STATE_FIXED_TRACKING_SHOT:
                m_fPrevWasOverShoulder = false;
				m_stateTimer.Initialize(2.0);
                break;
            case STATE_STD_OBSERVERMODE:
                m_fPrevWasOverShoulder = true;
                m_stateTimer.Initialize(3.0);
                break;
            }
            m_cameraState = cameraState;
            //m_cameraDist = cameraDist;
			switch (cameraState)
			{
			case STATE_FIXED_TRACKING_SHOT:
				m_cameraDist = LONGSHOT;
				break;
			
			case STATE_FRONT_SHOT:
				m_cameraDist = CLOSESHOT;
				break;
			
			case STATE_OVERSHOULDER_SHOT:
				m_cameraDist = CLOSESHOT;
				break;
			case STATE_STD_OBSERVERMODE:
				m_cameraDist = NO_RANGE;
				break;
			}
		}
	}
}



bool CCameraStateEngine::GetIsTimeToSelectNewShot()
{
	return m_fSelectState;
}


bool CCameraStateEngine::fIsValidSideDistance(float dist) const
{
	return (dist >= 4.0f && dist <= MAX_SIDE_DIST);
}