#include <fstream>
#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <algorithm> // For std::transform
#include <iomanip>

#include <k230_vdec.h>

// Size of the read buffer for each read operation
const size_t READ_BUFFER_SIZE = 40960;

std::atomic<bool> g_decoder_running(true);

// Helper function to get file extension (lowercase)
static std::string get_file_extension(const std::string& file_path) {
    size_t dot_pos = file_path.find_last_of('.');
    if (dot_pos == std::string::npos || dot_pos == file_path.length() - 1) {
        return ""; // No extension
    }
    std::string ext = file_path.substr(dot_pos + 1);
    std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); // Convert to lowercase
    return ext;
}

// Helper function to determine decoder type from file extension
static DecoderType get_decoder_type_from_extension(const std::string& ext) {
    if (ext == "h264" || ext == "264") {
        return VDEC_H264;
    } else if (ext == "h265" || ext == "265" || ext == "hevc") {
        return VDEC_H265; // Assume VDEC_H265 is defined for H.265/HEVC
    } else if (ext == "mjpeg" || ext == "mjpg") {
        return VDEC_JPEG; // Assume VDEC_MPEG is defined for MPEG
    }
    return VDEC_H264;
}

// Decoding frame processing thread function
static void frame_processing_thread(VideoDecoder* decoder)
{
    if (!decoder) {
        std::cerr << "Error: Invalid decoder pointer in processing thread" << std::endl;
        return;
    }

    int frame_count = 0;
    while (g_decoder_running) {

        // Continuously get decoded frames until there are no more frames
        k_video_frame_info frame;
        int ret;
        do {
            ret = decoder->get_frame(frame, 100);
            if (ret == 0) {
                frame_count++;
                std::cout << "[" << frame_count << "]:"
                        << "Obtained decoded frame - Width: " << frame.v_frame.width
                        << ", Height: " << frame.v_frame.height
                        << ", Format: " << frame.v_frame.pixel_format
                        << ", phy[0]: 0x" << std::hex <<  frame.v_frame.phys_addr[0]
                        << ", phy[1]: 0x" << std::hex << frame.v_frame.phys_addr[1]
                        << std::dec << std::endl;

                // Release frame buffer
                if (decoder->release_frame(frame) != 0) {
                    std::cerr << "Warning: Failed to release frame buffer" << std::endl;
                }
            }
        } while (ret == 0);
    }

    std::cout << "Frame processing thread exited" << std::endl;
}

// Modified function: Automatically determine decoder type by file extension
static int file_video_decoder(const char* file_path) {
    int ret = -1;

    if (!file_path) {
        std::cerr << "Error: File path is empty" << std::endl;
        return -1;
    }

    // Get file extension and determine decoder type
    std::string ext = get_file_extension(file_path);
    DecoderType dec_type = get_decoder_type_from_extension(ext);

    if (dec_type == VDEC_UNKNOWN) {
        std::cerr << "Error: Unsupported file format (extension: ." << ext << ")" << std::endl;
        return -1;
    }
    std::cout << "Detected file format: ." << ext << ", using decoder type: " << dec_type << std::endl;

    try {
        // Configure decoder parameters
        DecoderConfig config(dec_type);

        // Create decoder instance
        VideoDecoder decoder(config);

        // Initialize decoder
        if (decoder.create() != 0) {
            std::cerr << "Error: Failed to create decoder" << std::endl;
            return -1;
        }

        // Start decoder
        if (decoder.start() != 0) {
            std::cerr << "Error: Failed to start decoder" << std::endl;
            decoder.destroy();
            return -1;
        }

        // Create and start frame processing thread
        std::thread processing_thread(frame_processing_thread, &decoder);

        // Open file
        std::ifstream file(file_path, std::ios::binary);
        if (!file.is_open()) {
            std::cerr << "Error: Unable to open file: " << file_path << std::endl;
            // Notify thread to exit
            g_decoder_running = false;
            processing_thread.join();
            decoder.stop();
            decoder.destroy();
            return -1;
        }

        // Read file and send data to decoder (main thread)
        std::vector<uint8_t> read_buffer(READ_BUFFER_SIZE);
        size_t total_read = 0;

        while (file) {
            file.read(reinterpret_cast<char*>(read_buffer.data()), READ_BUFFER_SIZE);
            size_t bytes_read = file.gcount();

            if (bytes_read == 0) {
                break;
            }

            total_read += bytes_read;
            std::cout << "Read data: " << bytes_read << " bytes, total: " << total_read << " bytes" << std::endl;

            // Send data to decoder
            ret = decoder.send_stream(read_buffer.data(), bytes_read);
            if (ret != 0) {
                std::cerr << "Error: Failed to send stream data to decoder" << std::endl;
                break;
            }

        }

        std::cout << "File reading completed, total size: " << total_read << " bytes" << std::endl;

        // End of decoding
        g_decoder_running = false;

        // Wait for processing thread to complete
        if (processing_thread.joinable()) {
            processing_thread.join();
        }

        // Stop and destroy decoder
        decoder.stop();
        decoder.destroy();

        // Reset global variable
        g_decoder_running = true;

        return 0; // Success

    } catch (const std::exception& e) {
        std::cerr << "Exception occurred: " << e.what() << std::endl;
        g_decoder_running = false;
        return -1;
    } catch (...) {
        std::cerr << "Unknown exception occurred" << std::endl;
        g_decoder_running = false;
        return -1;
    }
}

void setup()
{
    file_video_decoder("/data/test.264");
}

void loop()
{

}