#ifndef MISSION_CONTROLLER_THERMALCONTROLLER_H_
#define MISSION_CONTROLLER_THERMALCONTROLLER_H_

#include <fsfw/controller/ExtendedControllerBase.h>
#include <fsfw/devicehandlers/DeviceHandlerThermalSet.h>
#include <fsfw/timemanager/Countdown.h>
#include <mission/controller/controllerdefinitions/ThermalControllerDefinitions.h>
#include <mission/devices/devicedefinitions/Max31865Definitions.h>
#include <mission/devices/devicedefinitions/Tmp1075Definitions.h>
#include <mission/devices/devicedefinitions/susMax1227Helpers.h>

#include <list>

#include "mission/devices/HeaterHandler.h"
#include "mission/devices/devicedefinitions/GomspaceDefinitions.h"
#include "mission/trace.h"

/**
 * NOP Limit: Hard limit for device, usually from datasheet. Device damage is possible lif NOP limit
 * is exceeded.
 * OP Limit: Soft limit. Device should be switched off or TCS controller should take action if the
 * limit is exceeded to avoid reaching NOP limit
 */
struct TempLimits {
  TempLimits(float nopLowerLimit, float opLowerLimit, float cutOffLimit, float opUpperLimit,
             float nopUpperLimit)
      : opLowerLimit(opLowerLimit),
        opUpperLimit(opUpperLimit),
        cutOffLimit(cutOffLimit),
        nopLowerLimit(nopLowerLimit),
        nopUpperLimit(nopUpperLimit) {}
  float opLowerLimit;
  float opUpperLimit;
  float cutOffLimit;
  float nopLowerLimit;
  float nopUpperLimit;
};

class ThermalController : public ExtendedControllerBase {
 public:
  static const uint16_t INVALID_TEMPERATURE = 999;
  static const uint8_t NUMBER_OF_SENSORS = 16;

  ThermalController(object_id_t objectId, HeaterHandler& heater);

  ReturnValue_t initialize() override;

 protected:
  void performThermalModuleCtrl();
  ReturnValue_t handleCommandMessage(CommandMessage* message) override;
  void performControlOperation() override;
  ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
                                        LocalDataPoolManager& poolManager) override;
  LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;

  // Mode abstract functions
  ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode,
                                 uint32_t* msToReachTheMode) override;

 private:
  static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TCS_CONTROLLER;
  static constexpr Event NO_VALID_SENSOR_TEMPERATURE = MAKE_EVENT(0, severity::MEDIUM);
  static constexpr Event NO_HEALTHY_HEATER_AVAILABLE = MAKE_EVENT(1, severity::MEDIUM);
  static constexpr Event SYRLINKS_OVERHEATING = MAKE_EVENT(2, severity::HIGH);
  static constexpr Event PLOC_OVERHEATING = MAKE_EVENT(3, severity::HIGH);
  static constexpr Event OBC_OVERHEATING = MAKE_EVENT(4, severity::HIGH);
  static constexpr Event HPA_OVERHEATING = MAKE_EVENT(5, severity::HIGH);
  static constexpr Event PLPCDU_OVERHEATING = MAKE_EVENT(6, severity::HIGH);

  static const uint32_t DELAY = 500;

  static const uint32_t TEMP_OFFSET = 5;

  enum class InternalState { STARTUP, INITIAL_DELAY, READY };

  InternalState internalState = InternalState::STARTUP;

  HeaterHandler& heaterHandler;

  tcsCtrl::SensorTemperatures sensorTemperatures;
  tcsCtrl::SusTemperatures susTemperatures;
  tcsCtrl::DeviceTemperatures deviceTemperatures;
  tcsCtrl::HeaterInfo heaterInfo;
  lp_vec_t<int16_t, 9> currentVecPdu2 =
      lp_vec_t<int16_t, 9>(gp_id_t(objects::PDU2_HANDLER, PDU::pool::PDU_CURRENTS));

  DeviceHandlerThermalSet imtqThermalSet;

  // Temperature Sensors
  MAX31865::PrimarySet max31865Set0;
  MAX31865::PrimarySet max31865Set1;
  MAX31865::PrimarySet max31865Set2;
  MAX31865::PrimarySet max31865Set3;
  MAX31865::PrimarySet max31865Set4;
  MAX31865::PrimarySet max31865Set5;
  MAX31865::PrimarySet max31865Set6;
  MAX31865::PrimarySet max31865Set7;
  MAX31865::PrimarySet max31865Set8;
  MAX31865::PrimarySet max31865Set9;
  MAX31865::PrimarySet max31865Set10;
  MAX31865::PrimarySet max31865Set11;
  MAX31865::PrimarySet max31865Set12;
  MAX31865::PrimarySet max31865Set13;
  MAX31865::PrimarySet max31865Set14;
  MAX31865::PrimarySet max31865Set15;

  TMP1075::Tmp1075Dataset tmp1075SetTcs0;
  TMP1075::Tmp1075Dataset tmp1075SetTcs1;
  TMP1075::Tmp1075Dataset tmp1075SetPlPcdu0;
  // damaged
  // TMP1075::Tmp1075Dataset tmp1075SetPlPcdu1;
  TMP1075::Tmp1075Dataset tmp1075SetIfBoard;

  // SUS
  susMax1227::SusDataset susSet0;
  susMax1227::SusDataset susSet1;
  susMax1227::SusDataset susSet2;
  susMax1227::SusDataset susSet3;
  susMax1227::SusDataset susSet4;
  susMax1227::SusDataset susSet5;
  susMax1227::SusDataset susSet6;
  susMax1227::SusDataset susSet7;
  susMax1227::SusDataset susSet8;
  susMax1227::SusDataset susSet9;
  susMax1227::SusDataset susSet10;
  susMax1227::SusDataset susSet11;

  // TempLimits
  TempLimits acsBoardLimits = TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0);
  TempLimits mgtLimits = TempLimits(-40.0, -40.0, 65.0, 70.0, 70.0);
  TempLimits rwLimits = TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0);
  TempLimits strLimits = TempLimits(-30.0, -20.0, 65.0, 70.0, 80.0);
  TempLimits ifBoardLimits = TempLimits(-65.0, -40.0, 80.0, 85.0, 150.0);
  TempLimits tcsBoardLimits = TempLimits(-60.0, -40.0, 80.0, 85.0, 130.0);
  TempLimits obcLimits = TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0);
  TempLimits obcIfBoardLimits = TempLimits(-65.0, -40.0, 80.0, 85.0, 125.0);
  TempLimits sBandTransceiverLimits = TempLimits(-40.0, -25.0, 35.0, 40.0, 65.0);
  TempLimits pcduP60BoardLimits = TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0);
  TempLimits pcduAcuLimits = TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0);
  TempLimits pcduPduLimits = TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0);
  TempLimits plPcduBoardLimits = TempLimits(-55.0, -40.0, 80.0, 85.0, 125.0);
  TempLimits plocMissionBoardLimits = TempLimits(-30.0, -10.0, 40.0, 45.0, 60);
  TempLimits plocProcessingBoardLimits = TempLimits(-30.0, -10.0, 40.0, 45.0, 60.0);
  TempLimits dacLimits = TempLimits(-65.0, -40.0, 113.0, 118.0, 150.0);
  TempLimits cameraLimits = TempLimits(-40.0, -30.0, 60.0, 65.0, 85.0);
  TempLimits droLimits = TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0);
  TempLimits x8Limits = TempLimits(-40.0, -30.0, -75.0, 80.0, 90.0);
  TempLimits hpaLimits = TempLimits(-40.0, -30.0, -75.0, 80.0, 90.0);
  TempLimits txLimits = TempLimits(-40.0, -30.0, -75.0, 80.0, 90.0);
  TempLimits mpaLimits = TempLimits(-40.0, -30.0, -75.0, 80.0, 90.0);
  TempLimits scexBoardLimits = TempLimits(-60.0, -40.0, 80.0, 85.0, 150.0);

  double sensorTemp = INVALID_TEMPERATURE;
  bool redSwitchNrInUse = false;
  bool componentAboveCutOffLimit = false;

  // Initial delay to make sure all pool variables have been initialized their owners
  Countdown initialCountdown = Countdown(DELAY);

