#include <iostream>
#include <string>
#include <vector>
#include <getopt.h>
#include <QCoreApplication>
#include <QEventLoop>
#include "livestacker.h"
#include "io/logger.h"
#include "io/timer.h"
#include "simd/simd_ops.h"

void printHelp(const char* appName)
{
    std::cout << "Usage: " << appName << " [options] input_file\n";
    std::cout << "\n";
    std::cout << "Options:\n";
    std::cout << "  -h, --help                 Show this help message\n";
    std::cout << "  -o, --output FILE          Output file (required)\n";
    std::cout << "  -r, --reference FILE       Reference frame (defaults to first input file)\n";
    std::cout << "  -m, --method METHOD        Stacking method: mean, median, sigmaclip (default: mean)\n";
    std::cout << "  -b, --background PERCENT   Background adjustment percentage (0-100, default: 15)\n";
    std::cout << "  -c, --contrast PERCENT     Contrast adjustment percentage (10-200, default: 120)\n";
    std::cout << "  -t, --saturation PERCENT   Saturation adjustment percentage (0-100, default: 120)\n";
    std::cout << "  -p, --compression PERCENT  Compression percentage (0-100, default: 20)\n";
    std::cout << "  -f, --median-filter SIZE   Median filter size (0 to disable, default: 0)\n";
    std::cout << "  -g, --sigma-clip FRAMES,LOWER,UPPER  Sigma clipping parameters (default: disabled)\n";
    std::cout << "  -e, --state FILE           State file for processing (required)\n";
    std::cout << "  -s, --save-state FILE      Save current state to file\n";
    std::cout << "  -l, --load-state FILE      Load state from file\n";
    std::cout << "  -x, --reset                Reset the internal state\n";
    std::cout << "  -q, --quality THRESHOLD    Frame quality threshold (0.0-1.0, default: 0.7)\n";
    std::cout << "  -i, --diagnostic PREFIX    Enable diagnostic mode with output prefix\n";
    std::cout << "  -P, --poly-degree DEGREE   Polynomial degree for background modeling (1-4, default: 2)\n";
    std::cout << "  -G, --grid-size SIZE       Sample grid size for background extraction (8-64, default: 16)\n";
    std::cout << "  -T, --protection THRESHOLD Protection threshold for nebulae (0.0-1.0, defaulremot: 0.5)\n";
    std::cout << "  -S, --smoothness FACTOR    Smoothness factor for background model (0.0-1.0, default: 0.5)\n";
    std::cout << "  -L, --local-hist-eq        Enable local histogram equalization\n";
    std::cout << "  -W, --hist-eq-window SIZE  Window size for local histogram equalization (default: 63)\n";
    std::cout << "  -C, --hist-eq-clip LIMIT   Clip limit for local histogram equalization (0.0-4.0, default: 1.0)\n";
    std::cout << "  -D, --denoise METHOD       Enable denoising (0=None, 1=NonLocalMeans, 2=Bilateral)\n";
    std::cout << "  -N, --denoise-strength STR Denoising strength (1-30, default: 10)\n";
    std::cout << "  -v, --verbose              Enable verbose output\n";
    std::cout << "  -k, --benchmark            Enable benchmark mode to time operations\n";
    std::cout << "  -z, --disable-simd         Disable SIMD acceleration\n";
    std::cout << "\n";
    std::cout << "Examples:\n";
    std::cout << "  " << appName << " -o output.jpg -e state.jpg -m median input.jpg\n";
    std::cout << "  " << appName << " -o output.jpg -e state.jpg input.jpg\n";
    std::cout << "  " << appName << " -o output2.jpg -e state.jpg input2.jpg\n";
    std::cout << "  " << appName << " -o output.jpg -e state.jpg -P 2 -G 16 -T 0.5 -S 0.5 input.jpg\n";
    std::cout << "  " << appName << " -o output.jpg -e state.jpg -L -W 63 -C 1.0 input.jpg\n";
}

