#ifndef _JOYSTICK_H_
#define _JOYSTICK_H_

#include "Button.h"
#include "Matrix.h"
#include <Arduino.h>
#include <Mux.h>

using namespace admux;

// If you're using the arduino-big-joystick firmware, these numbers can't be
// changed. If you're writing your own Report Descriptor, tune these to match by
// defining them *before* you include Joystick.h.
#ifndef JOYSTICK_NUM_AXES
#define JOYSTICK_NUM_AXES 8
#endif
#ifndef JOYSTICK_NUM_BUTTONS
#define JOYSTICK_NUM_BUTTONS 40
#endif
#define JOYSTICK_NUM_BYTES (JOYSTICK_NUM_BUTTONS+7)/8

struct JoyReport {
  int16_t axis[JOYSTICK_NUM_AXES];
  uint8_t button[JOYSTICK_NUM_BYTES];
};

bool operator ==(JoyReport a, JoyReport b);
bool operator !=(JoyReport a, JoyReport b);

class Joystick {
  public:
    Joystick(uint8_t release_delay = 50, bool debug = false);
    void Init();
    void Update();

    // Use these functions to add a button to the joystick.
    // Button types are documented in the ButtonType enum. See Button.h for details.
    // If 'pullup' is true, your button should connect the pin to ground. (also be sure that your board supports INPUT_PULLUP on that pin)
    // If 'pullup' is false, your button should connect the pin to VCC.

    // Add a button connected directly to a pin on the microcontroller.
    void AddButton(uint8_t pin, ButtonType type, bool pullup=true);

    // Add a button connected to a channel on a Multiplexer.
    // FIXME: This doesn't currently work!
    void AddMuxButton(uint8_t channel, Mux* mux, ButtonType type, bool pullup=true);

    // Add a button connected to a scan matrix.
    void AddMatrixButton(uint8_t row, uint8_t col, Matrix* matrix, ButtonType type, bool pullup=true);
  
    // Add a rotary encoder. ENCODER button types allow you to treat an encoder as a momentary button or an axis (TODO)
    void AddEncoder(uint8_t pin1, uint8_t pin2, int8_t tick_threshold, ButtonType type);

    // Add an analog axis to the joystick. THIS METHOD IS NOT CURRENTLY TESTED OR SUPPORTED. It might work, but probably not.
    void AddAxis(uint8_t pin);


    // These members should not be used by end users; todo: remember how friend classes work
    void PressButton(uint8_t button);
    void ReleaseButton(uint8_t button);
  
  private:
    void SetAxis(uint8_t axis, int16_t value);
    void ReleaseAllButtons();
    void Write();

    void _UpdateAxis(uint8_t index);

    void _addButton(ButtonType type, Reader* reader);
  
    Button* _buttons[JOYSTICK_NUM_BUTTONS];
    uint8_t _num_buttons;
    uint8_t _virtual_buttons; // a single user-defined button can have multiple virtual buttons.

    uint8_t _axes[JOYSTICK_NUM_AXES];
    uint8_t _num_axes;
    
    JoyReport _joyReport;

    bool _debug;
    uint8_t release_delay;
};

#endif