/*********************************************************\
         __      __           __  __
        / /     / / _____    / / / / ______   __     __
       / /     / / /___  \  / / / / / ___  | / /    / /
      / /_____/ / _____| / / / / / / /__/ / / /    / /
     / ______  / / ___  / / / / / / _____/ / /    / /
    / /     / / / /__/ / / / / / / |____  / |____/ /
   /_/     /_/ |______/ /_/ /_/  \_____/  \_____  /
                                         ______/ /
            High Level Game Framework   /_______/

  ------------------------------------------------------

  Copyright (c) 2009 - Rodrigo Braz Monteiro.
  This file is subject to the terms of license.txt.

\*********************************************************/

#include "input_joystick_xinput.h"
#ifdef XINPUT_AVAILABLE
#include <Xinput.h>
#include <sstream>
#include "utils.h"
#include "exception.h"
using namespace Halley;

#pragma comment(lib, "xinput.lib")


InputJoystickXInput::InputJoystickXInput(int number)
	: index(number)
	, enabled(false)
	, cooldown(0)
{
	// Axes
	axes.resize(6);
	axisAdjust = &defaultAxisAdjust;

	// Hat
	hats.resize(1);
	hats[0] = spInputButtonBase(new InputButtonBase(4));

	// Buttons
	init(10);
}


InputJoystickXInput::~InputJoystickXInput()
{
}


std::string InputJoystickXInput::getName() const
{
	std::stringstream str;
	str << "XInput Controller #" << (index+1);
	return str.str();
}


void InputJoystickXInput::update()
{
	// If disabled, only check once every 30 steps, since XInputGetState() is fairly expensive
	if (!enabled && cooldown > 0) {
		cooldown--;
		return;
	}

	XINPUT_STATE state;
	ZeroMemory(&state, sizeof(XINPUT_STATE));

	DWORD result = XInputGetState(index, &state);
	if (result == ERROR_SUCCESS) {	// WTF, Microsoft
		setEnabled(true);

		auto& gamepad = state.Gamepad;
		
		// Update axes
		axes[0] = gamepad.sThumbLX / 32768.0f;
		axes[1] = -gamepad.sThumbLY / 32768.0f;
		axes[2] = gamepad.sThumbRX / 32768.0f;
		axes[3] = -gamepad.sThumbRY / 32768.0f;
		axes[4] = gamepad.bLeftTrigger * 2.0f / 255.0f - 1.0f;
		axes[5] = gamepad.bRightTrigger * 2.0f / 255.0f - 1.0f;

		// Update buttons
		int b = gamepad.wButtons;
		onButtonStatus(0, (b & XINPUT_GAMEPAD_A) != 0);
		onButtonStatus(1, (b & XINPUT_GAMEPAD_B) != 0);
		onButtonStatus(2, (b & XINPUT_GAMEPAD_X) != 0);
		onButtonStatus(3, (b & XINPUT_GAMEPAD_Y) != 0);
		onButtonStatus(4, (b & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0);
		onButtonStatus(5, (b & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0);
		onButtonStatus(6, (b & XINPUT_GAMEPAD_LEFT_THUMB) != 0);
		onButtonStatus(7, (b & XINPUT_GAMEPAD_RIGHT_THUMB) != 0);
		onButtonStatus(8, (b & XINPUT_GAMEPAD_BACK) != 0);
		onButtonStatus(9, (b & XINPUT_GAMEPAD_START) != 0);

		// Update hat
		hats[0]->onButtonStatus(0, (b & XINPUT_GAMEPAD_DPAD_UP) != 0);
		hats[0]->onButtonStatus(1, (b & XINPUT_GAMEPAD_DPAD_RIGHT) != 0);
		hats[0]->onButtonStatus(2, (b & XINPUT_GAMEPAD_DPAD_DOWN) != 0);
		hats[0]->onButtonStatus(3, (b & XINPUT_GAMEPAD_DPAD_LEFT) != 0);
	} else {
		setEnabled(false);
		cooldown = 30;

		// Reset everything
		for (int i=0; i<6; i++) axes[i] = 0;
		for (int i=0; i<10; i++) onButtonStatus(i, false);
		for (int i=0; i<4; i++) hats[0]->onButtonStatus(i, false);
	}
}


void InputJoystickXInput::setEnabled(bool e)
{
	if (enabled != e) {
		enabled = e;
		if (enabled) {
			std::cout << "XInput controller connected on port " << (index+1) << std::endl;
		} else {
			std::cout << "XInput controller disconnected from port " << (index+1) << std::endl;
		}
	}
}


void InputJoystickXInput::setVibration(float high, float low)
{
	XINPUT_VIBRATION vibration;
	ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
	vibration.wLeftMotorSpeed = (WORD) std::max(0.0f, std::min(low * 65535, 65535.0f));
	vibration.wRightMotorSpeed = (WORD) std::max(0.0f, std::min(high * 65535, 65535.0f));
	XInputSetState(index, &vibration);
}


#endif
