#include <bsp_q7s/callbacks/q7sGpioCallbacks.h>
#include <bsp_q7s/core/XiphosWdtHandler.h>
#include <bsp_q7s/objectFactory.h>
#include <dummies/ComCookieDummy.h>
#include <dummies/PcduHandlerDummy.h>
#include <fsfw/health/HealthTableIF.h>
#include <fsfw/power/DummyPowerSwitcher.h>
#include <fsfw_hal/common/gpio/GpioCookie.h>
#include <mission/power/gsDefs.h>
#include <mission/system/systemTree.h>
#include <mission/utility/DummySdCardManager.h>

#include "OBSWConfig.h"
#include "bsp_q7s/core/CoreController.h"
#include "busConf.h"
#include "common/config/devices/addresses.h"
#include "devConf.h"
#include "dummies/helperFactory.h"
#include "eive/objects.h"
#include "fsfw_hal/linux/gpio/LinuxLibgpioIF.h"
#include "linux/ObjectFactory.h"
#include "linux/callbacks/gpioCallbacks.h"
#include "mission/genericFactory.h"
#include "mission/system/com/comModeTree.h"

void ObjectFactory::produce(void* args) {
  ObjectFactory::setStatics();
  HealthTableIF* healthTable = nullptr;
  PusTmFunnel* pusFunnel = nullptr;
  CfdpTmFunnel* cfdpFunnel = nullptr;
  StorageManagerIF* ipcStore = nullptr;
  StorageManagerIF* tmStore = nullptr;

  bool enableHkSets = false;
#if OBSW_ENABLE_PERIODIC_HK == 1
  enableHkSets = true;
#endif

  PersistentTmStores stores;
  readFirmwareVersion();
  new XiphosWdtHandler(objects::XIPHOS_WDT);
  ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel,
                                       *SdCardManager::instance(), &ipcStore, &tmStore, stores, 200,
                                       enableHkSets, true);

  LinuxLibgpioIF* gpioComIF = nullptr;
  SerialComIF* uartComIF = nullptr;
  SpiComIF* spiMainComIF = nullptr;
  I2cComIF* i2cComIF = nullptr;
  createCommunicationInterfaces(&gpioComIF, &uartComIF, &spiMainComIF, &i2cComIF);
  // Adding GPIOs for chip select decoding and initializing them.
  q7s::gpioCallbacks::initSpiCsDecoder(gpioComIF);
  gpioCallbacks::disableAllDecoder(gpioComIF);
  createPlI2cResetGpio(gpioComIF);

  // Hardware is usually not connected to EM, so we need to create dummies which replace lower
  // level components.
  dummy::DummyCfg dummyCfg;
  dummyCfg.addCoreCtrlCfg = false;
  dummyCfg.addCamSwitcherDummy = false;
#if OBSW_ADD_SYRLINKS == 1
  dummyCfg.addSyrlinksDummies = false;
#endif
#if OBSW_ADD_PLOC_SUPERVISOR == 1 || OBSW_ADD_PLOC_MPSOC == 1
  dummyCfg.addPlocDummies = false;
#endif
#if OBSW_ADD_TMP_DEVICES == 1
  std::vector<std::pair<object_id_t, address_t>> tmpDevsToAdd = {{
      {objects::TMP1075_HANDLER_PLPCDU_0, addresses::TMP1075_PLPCDU_0},
      {objects::TMP1075_HANDLER_PLPCDU_1, addresses::TMP1075_PLPCDU_1},
      {objects::TMP1075_HANDLER_IF_BOARD, addresses::TMP1075_IF_BOARD},
  }};
  createTmpComponents(tmpDevsToAdd);
  dummy::Tmp1075Cfg tmpCfg{};
  tmpCfg.addTcsBrd0 = true;
  tmpCfg.addTcsBrd1 = true;
  tmpCfg.addPlPcdu0 = false;
  tmpCfg.addPlPcdu1 = false;
  tmpCfg.addIfBrd = false;
  dummyCfg.tmp1075Cfg = tmpCfg;
#endif
#if OBSW_ADD_GOMSPACE_PCDU == 1
  dummyCfg.addPowerDummies = false;
  // The ACU broke.
  dummyCfg.addOnlyAcuDummy = true;
#endif
#if OBSW_ADD_STAR_TRACKER == 1
  dummyCfg.addStrDummy = false;
#endif
#if OBSW_ADD_SCEX_DEVICE == 0
  dummyCfg.addScexDummy = true;
