/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_PLATFORM_COMMON_OWIMASTER_H
#define ECXX_PLATFORM_COMMON_OWIMASTER_H


#include <eCxx/CTL/BasicOutputWriter.h>


namespace eCxx {


//
// A simple OWI-master driver
//
// WARNING : All functions derived from BasicOutputWriter<T> that accept parameters with data types
//           'PGMCharPointer' and 'PGMCharPointerFar' expect the pointers to come from program
//           memory exclusively!
//
// NOTE    : Whenever possible, use the versions that accept near program pointers because those that
//           accept the far program pointers may have more overhead and restrictions.
//
template <typename ApplicationT>
class Application_OWIMaster : public BasicOutputWriter< Application_OWIMaster<ApplicationT> > {

    public:
        struct OneWireDevice {
            uint8_t id[8];
        };

        static constexpr uint8_t Invalid = 0xFF; // Indicates error or nothing found

    public:
        Application_OWIMaster(const Application_OWIMaster& ) = delete;
        Application_OWIMaster(      Application_OWIMaster&&) = delete;

        Application_OWIMaster& operator=(const Application_OWIMaster& ) = delete;
        Application_OWIMaster& operator=(      Application_OWIMaster&&) = delete;

    public:
        inline Application_OWIMaster();
        inline ~Application_OWIMaster();

        inline uint8_t crc8(uint8_t in, uint8_t seed) const;
        inline uint16_t crc16(uint8_t in, uint16_t seed) const;

        inline bool begin(uint8_t pin);
        inline void end();

        inline bool reset() const;

        inline bool write(uint8_t value) const;
        inline bool read(uint8_t& value) const;
        inline uint8_t read() const; // This version ignore possible error

        inline void skipROM() const;
        inline void matchROM(const uint8_t* values8) const;
        inline void readROM(uint8_t* values8) const;

        inline void matchROM(const uint8_t (&value)[8]) const;
        inline void readROM(uint8_t (&value)[8]) const;

        inline uint8_t searchROM(uint8_t (&bitPattern)[8], uint8_t lastDeviation) const;

        template <uint8_t N>
        inline uint8_t searchBus(OneWireDevice (&slaveDevice)[N]) const;

        template <uint8_t N>
        inline uint8_t findFamily(const OneWireDevice (&slaveDevice)[N], uint8_t familyID, uint8_t startOffset = 0) const;

        template <uint8_t N, uint8_t F>
        inline uint8_t findFamily(const OneWireDevice (&slaveDevice)[N], const uint8_t (&familyID)[F], uint8_t startOffset = 0) const;

    private:
        friend ApplicationT;

        static constexpr uint16_t Delay_A   =    6;
        static constexpr uint16_t Delay_B   =   64;
        static constexpr uint16_t Delay_C   =   60;
        static constexpr uint16_t Delay_D   =   10;
        static constexpr uint16_t Delay_E   =    9;
        static constexpr uint16_t Delay_F   =   55;
        static constexpr uint16_t Delay_G   =    0;
        static constexpr uint16_t Delay_H   =  480;
        static constexpr uint16_t Delay_I   =   70;
        static constexpr uint16_t Delay_J   =  410;

        static constexpr uint8_t  cmdSkip   = 0xCC;
        static constexpr uint8_t  cmdMatch  = 0x55;
        static constexpr uint8_t  cmdRead   = 0x33;
        static constexpr uint8_t  cmdSearch = 0xF0;

    private:
        typename ApplicationT::OWIData _owiData;

        inline bool crcROM(const uint8_t (&romValue)[8]) const;
};


//
// Include the implementation file
//
#define INCLUDE_ECXX_PLATFORM_COMMON_OWIMASTER_TPP
#include "OWIMaster.tpp"


} // namespace eCxx


#endif