int main(int argc, char* argv[])
{
    // Create QCoreApplication instance (non-GUI)
    QCoreApplication app(argc, argv);

    // Define long options
    static struct option longOptions[] =
    {
        {"help",           no_argument,       nullptr, 'h'},
        {"output",         required_argument, nullptr, 'o'},
        {"reference",      required_argument, nullptr, 'r'},
        {"method",         required_argument, nullptr, 'm'},
        {"background",     required_argument, nullptr, 'b'},
        {"contrast",       required_argument, nullptr, 'c'},
        {"saturation",     required_argument, nullptr, 't'},
        {"compression",    required_argument, nullptr, 'p'},
        {"median-filter",  required_argument, nullptr, 'f'},
        {"sigma-clip",     required_argument, nullptr, 'g'},
        {"state",          required_argument, nullptr, 'e'},
        {"save-state",     required_argument, nullptr, 's'},
        {"load-state",     required_argument, nullptr, 'l'},
        {"reset",          no_argument,       nullptr, 'x'},
        {"quality",        required_argument, nullptr, 'q'},
        {"diagnostic",     required_argument, nullptr, 'i'},
        {"poly-degree",    required_argument, nullptr, 'P'},
        {"grid-size",      required_argument, nullptr, 'G'},
        {"protection",     required_argument, nullptr, 'T'},
        {"smoothness",     required_argument, nullptr, 'S'},
        {"local-hist-eq",  no_argument,       nullptr, 'L'},
        {"hist-eq-window", required_argument, nullptr, 'W'},
        {"hist-eq-clip",   required_argument, nullptr, 'C'},
        {"denoise",        required_argument, nullptr, 'D'},
        {"denoise-strength", required_argument, nullptr, 'N'},
        {"verbose",        no_argument,       nullptr, 'v'},
        {"benchmark",      no_argument,       nullptr, 'k'},
        {"disable-simd",   no_argument,       nullptr, 'z'},
        {nullptr,          0,                 nullptr, 0}
    };

    // Create LiveStacker instance
    LiveStacker stacker;

    // Default values
    std::string outputFile;
    std::string referenceFile;
    std::string stateFile;
    std::string saveStateFile;
    std::string loadStateFile;
    bool resetState = false;
    std::string stackingMethod = "mean";
    float backgroundAdjustment = 15.0f;
    float contrastAdjustment = 120.0f;
    float saturationAdjustment = 120.0f;
    float compressionPercentage = 20.0f;
    int medianFilterSize = 0;
    int sigmaClippingFrames = 0;
    float sigmaClippingLower = 3.0f;
    float sigmaClippingUpper = 3.0f;
    float qualityThreshold = 0.5f;
    bool diagnosticMode = false;
    std::string diagnosticPrefix = "frame_";
    bool localHistEqEnabled = false;
    int localHistEqWindowSize = 63;
    float localHistEqClipLimit = 1.0f;
    int denoisingMethod = 0;
    int denoisingStrength = 10;
    bool denoisingEnabled = false;
    bool verboseMode = false;
    bool benchmarkMode = false;

    // Track which parameters were explicitly set on the command line
    bool backgroundAdjustmentSet = false;
    bool contrastAdjustmentSet = false;
    bool saturationAdjustmentSet = false;
    bool compressionPercentageSet = false;
    bool medianFilterSizeSet = false;
    bool denoisingSet = false;

    // Parse options
    int opt;
    int optionIndex = 0;
    while ((opt = getopt_long(argc, argv, "ho:r:m:b:c:t:p:f:g:e:s:l:xq:i:P:G:T:S:LW:C:D:N:vkz", longOptions,
                              &optionIndex)) != -1)
    {
        switch (opt)
        {
            case 'h':
                printHelp(argv[0]);
                return 0;
            case 'o':
                outputFile = optarg;
                break;
            case 'r':
                referenceFile = optarg;
                break;
            case 'm':
                stackingMethod = optarg;
                break;
            case 'b':
                backgroundAdjustment = std::stof(optarg);
                backgroundAdjustmentSet = true;
                break;
            case 'c':
                contrastAdjustment = std::stof(optarg);
                contrastAdjustmentSet = true;
                break;
            case 't':
                saturationAdjustment = std::stof(optarg);
                saturationAdjustmentSet = true;
                break;
            case 'p':
                compressionPercentage = std::stof(optarg);
                compressionPercentageSet = true;
                break;
            case 'f':
                medianFilterSize = std::stoi(optarg);
                medianFilterSizeSet = true;
                break;
            case 'g':
            {
                // Parse sigma clipping parameters (format: frames,lower,upper)
                std::string params = optarg;
                size_t pos1 = params.find(',');
                size_t pos2 = params.find(',', pos1 + 1);
                if (pos1 != std::string::npos && pos2 != std::string::npos)
                {
                    sigmaClippingFrames = std::stoi(params.substr(0, pos1));
                    sigmaClippingLower = std::stof(params.substr(pos1 + 1, pos2 - pos1 - 1));
                    sigmaClippingUpper = std::stof(params.substr(pos2 + 1));
                }
                else
                {
                    LOG_ERROR("Invalid sigma clipping parameters. Format: frames,lower,upper");
                    return 1;
                }
                break;
            }
            case 'e':
                stateFile = optarg;
                break;
            case 's':
                saveStateFile = optarg;
                break;
            case 'l':
                loadStateFile = optarg;
                break;
            case 'x':
                resetState = true;
                break;
            case 'q':
                stacker.setQualityThreshold(std::stof(optarg));
                break;
            case 'i':
                stacker.setDiagnosticMode(true, optarg);
                break;
            case 'P':
                stacker.setBackgroundPolynomialDegree(std::stoi(optarg));
                break;
            case 'G':
                stacker.setBackgroundSampleGridSize(std::stoi(optarg));
                break;
            case 'T':
                stacker.setBackgroundProtectionThreshold(std::stof(optarg));
                break;
            case 'S':
                stacker.setBackgroundSmoothnessFactor(std::stof(optarg));
                break;
            case 'L':
                localHistEqEnabled = true;
                break;
            case 'W':
                localHistEqEnabled = true;
                localHistEqWindowSize = std::stoi(optarg);
                break;
            case 'C':
                localHistEqEnabled = true;
                localHistEqClipLimit = std::stof(optarg);
                break;
            case 'D':
                denoisingMethod = std::stoi(optarg);
                denoisingEnabled = true;
                denoisingSet = true;
                break;
            case 'N':
                denoisingStrength = std::stoi(optarg);
                denoisingSet = true;
                break;
            case 'v':
                // Set verbose mode flag
                verboseMode = true;
                LOG_INFO("Verbose mode enabled");
                Logger::getInstance().setVerboseMode(true);
                break;
            case 'k':
                // Set benchmark mode flag
                benchmarkMode = true;
                stacker.setBenchmarkMode(benchmarkMode);
                LOG_INFO("Benchmark mode enabled");
                Timer::setEnabled(true);
                break;
            case 'z':
                // Disable SIMD acceleration
                simd::enable_simd(false);
                LOG_INFO("SIMD acceleration disabled");
                break;
            default:
                printHelp(argv[0]);
                return 1;
        }
    }

    // Check required parameters
    if (outputFile.empty())
    {
        LOG_ERROR("Error: Output file is required");
        printHelp(argv[0]);
        return 1;
    }

    if (stateFile.empty())
    {
        LOG_ERROR("Error: State file is required (-e option)");
        printHelp(argv[0]);
        return 1;
    }

    // Collect input file
    std::string inputFile;
    if (optind < argc)
    {
        inputFile = argv[optind];
    }
    else
    {
        LOG_ERROR("Error: No input file specified");
        printHelp(argv[0]);
        return 1;
    }

    // If no reference frame is specified, use the input file
    if (referenceFile.empty())
    {
        referenceFile = inputFile;
    }

    // Reset state if requested
    if (resetState)
    {
        stacker.reset();
        LOG_INFO("Internal state reset");
    }

    // Load state if requested
    if (!loadStateFile.empty())
    {
        LOG_INFO("Loading state from " + loadStateFile + "...");
        if (!stacker.loadState(loadStateFile))
        {
            LOG_ERROR("Error: Failed to load state: " + stacker.getLastError());
            return 1;
        }
        LOG_INFO("State loaded successfully");
    }

    // Set stacking method
    if (stackingMethod == "mean")
    {
        stacker.setStackingMethod(LiveStacker::StackingMethod::Mean);
    }
    else if (stackingMethod == "median" || stackingMethod == "sigmaclip")
    {
        // Check if we're in stateful operation (state file is specified)
        if (!stateFile.empty())
        {
            LOG_WARN("Median and sigma clipping methods are not supported in stateful operation.");
            LOG_WARN("Falling back to mean stacking method.");
            stacker.setStackingMethod(LiveStacker::StackingMethod::Mean);
        }
        else
        {
            // Not in stateful operation, so we can use the requested method
            if (stackingMethod == "median")
            {
                stacker.setStackingMethod(LiveStacker::StackingMethod::Median);
            }
            else // sigmaclip
            {
                stacker.setStackingMethod(LiveStacker::StackingMethod::SigmaClip);
                if (sigmaClippingFrames > 0)
                {
                    stacker.setSigmaClippingParams(sigmaClippingFrames, sigmaClippingLower, sigmaClippingUpper);
                }
            }
        }
    }
    else
    {
        LOG_ERROR("Error: Unknown stacking method: " + stackingMethod);
        return 1;
    }

    // We're already tracking which parameters were explicitly set during the first parsing

    // Only set parameters that were explicitly specified on the command line
    if (backgroundAdjustmentSet)
    {
        stacker.setBackgroundAdjustment(backgroundAdjustment);
    }

    if (contrastAdjustmentSet)
    {
        stacker.setContrastAdjustment(contrastAdjustment);
    }

    if (saturationAdjustmentSet)
    {
        stacker.setSaturationAdjustment(saturationAdjustment);
    }

    if (compressionPercentageSet)
    {
        stacker.setCompressionPercentage(compressionPercentage);
    }

    if (medianFilterSizeSet)
    {
        stacker.setMedianFilterSize(medianFilterSize);
    }

    // Set local histogram equalization parameters if enabled
    if (localHistEqEnabled)
    {
        stacker.setLocalHistogramEqualization(true, localHistEqWindowSize, localHistEqClipLimit);
    }

    // Set denoising parameters if enabled
    if (denoisingEnabled)
    {
        stacker.setDenoising(denoisingMethod, denoisingStrength);
    }

    // Reference frame is now handled internally in processFrame

    // Try to load the state if it exists
    if (stacker.loadState(stateFile))
    {
        LOG_INFO("Loaded existing state from " + stateFile);
    }
    else
    {
        LOG_INFO("No existing state found, starting fresh");
    }

    LOG_INFO("Processing frame " + inputFile + "...");
    if (!stacker.processFrame(inputFile, outputFile))
    {
        LOG_ERROR("Error: Failed to process frame: " + stacker.getLastError());
        return 1;
    }

    // Save state
    if (!stacker.saveState(stateFile))
    {
        LOG_ERROR("Error: Failed to save state: " + stacker.getLastError());
        return 1;
    }
    LOG_INFO("State saved to " + stateFile);

    // Save state if requested
    if (!saveStateFile.empty())
    {
        LOG_INFO("Saving state to " + saveStateFile + "...");
        if (!stacker.saveState(saveStateFile))
        {
            LOG_ERROR("Error: Failed to save state: " + stacker.getLastError());
            return 1;
        }
        LOG_INFO("State saved successfully");
    }

    LOG_INFO("Successfully saved stacked image to " + outputFile);

    // Process Qt events using QEventLoop
    QEventLoop eventLoop;
    eventLoop.processEvents();

    // Log total execution time if benchmark mode is enabled
    if (benchmarkMode)
    {
        Timer::logTotalExecutionTime();
    }

    return 0;
}