#if OBSW_THREAD_TRACING == 1
  uint32_t opCounter = 0;
#endif

  std::array<std::pair<bool, double>, 5> sensors;
  uint8_t numSensors = 0;

  PoolEntry<float> tmp1075Tcs0 = PoolEntry<float>({10.0});
  PoolEntry<float> tmp1075Tcs1 = PoolEntry<float>({10.0});
  PoolEntry<float> tmp1075PlPcdu0 = PoolEntry<float>({10.0});
  PoolEntry<float> tmp1075PlPcdu1 = PoolEntry<float>({10.0});
  PoolEntry<float> tmp1075IfBrd = PoolEntry<float>({10.0});
  PoolEntry<uint8_t> heaterSwitchStates = PoolEntry<uint8_t>(heater::NUMBER_OF_SWITCHES);
  PoolEntry<int16_t> heaterCurrent = PoolEntry<int16_t>();

  static constexpr dur_millis_t MUTEX_TIMEOUT = 50;

  void resetSensorsArray();
  void copySensors();
  void copySus();
  void copyDevices();

  void ctrlComponentTemperature(heater::Switchers switchNr, heater::Switchers redSwitchNr,
                                TempLimits& tempLimit);
  void ctrlHeater(heater::Switchers switchNr, heater::Switchers redSwitchNr, TempLimits& tempLimit);
  bool chooseHeater(heater::Switchers& switchNr, heater::Switchers redSwitchNr);
  bool selectAndReadSensorTemp();

  void ctrlAcsBoard();
  void ctrlMgt();
  void ctrlRw();
  void ctrlStr();
  void ctrlIfBoard();
  void ctrlTcsBoard();
  void ctrlObc();
  void ctrlObcIfBoard();
  void ctrlSBandTransceiver();
  void ctrlPcduP60Board();
  void ctrlPcduAcu();
  void ctrlPcduPdu();
  void ctrlPlPcduBoard();
  void ctrlPlocMissionBoard();
  void ctrlPlocProcessingBoard();
  void ctrlDac();
  void ctrlCameraBody();
  void ctrlDro();
  void ctrlX8();
  void ctrlHpa();
  void ctrlTx();
  void ctrlMpa();
  void ctrlScexBoard();
};

#endif /* MISSION_CONTROLLER_THERMALCONTROLLER_H_ */