#endif
#if OBSW_ADD_BPX_BATTERY_HANDLER == 1
  dummyCfg.addBpxBattDummy = false;
#endif
#if OBSW_ADD_ACS_BOARD == 1
  dummyCfg.addAcsBoardDummies = false;
#endif
#if OBSW_ADD_PL_PCDU == 0
  dummyCfg.addPlPcduDummy = true;
#endif

  PowerSwitchIF* pwrSwitcher = nullptr;
#if OBSW_ADD_GOMSPACE_PCDU == 0
  pwrSwitcher = new PcduHandlerDummy(objects::PCDU_HANDLER);
#else
  createPcduComponents(gpioComIF, &pwrSwitcher, enableHkSets);
#endif
  satsystem::EIVE_SYSTEM.setI2cRecoveryParams(pwrSwitcher);

  const char* battAndImtqI2cDev = q7s::I2C_PL_EIVE;
  if (core::FW_VERSION_MAJOR >= 4) {
    battAndImtqI2cDev = q7s::I2C_PS_EIVE;
  }
  static_cast<void>(battAndImtqI2cDev);

#if OBSW_ADD_BPX_BATTERY_HANDLER == 1
  createBpxBatteryComponent(enableHkSets, battAndImtqI2cDev);
#endif
  createPowerController(true, enableHkSets);

  dummy::createDummies(dummyCfg, *pwrSwitcher, gpioComIF, enableHkSets);

  new CoreController(objects::CORE_CONTROLLER, enableHkSets);

  auto* stackHandler = new Stack5VHandler(*pwrSwitcher);
  static_cast<void>(stackHandler);

  // Initialize chip select to avoid SPI bus issues.
  createRadSensorChipSelect(gpioComIF);

#if OBSW_ADD_ACS_BOARD == 1
  createAcsBoardComponents(*spiMainComIF, gpioComIF, uartComIF, *pwrSwitcher, true,
                           adis1650x::Type::ADIS16507);
#else
  // Still add all GPIOs for EM.
  GpioCookie* acsBoardGpios = new GpioCookie();
  createAcsBoardGpios(*acsBoardGpios);
  gpioChecker(gpioComIF->addGpios(acsBoardGpios), "ACS Board");
#endif

#if OBSW_ADD_MGT == 1
  createImtqComponents(pwrSwitcher, enableHkSets, battAndImtqI2cDev);
#endif

#if OBSW_ADD_SYRLINKS == 1
  createSyrlinksComponents(pwrSwitcher);
#endif /* OBSW_ADD_SYRLINKS == 1 */

#if OBSW_ADD_RW == 1
  createReactionWheelComponents(gpioComIF, pwrSwitcher);
#endif

#if OBSW_ADD_STAR_TRACKER == 1
  createStrComponents(pwrSwitcher, *SdCardManager::instance());
#endif /* OBSW_ADD_STAR_TRACKER == 1 */

#if OBSW_ADD_PL_PCDU == 1
  createPlPcduComponents(gpioComIF, spiMainComIF, pwrSwitcher, *stackHandler);
#endif
  createPayloadComponents(gpioComIF, *pwrSwitcher);

#if OBSW_ADD_CCSDS_IP_CORES == 1
  CcsdsIpCoreHandler* ipCoreHandler = nullptr;
  CcsdsComponentArgs ccsdsArgs(*gpioComIF, *ipcStore, *tmStore, stores, *pusFunnel, *cfdpFunnel,
                               &ipCoreHandler, 0, 0);
  createCcsdsIpComponentsWrapper(ccsdsArgs);
#endif /* OBSW_ADD_CCSDS_IP_CORES == 1 */

  /* Test Task */
#if OBSW_ADD_TEST_CODE == 1
  createTestComponents(gpioComIF);
#endif /* OBSW_ADD_TEST_CODE == 1 */
#if OBSW_ADD_SCEX_DEVICE == 1
  createScexComponents(q7s::UART_SCEX_DEV, pwrSwitcher, *SdCardManager::instance(), false,
                       power::Switches::PDU1_CH5_SOLAR_CELL_EXP_5V);
#endif
  createAcsController(true, enableHkSets, *SdCardManager::instance());
  HeaterHandler* heaterHandler;
  createHeaterComponents(gpioComIF, pwrSwitcher, healthTable, heaterHandler);
  createThermalController(*heaterHandler, true);
  satsystem::init(true);
}