// Copyright (c) 2021 Dominic Masters
// 
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

#pragma once
#include "../libs.h"
#include "../util/list.h"

/** Debug Inputs */
#define INPUT_NULL        (inputbind_t)0x00

/** Real Inputs (Starts at 32/0x20) */
#define INPUT_UP          (inputbind_t)0x20
#define INPUT_DOWN        (inputbind_t)0x21
#define INPUT_LEFT        (inputbind_t)0x22
#define INPUT_RIGHT       (inputbind_t)0x23
#define INPUT_ACCEPT      (inputbind_t)0x24

/** Additional sources */
#define INPUT_MOUSE_X     (inputsource_t)0x10
#define INPUT_MOUSE_Y     (inputsource_t)0x11

#define INPUT_BIND_COUNT 0x40
#define INPUT_SOURCE_COUNT 0xFF

/**
 * Input Bind, a specific action bind reference for the game engine to use.
 * e.g. "Jump" or "Walk Forward".
 */
typedef uint8_t inputbind_t;

/**
 * Input source identifier. It's up to the platform itself to decide what the
 * hell this number refers to. For most platforms it will be an input, such as a
 * keyboard scancode or a (pad number * button count) + button.
 */
typedef uint8_t inputsource_t;

/**
 * Value that represents the state of an input. Defined as 0-1 where 0 is set
 * to be completely off / netural state, and 1 is completely on / full state.
 */
typedef float inputval_t;

/**
 * Structure for the entire input mapping.
 */
typedef struct {
  /** Float of the input between 0 and 1. */
  inputval_t inputsA[INPUT_BIND_COUNT];
  /** Float of the input between 0 and 1. */
  inputval_t inputsB[INPUT_BIND_COUNT];

  /** Flippable state */
  inputval_t *current, *previous;

  /**
   * Binding Map, Array of lists where index = binding and entry is a list of
   * input sources.
   */
  list_t *bindMap[INPUT_BIND_COUNT];

  /**
   * Input buffer array. Keeps track of raw values from the inputs.
   * The engine will read from the buffer when necessary.
   */
  inputval_t buffer[INPUT_SOURCE_COUNT];

  /** Float of the GameTime that the input was actuated last. */
  float times[INPUT_BIND_COUNT];
} input_t;

/**
 * Initializes the input manager.
 * 
 * @param input The input manager to initialize.
 */
void inputInit(input_t *input);

/**
 * Tick the input manager.
 * 
 * @param input The input manager to update.
 */
void inputUpdate(input_t *input);

/**
 * Destroy the input manager and cleanup.
 * 
 * @param input The input manager to dispose.
 */
void inputDispose(input_t *input);

/**
 * Binds the given input binding to the input source. Essentially allowing any
 * time we fetch the state of bind, we will read the value from source.
 * 
 * @param input The input manager.
 * @param bind The binding to bind against.
 * @param source The source that is being bound.
 */
void inputBind(input_t *input, inputbind_t bind, inputsource_t source);

/**
 * Unbind a previously bound input source from a binding. This method is costly.
 * 
 * @param input The input manager.
 * @param bind The binding to unbind from.
 * @param source The source that is being unbound.
 */
void inputUnbind(input_t *input, inputbind_t bind, inputsource_t source);

/**
 * Set the state of an input.
 * 
 * @param input Input to set the state for.
 * @param source Source to set.
 * @param value Value to set.
 */
void inputStateSet(input_t *input, inputsource_t source, float value);

/**
 * Is the current input "down", being pressed, being moved, not in a state
 * of rest.
 * 
 * @param input The input manager.
 * @param binding The previously bound input binding.
 * @return True if the input vector is non-zero.
 */
bool inputIsDown(input_t *input, inputbind_t binding);

/**
 * Is the current input "up", in a state of rest, not being actioned, moved.
 * 
 * @param input The input manager.
 * @param binding The previously bound input binding.
 * @return True if input vector is zero
 */
bool inputIsUp(input_t *input, inputbind_t binding);

/**
 * Returns true on the first tick that an input was actioned/downed.
 * 
 * @param input The input manager.
 * @param binding The previously bound input binding.
 * @return True if the input vector was non-zeroed this tick but not last. 
 */
bool inputIsPressed(input_t *input, inputbind_t binding);

/**
 * Returns true on the first tick that an input was released/upped.
 * 
 * @param input The input manager.
 * @param binding The previously bound input binding.
 * @return True if the input vector was zeroed this tick but not last. 
 */
bool inputIsReleased(input_t *input, inputbind_t binding);

/**
 * Returns the raw input value as a float between 0 and 1. For digital (buttons)
 * this will typicall be 0 or 1 only. Other analogue inputs will have anywhere
 * within the range.
 * 
 * @param input The input manager.
 * @param binding The previously bound input binding.
 * @return Input state of the axis.
 */
inputval_t inputGetAxis(input_t *input, inputbind_t binding);

/**
 * Returns a raw input value between -1 and 1 between two axis. This would be
 * indicitive of having an input with an axis that can be moved one direction
 * for a positive input and another for a negative input, typically a game
 * controller's analogue sticks.
 * 
 * @param input The input manager.
 * @param postitive The positive axis binding.
 * @param negative The negative axis binding.
 * @return A float between -1 and 1 representing the result of both.
 */
float inputGetFullAxis(input_t *input, inputbind_t positive, inputbind_t negative);

/**
 * Returns the time that an input was actuated at. Actuate would count as a
 * non-zero input for analogue inputs.
 * 
 * @param input The input manager.
 * @param binding The previously bound input binding.
 * @return Game Engine time that an input was non-zeroed
 */
float inputGetAccuated(input_t *input, inputbind_t binding);