#include "SpectatorCameraManipulator.h"
#include <windows.h>

const Vector3 SpectatorCameraManipulator::_forward(0.0f, 0.0f, 1.0f);
const Vector3 SpectatorCameraManipulator::_right(1.0f, 0.0f, 0.0f);
const Vector3 SpectatorCameraManipulator::_up(0.0f, 1.0f, 0.0f);


SpectatorCameraManipulator::SpectatorCameraManipulator(Camera* camera)
: CameraManipulator(camera),
_desiredYaw(0.0f), _desiredPitch(0.0f), _yaw(0.0f), _pitch(0.0f), 
_lastMouseX(0.0f), _lastMouseY(0.0f), _screenLastMouseX(0), _screenLastMouseY(0)
{
    _inputForward = 0.0f;
    _inputBackward = 0.0f;
    _inputLeft = 0.0f;
    _inputRight = 0.0f;
}

SpectatorCameraManipulator::~SpectatorCameraManipulator()
{
}

void SpectatorCameraManipulator::update(float elapsedTime)
{
    lock();

    Vector3 forward = _forward;
    Vector3 right = _right;

    
    if (_desiredYaw > _yaw) 
        _yaw += min((_desiredYaw - _yaw), (_desiredYaw - _yaw) * (elapsedTime / _mouseSmoothingInterval));
    else if (_desiredYaw < _yaw)
        _yaw += max((_desiredYaw - _yaw), (_desiredYaw - _yaw) * (elapsedTime / _mouseSmoothingInterval));

    if (_desiredPitch > _pitch) 
        _pitch += min((_desiredPitch - _pitch), (_desiredPitch - _pitch) * (elapsedTime / _mouseSmoothingInterval));
    else if (_desiredPitch < _pitch)
        _pitch += max((_desiredPitch - _pitch), (_desiredPitch - _pitch) * (elapsedTime / _mouseSmoothingInterval));


    forward = Vector3::rotateAroundAxis(forward, _up, _yaw);
    right = Vector3::rotateAroundAxis(right, _up, _yaw);

    forward = Vector3::rotateAroundAxis(forward, right, _pitch);


    Vector3 direction = forward * _inputForward + forward * _inputBackward + right * _inputLeft + right * _inputRight;
    
    if (direction.magnitudeSq() != 0.0f)
        direction.normalize();

    direction *= _speed * elapsedTime;

    _eye += direction;
    _center = _eye + forward;

    if (_camera != 0)
        _camera->setView(_eye, _center, _up);

    unlock();
}

void SpectatorCameraManipulator::handleEvent(Event& event)
{
    lock();

    switch(event.type) {
        case Event::KEY_DOWN:
            switch(_keyBindings[event.keyCode]) {
                case MoveForward:
                    if (_active) _inputForward = 1.0f;
                    break;

                case MoveBackward:
                    if (_active) _inputBackward = -1.0f;
                    break;

                case MoveLeft:
                    if (_active) _inputLeft = -1.0f;
                    break;

                case MoveRight:
                    if (_active) _inputRight= 1.0f;
                    break;
            }
            break;

        case Event::KEY_UP:
            switch(_keyBindings[event.keyCode]) {
                case MoveForward:
                    if (_active) _inputForward = 0.0f;
                    break;

                case MoveBackward:
                    if (_active) _inputBackward = 0.0f;
                    break;

                case MoveLeft:
                    if (_active) _inputLeft = 0.0f;
                    break;

                case MoveRight:
                    if (_active) _inputRight = 0.0f;
                    break;
            }
            break;

        case Event::MOUSE_MIDDLE_DOWN:
            _active = !_active;

            if (_active) {
                _lastMouseX = event.mouseX;
                _lastMouseY = event.mouseY;

                POINT point;
                point.x = (LONG) _lastMouseX;
                point.y = (LONG) _lastMouseY;
                ClientToScreen(GetActiveWindow(), &point);
                _screenLastMouseX = point.x;
                _screenLastMouseY = point.y;

                ShowCursor(FALSE);
            }
            else {
                _inputLeft = 0.0f;
                _inputRight = 0.0f;
                _inputForward = 0.0f;
                _inputBackward = 0.0f;

                ShowCursor(TRUE);
            }
            break;

        case Event::MOUSE_MOVE:
            if (_active) {
                if ((event.mouseX != _lastMouseX) || (event.mouseY != _lastMouseY)) {
                    _desiredYaw   += (_mouseSensitivity * (event.mouseX - _lastMouseX)) / 200.0f;
                    _desiredPitch += (_mouseSensitivity * (event.mouseY - _lastMouseY)) / 200.0f;

                    if (_desiredPitch > 1.5533430342749533234620847839549f)
                        _desiredPitch = 1.5533430342749533234620847839549f;
                    else if (_desiredPitch < -1.5533430342749533234620847839549f)
                        _desiredPitch = -1.5533430342749533234620847839549f;

                    SetCursorPos(_screenLastMouseX, _screenLastMouseY);
                }
            }
            break;
    }

    unlock();
}