/***************************************************************************
 *   Copyright (C) 2020 by Santiago González                               *
 *                                                                         *
 ***( see copyright.txt file at root folder )*******************************/

#pragma once

#include<QList>

#include "mcumodule.h"
#include "e-element.h"
#include "transmodule.h"

#define mSTOPBITS m_usart->stopBits()
#define mDATABITS m_usart->dataBits()
#define mDATAMASK m_usart->dataMask()
#define mPARITY   m_usart->parity()

enum parity_t{
    parNONE=0,
    parEVEN=2,
    parODD=3,
};

enum rxError_t{
    frameError =1<<12,
    dataOverrun=1<<13,
    parityError=1<<14
};

class IoPin;
class UartTx;
class UartRx;
class UartSync;
class SerialMonitor;

class UsartModule : public TransModule
{
        friend class eMcu;
    public:
        UsartModule( eMcu* mcu, QString name );
        ~UsartModule();

        int  baudRate() { return m_baudRate; }
        void setBaudRate( int br );

        int dataBits() { return m_dataBits; }
        void setDataBits( int b );

        uint8_t dataMask() { return m_dataMask; }

        int stopBits() { return m_stopBits; }
        void setStopBits( int sb ) { m_stopBits = sb; }

        parity_t parity() { return m_parity; }
        void setParity( int par ) { m_parity = (parity_t)par; }

        virtual uint8_t getBit9Tx(){return 0;}
        virtual void setBit9Rx( uint8_t bit ){;}

        virtual void sendByte( uint8_t data );
        virtual void bufferEmpty(){;}
        virtual void frameSent( uint8_t data );
        virtual void readByte( uint8_t data ){;}
        virtual void byteReceived( uint8_t data );
        virtual void setRxFlags( uint16_t frame ){;}

    protected:
        void setPeriod( uint64_t period );
        void setSynchronous( bool s );

        bool m_synchronous;

        int m_baudRate;

        UartSync* m_uartSync;
        UartTx* m_sender;
        UartRx* m_receiver;

        uint8_t m_mode;
        uint8_t m_stopBits;
        parity_t m_parity;

    private:
        uint8_t m_dataBits;
        uint8_t m_dataMask;
};

class UartSync : public eElement
{
    friend class UsartModule;

    public:
        UartSync( UsartModule* usart, /*eMcu* mcu,*/ QString name );
        ~UartSync();

        virtual void runEvent() override;

        void setClkOffset( uint64_t o ) { m_syncClkOffset = o; }
        void sendSyncData( uint8_t data );
        void setPeriod( uint64_t p ) { m_syncPeriod = p; }

    private:
        int m_currentBit;
        bool m_clkState;

        uint8_t  m_syncData;
        uint16_t m_frame;
        uint64_t m_syncPeriod;
        uint64_t m_syncClkOffset; // Synchronous clock first edge after data set

        UsartModule* m_usart;

        IoPin* m_syncTxPin;
        IoPin* m_syncClkPin;
};

class Interrupt;

class UartTR : public McuModule, public eElement
{
        friend class McuCreator;

    public:
        UartTR( UsartModule* usart, eMcu* mcu, QString name );
        ~UartTR();

        enum state_t{
            usartSTOPPED=0,
            usartIDLE,
            usartTRANSMIT,
            usartCLOCK,
            usartTXEND,
            usartRECEIVE,
        };

        virtual void initialize() override;

        virtual void enable( uint8_t ){;}
        virtual uint8_t getData() { return  m_data; }

        virtual void configureA( uint8_t val ) override;

        bool isEnabled() { return m_enabled; }

        void setPeriod( uint64_t p ) { m_period = p; }
        bool getParity( uint16_t data );

        state_t state() { return m_state; }

        void setPins( QList<IoPin*> pinList );
        IoPin* getPin() { return m_ioPin; }
        IoPin** getPinPointer() { return &m_ioPin; }

        void raiseInt( uint8_t data=0 );

    protected:
        UsartModule* m_usart;
        IoPin* m_ioPin;
        QList<IoPin*> m_pinList;

        uint8_t m_buffer;
        uint8_t m_data;
        uint16_t m_frame;
        uint8_t m_framesize;
        uint8_t m_currentBit;
        uint8_t m_bit9;
        state_t m_state;

        bool m_enabled;

        uint64_t m_period; // Baudrate period
};
