/*
 *  Copyright (C) 2014-2018 Team Kodi
 *  This file is part of Kodi - https://kodi.tv
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  See LICENSES/README.md for more information.
 */

#pragma once

#include "addons/binary-addons/AddonInstanceHandler.h"
#include "addons/binary-addons/BinaryAddonBase.h"
#include "addons/kodi-addon-dev-kit/include/kodi/addon-instance/Peripheral.h"
#include "addons/kodi-addon-dev-kit/include/kodi/addon-instance/PeripheralUtils.h"
#include "input/joysticks/JoystickTypes.h"
#include "peripherals/PeripheralTypes.h"
#include "threads/CriticalSection.h"
#include "threads/SharedSection.h"

#include <map>
#include <memory>
#include <vector>

namespace KODI
{
namespace JOYSTICK
{
  class IButtonMap;
  class IDriverHandler;
}
}

namespace PERIPHERALS
{
  class CPeripheral;
  class CPeripheralJoystick;
  class CPeripherals;

  typedef std::vector<kodi::addon::DriverPrimitive> PrimitiveVector;
  typedef std::map<KODI::JOYSTICK::FeatureName, kodi::addon::JoystickFeature> FeatureMap;

  class CPeripheralAddon : public ADDON::IAddonInstanceHandler
  {
  public:
    explicit CPeripheralAddon(const ADDON::BinaryAddonBasePtr& addonInfo, CPeripherals &manager);
    ~CPeripheralAddon(void) override;

    /*!
     * @brief Initialise the instance of this add-on
     */
    bool CreateAddon(void);

    /*!
     * \brief Deinitialize the instance of this add-on
     */
    void DestroyAddon();

    bool         Register(unsigned int peripheralIndex, const PeripheralPtr& peripheral);
    void         UnregisterRemovedDevices(const PeripheralScanResults &results, PeripheralVector& removedPeripherals);
    void         GetFeatures(std::vector<PeripheralFeature> &features) const;
    bool         HasFeature(const PeripheralFeature feature) const;
    PeripheralPtr GetPeripheral(unsigned int index) const;
    PeripheralPtr GetByPath(const std::string &strPath) const;
    bool         SupportsFeature(PeripheralFeature feature) const;
    unsigned int GetPeripheralsWithFeature(PeripheralVector &results, const PeripheralFeature feature) const;
    unsigned int GetNumberOfPeripherals(void) const;
    unsigned int GetNumberOfPeripheralsWithId(const int iVendorId, const int iProductId) const;
    void         GetDirectory(const std::string &strPath, CFileItemList &items) const;

    /** @name Peripheral add-on methods */
    //@{
    bool PerformDeviceScan(PeripheralScanResults &results);
    bool ProcessEvents(void);
    bool SendRumbleEvent(unsigned int index, unsigned int driverIndex, float magnitude);
    //@}

    /** @name Joystick methods */
    //@{
    bool GetJoystickProperties(unsigned int index, CPeripheralJoystick& joystick);
    bool HasButtonMaps(void) const { return m_bProvidesButtonMaps; }
    bool GetFeatures(const CPeripheral* device, const std::string& strControllerId, FeatureMap& features);
    bool MapFeature(const CPeripheral* device, const std::string& strControllerId, const kodi::addon::JoystickFeature& feature);
    bool GetIgnoredPrimitives(const CPeripheral* device, PrimitiveVector& primitives);
    bool SetIgnoredPrimitives(const CPeripheral* device, const PrimitiveVector& primitives);
    void SaveButtonMap(const CPeripheral* device);
    void RevertButtonMap(const CPeripheral* device);
    void ResetButtonMap(const CPeripheral* device, const std::string& strControllerId);
    void PowerOffJoystick(unsigned int index);
    //@}

    void RegisterButtonMap(CPeripheral* device, KODI::JOYSTICK::IButtonMap* buttonMap);
    void UnregisterButtonMap(KODI::JOYSTICK::IButtonMap* buttonMap);

    static inline bool ProvidesJoysticks(const ADDON::BinaryAddonBasePtr& addonInfo)
    {
      return addonInfo->Type(ADDON::ADDON_PERIPHERALDLL)->GetValue("@provides_joysticks").asBoolean();
    }

    static inline bool ProvidesButtonMaps(const ADDON::BinaryAddonBasePtr& addonInfo)
    {
      return addonInfo->Type(ADDON::ADDON_PERIPHERALDLL)->GetValue("@provides_buttonmaps").asBoolean();
    }

  private:
    void UnregisterButtonMap(CPeripheral* device);

    // Binary add-on callbacks
    void TriggerDeviceScan();
    void RefreshButtonMaps(const std::string& strDeviceName = "");
    unsigned int FeatureCount(const std::string &controllerId, JOYSTICK_FEATURE_TYPE type) const;
    JOYSTICK_FEATURE_TYPE FeatureType(const std::string &controllerId, const std::string &featureName) const;

    /*!
     * @brief Helper functions
     */
    static void GetPeripheralInfo(const CPeripheral* device, kodi::addon::Peripheral& peripheralInfo);

    static void GetJoystickInfo(const CPeripheral* device, kodi::addon::Joystick& joystickInfo);
    static void SetJoystickInfo(CPeripheralJoystick& joystick, const kodi::addon::Joystick& joystickInfo);

    /*!
     * @brief Reset all class members to their defaults. Called by the constructors
     */
    void ResetProperties(void);

    /*!
     * @brief Retrieve add-on properties from the add-on
     */
    bool GetAddonProperties(void);

    bool LogError(const PERIPHERAL_ERROR error, const char *strMethod) const;

    static std::string GetDeviceName(PeripheralType type);
    static std::string GetProvider(PeripheralType type);

    // Construction parameters
    CPeripherals &m_manager;

    /* @brief Cache for const char* members in PERIPHERAL_PROPERTIES */
    std::string         m_strUserPath;    /*!< @brief translated path to the user profile */
    std::string         m_strClientPath;  /*!< @brief translated path to this add-on */

    /*!
     * @brief Callback functions from addon to kodi
     */
    //@{
    static void cb_trigger_scan(void* kodiInstance);
    static void cb_refresh_button_maps(void* kodiInstance, const char* deviceName, const char* controllerId);
    static unsigned int cb_feature_count(void* kodiInstance, const char* controllerId, JOYSTICK_FEATURE_TYPE type);
    static JOYSTICK_FEATURE_TYPE cb_feature_type(void* kodiInstance, const char* controllerId, const char* featureName);
    //@}

    /* @brief Add-on properties */
    bool                m_bProvidesJoysticks;
    bool                m_bSupportsJoystickRumble;
    bool                m_bSupportsJoystickPowerOff;
    bool                m_bProvidesButtonMaps;

    /* @brief Map of peripherals belonging to the add-on */
    std::map<unsigned int, PeripheralPtr>  m_peripherals;

    /* @brief Button map observers */
    std::vector<std::pair<CPeripheral*, KODI::JOYSTICK::IButtonMap*> > m_buttonMaps;
    CCriticalSection m_buttonMapMutex;

    /* @brief Thread synchronization */
    mutable CCriticalSection m_critSection;

    AddonInstance_Peripheral m_struct;

    CSharedSection      m_dllSection;
  };
}
