#pragma once
#ifndef _MOTION_SENSOR_H_
#define _MOTION_SENSOR_H_

#define MOTION_SENSOR_CHECK_INTERVAL_MS 50
#define MOTION_SENSOR_CLICK_THRESHOLD 40

#include "../configuration.h"

#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C

#include "../Power.h"
#include "../PowerFSM.h"
#include "../detect/ScanI2C.h"
#include "../graphics/Screen.h"
#include "../graphics/ScreenFonts.h"
#include "Wire.h"

// Base class for motion processing
class MotionSensor
{
  public:
    explicit MotionSensor(ScanI2C::FoundDevice foundDevice);
    virtual ~MotionSensor(){};

    // Get the device type
    ScanI2C::DeviceType deviceType();

    // Get the device address
    uint8_t deviceAddress();

    // Get the device port
    ScanI2C::I2CPort devicePort();

    // Initialise the motion sensor
    inline virtual bool init() { return false; };

    // The method that will be called each time our sensor gets a chance to run
    // Returns the desired period for next invocation (or RUN_SAME for no change)
    // Refer to /src/concurrency/OSThread.h for more information
    inline virtual int32_t runOnce() { return MOTION_SENSOR_CHECK_INTERVAL_MS; };

    virtual void calibrate(uint16_t forSeconds){};

    // True if this sensor produces the compass heading (screen->setHeading()) in runOnce().
    // Combined accel+magnetometer parts (e.g. BMX160, ICM20948) and standalone magnetometers
    // handled by the accelerometer thread (e.g. BMM150) override this. Used to avoid halting
    // the thread - and freezing the compass - when motion-only features are disabled at runtime.
    inline virtual bool providesHeading() const { return false; };

  protected:
    // Turn on the screen when a tap or motion is detected
    virtual void wakeScreen();

    // Register a button press when a double-tap is detected
    virtual void buttonPress();

#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
    // draw an OLED frame (currently only used by the RAK4631 BMX160 sensor)
    static void drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
#endif

    bool saveMagnetometerCalibration(const char *filePath, float highestX, float lowestX, float highestY, float lowestY,
                                     float highestZ, float lowestZ);
    bool loadMagnetometerCalibration(const char *filePath, float &highestX, float &lowestX, float &highestY, float &lowestY,
                                     float &highestZ, float &lowestZ);
    void beginCalibrationDisplay(bool &showingScreen);
    void finishCalibrationIfExpired(bool &showingScreen, const char *filePath, float highestX, float lowestX, float highestY,
                                    float lowestY, float highestZ, float lowestZ);
    void startCalibrationWindow(uint16_t forSeconds);
    static void seedCalibrationExtrema(float x, float y, float z, float &highestX, float &lowestX, float &highestY,
                                       float &lowestY, float &highestZ, float &lowestZ);
    static void updateCalibrationExtrema(float x, float y, float z, float &highestX, float &lowestX, float &highestY,
                                         float &lowestY, float &highestZ, float &lowestZ);
    static float applyCompassOrientation(float heading);
    static void publishCompassAccelSample(float x, float y, float z);
    static bool getLatestCompassAccelSample(float &x, float &y, float &z, uint32_t &ageMs);

    ScanI2C::FoundDevice device;

    // Do calibration if true
    bool doCalibration = false;
    uint32_t endCalibrationAt = 0;
};

#endif

#endif
