#pragma once

#include <map>
#include <mutex>
#include "image/image.h"
#include "image/io.h"
#include "image/j2k_utils.h"
#include "common/ccsds/ccsds.h"
#include "common/ccsds/ccsds_time.h"
#include "common/utils.h"
#include "imgui/imgui_image.h"
#include <filesystem>
#include "utils/time.h"

namespace orb
{
    class ImageParser
    {
    public:
        struct ImgStc
        {
            image::Image img;
            uint32_t textureID = 0;
            uint32_t *textureBuffer;
            int imgsize = 0;
            bool hasToUpdate = false;
            bool is_dling = false;
        }; // TODO, handle changing image sizes!

        std::map<int, ImgStc> decoded_imgs;
        time_t last_timestamp = 0;
        std::mutex savingMutex;

    public:
        std::string directory;

    public:
        ImageParser()
        {
        }

        ~ImageParser()
        {
        }

        void work(ccsds::CCSDSPacket &pkt)
        {
            if (pkt.payload.size() < 18)
            {
                if (pkt.payload.size() == 10) // END?
                {
                    if (pkt.payload[9] == 0xFF)
                    {
                        int channel = pkt.payload[8];
                        saveAll(channel);
                    }
                }

                return;
            }

            image::Image imgt = image::decompress_j2k_openjp2(&pkt.payload[17], pkt.payload.size() - 17);

            uint64_t timestamp = (uint64_t)pkt.payload[0] << 56 |
                                 (uint64_t)pkt.payload[1] << 48 |
                                 (uint64_t)pkt.payload[2] << 40 |
                                 (uint64_t)pkt.payload[3] << 32 |
                                 (uint64_t)pkt.payload[4] << 24 |
                                 (uint64_t)pkt.payload[5] << 16 |
                                 (uint64_t)pkt.payload[6] << 8 |
                                 (uint64_t)pkt.payload[7]; // TODO use CCSDS
            int channel = pkt.payload[8];
            int width = pkt.payload[9] << 8 | pkt.payload[10];
            int height = pkt.payload[11] << 8 | pkt.payload[12];
            int x_s = pkt.payload[13] << 8 | pkt.payload[14];
            int y_s = pkt.payload[15] << 8 | pkt.payload[16];

            image::Image img = imgt.to8bits();

            for (size_t i = 0; i < img.size(); i++)
                img.set(i, imgt.get(i));

            if (decoded_imgs.count(channel) == 0)
                decoded_imgs.emplace(channel, ImgStc());

            if ((uint64_t)last_timestamp != timestamp || decoded_imgs[channel].img.size() == 0)
            {
                if (decoded_imgs[channel].img.size() != 0)
                    saveAll();

                // logger->critical("%d %d %d", width, height, channel);
                decoded_imgs[channel].img.init(8, width, height, 1);
                last_timestamp = timestamp;
            }

            decoded_imgs[channel].img.draw_image(0, img, x_s, y_s);
            decoded_imgs[channel].is_dling = true;

            if (decoded_imgs[channel].textureID != 0)
            {
                auto imageScaled = decoded_imgs[channel].img;
                imageScaled.resize(1000, 1000);
                image::image_to_rgba(imageScaled, decoded_imgs[channel].textureBuffer);
                decoded_imgs[channel].hasToUpdate = true;
            }
        }

        void saveAll(int channel = -1)
        {
            std::string dirpath = directory + "/" +
                                  satdump::timestamp_to_string(last_timestamp); //+std::to_string(last_timestamp);

            if (!std::filesystem::exists(dirpath))
                std::filesystem::create_directories(dirpath);

            if (channel == -1)
            {
                for (auto &ch : decoded_imgs)
                {
                    std::string path = dirpath +
                                       "/" +
                                       std::to_string(ch.first);

                    if (decoded_imgs[ch.first].is_dling)
                        image::save_img(decoded_imgs[ch.first].img, path);
                    decoded_imgs[ch.first].is_dling = false;
                }
            }
            else if (decoded_imgs.count(channel) > 0)
            {
                std::string path = dirpath +
                                   "/" +
                                   std::to_string(channel);

                image::save_img(decoded_imgs[channel].img, path);
                decoded_imgs[channel].is_dling = false;
                decoded_imgs[channel].img.fill(0);
            }
        }
    };
}
