#pragma once

#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_i2c.hpp"

namespace pimoroni {

  class MSA301 {
    //--------------------------------------------------
    // Constants
    //--------------------------------------------------
  public:
    static const uint8_t DEFAULT_I2C_ADDRESS    = 0x26;
    
    static const uint8_t SOFT_RESET             = 0x00;
    static const uint8_t PART_ID                = 0x01;
    static const uint8_t MOTION_INTERRUPT       = 0x09;
    static const uint8_t DATA_INTERRUPT         = 0x0a;
    static const uint8_t ORIENTATION_STATUS     = 0x0c;
    static const uint8_t RESOLUTION_RANGE       = 0x0f;
    static const uint8_t POWER_MODE_BANDWIDTH   = 0x11;
    static const uint8_t SET_AXIS_POLARITY      = 0x12;
    static const uint8_t INTERRUPT_ENABLE_0     = 0x16;
    static const uint8_t INTERRUPT_ENABLE_1     = 0x17;
    static const uint8_t INTERRUPT_LATCH_PERIOD = 0x21;
    static const uint8_t FREEFALL_DURATION      = 0x22;


    //--------------------------------------------------
    // Enums
    //--------------------------------------------------
  public:
    enum Axis {
      X = 0x02,
      Y = 0x04,
      Z = 0x06
    };

    enum Orientation {
      PORTRAIT            = 0b00,
      PORTRAIT_INVERTED   = 0b01,
      LANDSCAPE           = 0b10,
      LANDSCAPE_INVERTED  = 0b11
    };

    enum PowerMode {
      NORMAL  = 0b00,
      LOW     = 0b01,
      SUSPEND = 0b10
    };

    enum Range {
      G_2   = 0b00,
      G_4   = 0b01,
      G_8   = 0b10,
      G_16  = 0b11
    };

    enum Resolution {
      BITS_14   = 0b0000,
      BITS_12   = 0b0100,
      BITS_10   = 0b1000,
      BITS_8    = 0b1100
    };
    
    enum AxisPolarity {
      INVERT_X  = 0b1000,
      INVERT_Y  = 0b0100,
      INVERT_Z  = 0b0010,
      XY_SWAP   = 0b0001
    };

    enum Interrupt {
      NONE          = 0,
      ACTIVE        = 0b0000111,
      NEW_DATA      = 0b1000000000000,
      FREEFALL      = 0b0100000000000,
      ORIENTATION   = 0b1000000,
      SINGLE_TAP    = 0b0100000,
      DOUBLE_TAP    = 0b0010000,
      Z_ACTIVE      = 0b0000100,
      Y_ACTIVE      = 0b0000010,
      X_ACTIVE      = 0b0000001
    };

    enum InterruptLatchPeriod {
      LATCH_1MS     = 0b1001,
      LATCH_2MS     = 0b1011,
      LATCH_25MS    = 0b1100,
      LATCH_50MS    = 0b1101,
      LATCH_100MS   = 0b1110,
      LATCH_250MS   = 0b0001,
      LATCH_500MS   = 0b0010,
      LATCH_1S      = 0b0011,
      LATCH_2S      = 0b0100,
      LATCH_4S      = 0b0101,
      LATCH_8S      = 0b0110
    };


    //--------------------------------------------------
    // Variables
    //--------------------------------------------------
  private:
    I2C *i2c;
    const uint8_t address  = DEFAULT_I2C_ADDRESS;
    uint interrupt         = PIN_UNUSED;

    //--------------------------------------------------
    // Constructors/Destructor
    //--------------------------------------------------
  public:
    MSA301() : MSA301(new I2C()) {};

    MSA301(I2C *i2c, uint interrupt = PIN_UNUSED) : i2c(i2c), interrupt(interrupt) {}


    //--------------------------------------------------
    // Methods
    //--------------------------------------------------
  public:
    bool init();
    void reset();

    i2c_inst_t* get_i2c() const;
    int get_sda() const;
    int get_scl() const;
    int get_int() const;

    uint8_t part_id();
    float get_axis(Axis axis, uint8_t sample_count = 1);
    float get_x_axis(uint8_t sample_count = 1);
    float get_y_axis(uint8_t sample_count = 1);
    float get_z_axis(uint8_t sample_count = 1);
    Orientation get_orientation();

    void set_power_mode(MSA301::PowerMode power_mode);
    void set_range_and_resolution(Range range, MSA301::Resolution resolution);
    void set_axis_polarity(uint8_t polarity);

    void disable_all_interrupts();
    void enable_interrupts(uint16_t interrupts);
    void set_interrupt_latch(InterruptLatchPeriod latch_period, bool reset_latched);
    bool read_interrupt(Interrupt interrupt);
  };
}

