/**
 * @file Receiver.hpp
 * @brief Header file for the Receiver class.
 * @author Adam Prochazka <xproch0f>
 *
 * This file contains the declaration of the Receiver class which is responsible for
 * managing the serial communication and processing the received data to display images.
 */


#include "main.hpp"

#ifndef DISPLAYER_HPP
#include "Displayer.hpp"
#endif

/**
 * @class Receiver
 * @brief Handles receiving data from the serial port and buffering the data for the Displayer.
 */
class Receiver{
    private:
        int cdcFile;
        struct termios tty;
        int currentBufferIndex = 0;
        std::mutex currentBufferIndexMutex;
        std::vector<uint8_t> buffer_initial{
        0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0x00, 0x43, 0x01, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x11,
        0x08, 0x00, 0x60, 0x00, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff,
        0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x92, 0x8a, 0x00,
        0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45,
        0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3, 0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89,
        0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad,
        0x4c, 0x84, 0xa2, 0xb3, 0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25,
        0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3,
        0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1,
        0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3, 0x19, 0x62, 0x8a, 0x00,
        0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45,
        0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3, 0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89,
        0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad,
        0x4c, 0x84, 0xa2, 0xb3, 0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25,
        0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3,
        0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1,
        0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3, 0x19, 0x62, 0x8a, 0x00,
        0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45,
        0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3, 0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89,
        0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad,
        0x4c, 0x84, 0xa2, 0xb3, 0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25,
        0x15, 0x98, 0xc6, 0xd1, 0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3,
        0x19, 0x62, 0x8a, 0x00, 0x4a, 0x2b, 0x31, 0x89, 0x45, 0x6a, 0x64, 0x25, 0x15, 0x98, 0xc6, 0xd1,
        0x5b, 0x1b, 0x09, 0x45, 0x66, 0x31, 0x28, 0xad, 0x4c, 0x84, 0xa2, 0xb3, 0x19, 0xff, 0xd9
        };
        std::vector<uint8_t> buffer1;
        std::vector<uint8_t> buffer2;
        std::vector<uint8_t> buffer3;
        Displayer *dis;

        int distance = 0;
        int sequenceEndedFF = 0; ///< Flag to indicate if the sequence has ended with 0xFF.
        int simulateIdx = 0;

        int debugFileIdx = 0;
    public:
        /**
        * @brief Constructor for the Receiver class.
        * @param displayerPtr Pointer to the Displayer instance.
        */
        Receiver(Displayer *displayerPtr);

        /**
        * @brief Opens the serial port stream.
        * @param port Name of serial port to be openned.
        */
        void openStream(char * port);
        
        /**
        * @brief Closes the serial port stream.
        */
        void closeStream();

        /**
        * @brief Initializes the serial port settings.
        * @return 1 if successful, -1 if an error occurred.
        */
        int initSerial();

        /**
        * @brief Reads data from the CDC device.
        * @param[out] character Pointer to the character buffer to store the read data.
        * @return 0 if successful, -1 if error occurred.
        */
        int readCdcData(unsigned char (*character)[CDC_FRAME_SIZE]);
        
        /**
        * @brief Prints char as hexadecimal value with a 0x prefix.
        * @param value The char to be printed.
        */
        void printHex(unsigned char value);

        /**
        * @brief Fills the buffer with data from the serial port.
        * @param saveToFile Indicates if image data should be saved to file.
        * @return 0.
        */
        int fillBuffer(bool saveToFile = false);

        /**
        * @brief Gets the distance data from serial port and print it to std out.
        * @return 0 if successful, 1 if error occurred.
        */
        int getDistance();

        /**
        * @brief Sends the buffer to the Displayer for rendering.
        * @param printRaw Bool that indicates weather received byte data should be printed to std::out (for debug).
        */
        void bufferToDisplay(bool printRaw = false);

        /**
        * @brief Initializes textures in Displayer to default values.
        */
        void initTextures();

        /**
        * @brief Finds a sequence of two characters in the given string.
        * @param[in] str Pointer to the input string.
        * @param ch1 First character of the sequence to find.
        * @param ch2 Second character of the sequence to find.
        * @return Index of the first occurrence of the sequence, or -1 if not found.
        */
        int findSequence(unsigned char (*str)[CDC_FRAME_SIZE], unsigned char ch1, unsigned char ch2);
        
        /**
        * @brief Find index of bytes signalizing start of image.
        * @param[in] str Pointer to the input string.
        * @return Index of the start of the data sequence, or -1 if not found.
        */
        int findStart(unsigned char (*str)[CDC_FRAME_SIZE]);

        /**
        * @brief Find index of bytes signalizing end of image.
        * @param[in] str Pointer to the input string.
        * @return Index of the start of the data sequence, or -1 if not found.
        */
        int findEnd(unsigned char (*str)[CDC_FRAME_SIZE]);

        /**
        * @brief Test function to simulate reading from serial port.
        * @param[in] str Pointer to the string buffer.
        * @return 0.
        */
        int simulateRead(unsigned char (*character)[CDC_FRAME_SIZE]);
};