/*
    ZWO EAF Focuser
    Copyright (C) 2019 Jasem Mutlaq (mutlaqja@ikarustech.com)
    Copyright (C) 2023 Jarno Paananen (jarno.paananen@gmail.com)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

#pragma once

#include <EAF_focuser.h>

#include "indifocuser.h"

class ASIEAF : public INDI::Focuser
{
    public:
        ASIEAF(const EAF_INFO &info, const char *name, const std::string &serialNumber);
        virtual ~ASIEAF() override = default;

        /**
         * @brief Returns the serial number of the focuser.
         * @return The serial number as a string.
         */
        const std::string &getSerialNumber() const
        {
            return mSerialNumber;
        }

        /**
         * @brief Returns the EAF_INFO structure for this focuser.
         * @return The EAF_INFO structure.
         */
        const EAF_INFO &getEAFInfo() const
        {
            return mEAFInfo;
        }

        const char * getDefaultName() override;
        virtual bool initProperties() override;
        virtual bool updateProperties() override;
        virtual bool ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n) override;
        virtual bool ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n) override;
        // save settings
        virtual bool saveConfigItems(FILE *fp) override;

    protected:
        virtual bool Connect() override;
        virtual bool Disconnect() override;

        /**
         * @brief MoveAbsFocuser Move to an absolute target position
         * @param targetTicks target position
         * @return IPS_BUSY if motion is in progress. IPS_OK if motion is small and already complete. IPS_ALERT for trouble.
         */
        virtual IPState MoveAbsFocuser(uint32_t targetTicks) override;

        /**
         * @brief MoveRelFocuser Move focuser for a relative amount of ticks in a specific direction
         * @param dir Directoin of motion
         * @param ticks steps to move
         * @return IPS_BUSY if motion is in progress. IPS_OK if motion is small and already complete. IPS_ALERT for trouble.
         */
        virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override;

        /**
         * @brief SyncFocuser Set the supplied position as the current focuser position
         * @param ticks target position
         * @return IPS_OK if focuser position is now set to ticks. IPS_ALERT for problems.
         */
        virtual bool SyncFocuser(uint32_t ticks) override;

        virtual bool ReverseFocuser(bool enabled) override;
        virtual bool SetFocuserMaxPosition(uint32_t ticks) override;
        virtual bool SetFocuserBacklash(int32_t steps) override;
        virtual bool AbortFocuser() override;
        virtual void TimerHit() override;

        virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override;

        virtual void nicknameSet(const char *nickname) override;

        /** Additional Properties to INDI::Focuser */
        INDI::PropertyText    SerialNumberTP {1};

    private:
        // Get initial focuser parameter when we first connect
        void GetFocusParams();
        // Read and update Temperature
        bool readTemperature();
        // Read and update Position
        bool readPosition();
        // Read max position
        bool readMaxPosition();
        // Read reverse state
        bool readReverse();
        // Read Beep state
        bool readBeep();
        // Are we moving?
        bool isMoving();
        // Read backlash
        bool readBacklash();
        // Goto absolute position.
        bool gotoAbsolute(uint32_t position);

        double targetPos { 0 }, lastPos { 0 }, lastTemperature { 0 };

        // Read Only Temperature Reporting
        INDI::PropertyNumber TemperatureNP{1};

        // Beep
        INDI::PropertySwitch BeepSP{2};
        enum
        {
            BEEP_ON,
            BEEP_OFF
        };

        //
        // Temperature compensation
        static constexpr const char * TEMPC_TAB = "Temperature compensation";

        // enabling switch
        INDI::PropertySwitch TempCSP{2};
        enum
        {
            TEMPC_ON,
            TEMPC_OFF
        };
        bool TempCEnabled = false;

        // settings
        INDI::PropertyNumber TempCNP{4};
        enum
        {
            TEMPC_STEPS,
            TEMPC_HYSTER,
            TEMPC_SAMPLES,
            TEMPC_MEAN
        };
        int TempCSteps = 0;
        double TempCHyster = 1;
        int TempCSamples = 5;
        double TempCTotalTemp = 0;
        double TempCLastTemp = -274; // should never reach 0 Kelvin
        int TempCCounter = 0;

        // firmware and SDK versions
        INDI::PropertyText VersionInfoSP{2};

        const uint8_t m_ID;
        const int m_MaxSteps;

        // EAF Info structure
        EAF_INFO mEAFInfo;

        // EAF serial number for nickname
        std::string mSerialNumber;
};
