#pragma once

#include "libs/others/strptime.h"
#include <cstdint>
#include <ctime>
#include <map>
#include <string>
#include <vector>

namespace satdump
{
    namespace xrit
    {
        ////////////////////////////////////////
        struct PrimaryHeader
        {
            static constexpr int TYPE = 0;

            uint8_t type;
            uint16_t record_length;
            uint8_t file_type_code;
            uint32_t total_header_length;
            uint64_t data_field_length;

            PrimaryHeader(uint8_t *data)
            {
                type = data[0];
                record_length = data[1] << 8 | data[2];
                file_type_code = data[3];
                total_header_length = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
                data_field_length = (uint64_t)data[8] << 56 | (uint64_t)data[9] << 48 | (uint64_t)data[10] << 40 | (uint64_t)data[11] << 32 | (uint64_t)data[12] << 24 | (uint64_t)data[13] << 16 |
                                    (uint64_t)data[14] << 8 | (uint64_t)data[15];
            }
        };

        struct ImageStructureRecord
        {
            static constexpr int TYPE = 1;

            uint8_t type;
            uint16_t record_length;
            uint8_t bit_per_pixel;
            uint16_t columns_count;
            uint16_t lines_count;
            uint8_t compression_flag;

            ImageStructureRecord(uint8_t *data)
            {
                type = data[0];
                record_length = data[1] << 8 | data[2];
                bit_per_pixel = data[3];
                columns_count = data[4] << 8 | data[5];
                lines_count = data[6] << 8 | data[7];
                compression_flag = data[8];
            }
        };

        struct ImageNavigationRecord
        {
            static constexpr int TYPE = 2;

            uint8_t type;
            uint16_t record_length;
            std::string projection_name;
            int32_t column_scaling_factor;
            int32_t line_scaling_factor;
            double column_offset;
            double line_offset;

            // Pre-computed projection information
            double column_scalar;
            double line_scalar;

            ImageNavigationRecord(uint8_t *data)
            {
                type = data[0];
                record_length = data[1] << 8 | data[2];
                projection_name = std::string((char *)&data[3], (char *)&data[3 + 32]);
                column_scaling_factor = data[35] << 24 | data[36] << 16 | data[37] << 8 | data[38];
                line_scaling_factor = data[39] << 24 | data[40] << 16 | data[41] << 8 | data[42];
                column_offset = (int32_t)(data[43] << 24 | data[44] << 16 | data[45] << 8 | data[46]);
                line_offset = (int32_t)(data[47] << 24 | data[48] << 16 | data[49] << 8 | data[50]);
                column_scalar = line_scalar = 0.0;
            }
        };

        struct ImageDataFunctionRecord
        {
            static constexpr int TYPE = 3;

            uint8_t type;
            uint16_t record_length;
            std::string datas;

            ImageDataFunctionRecord(uint8_t *data)
            {
                type = data[0];
                record_length = data[1] << 8 | data[2];
                datas = std::string((char *)&data[3], (char *)&data[3 + record_length - 3]);
            }
        };

        struct AnnotationRecord
        {
            static constexpr int TYPE = 4;

            uint8_t type;
            uint16_t record_length;
            std::string annotation_text;

            AnnotationRecord(uint8_t *data)
            {
                type = data[0];
                record_length = data[1] << 8 | data[2];
                annotation_text.insert(annotation_text.end(), &data[3], &data[record_length]);
            }
        };

        struct TimeStampRecord
        {
            static constexpr int TYPE = 5;

            uint8_t type;
            uint16_t record_length;
            uint16_t days;
            uint32_t milliseconds_of_day;

            time_t timestamp;

            TimeStampRecord(uint8_t *data)
            {
                type = data[0];
                record_length = data[1] << 8 | data[2];
                uint16_t days = data[3] << 8 | data[4];
                uint32_t milliseconds_of_day = data[5] << 24 | data[6] << 16 | data[7] << 8 | data[8];

                timestamp = (days - 4383) * 86400 + milliseconds_of_day;
            }
        };
        ////////////////////////////////////////

        struct XRITFile
        {
            int vcid = -1;
            int last_tracked_counter = -1;

            bool file_in_progress = false;
            bool header_parsed = false;

            std::map<int, int> custom_flags;

            std::string filename;
            int total_header_length;
            std::map<int, int> all_headers;
            std::vector<uint8_t> lrit_data;

            template <typename T>
            T getHeader()
            {
                if constexpr (std::is_same_v<T, PrimaryHeader>)
                    return PrimaryHeader(&lrit_data[0]);
                else
                    return T(&lrit_data[all_headers[T::TYPE]]);
            }

            template <typename T>
            bool hasHeader()
            {
                return all_headers.count(T::TYPE);
            }

            void parseHeaders(bool safe = false);
        };
    } // namespace xrit
} // namespace satdump