#ifndef STACKER_CONTRAST_ADJUSTMENT_TPP
#define STACKER_CONTRAST_ADJUSTMENT_TPP

#include "../io/timer.h"
#include "../io/logger.h"
#include <memory>
#include <vector>
#include <algorithm>

template <typename T>
std::shared_ptr<ImageBuffer<T>> Stacker<T>::applyContrastAdjustment(const std::shared_ptr<ImageBuffer<T>> &frame,
                             float percentage)
{
    TIME_FUNCTION("applyContrastAdjustment");

    // Create a copy of the frame
    auto result = frame->clone();

    // Get max possible value based on data type
    T maxPossibleValue = std::is_same<T, uint8_t>::value ? 255 : 65535;

    // For astronomical images, we'll use a percentile-based approach
    // This avoids outliers (very bright stars) from skewing the contrast calculation

    // First, collect histogram data for each channel
    const int numBins = 256; // For histogram calculation
    std::vector<std::vector<int>> histograms(result->getChannels(), std::vector<int>(numBins, 0));
    std::vector<float> binSize(result->getChannels(), static_cast<float>(maxPossibleValue) / numBins);

    // Build histograms
    for (int y = 0; y < result->getHeight(); ++y)
    {
        for (int x = 0; x < result->getWidth(); ++x)
        {
            for (int c = 0; c < result->getChannels(); ++c)
            {
                T value = result->getPixel(x, y, c);
                int bin = std::min(numBins - 1, static_cast<int>(value / binSize[c]));
                histograms[c][bin]++;
            }
        }
    }

    // Calculate percentiles for each channel
    // For astronomical images, 15th and 85th percentiles work well
    const float lowerPercentile = 15.0f; // 15th percentile
    const float upperPercentile = 85.0f; // 85th percentile

    std::vector<float> lowValues(result->getChannels(), 0);
    std::vector<float> highValues(result->getChannels(), 0);

    for (int c = 0; c < result->getChannels(); ++c)
    {
        int totalPixels = result->getWidth() * result->getHeight();
        int lowerCount = static_cast<int>(totalPixels * lowerPercentile / 100.0f);
        int upperCount = static_cast<int>(totalPixels * upperPercentile / 100.0f);

        int count = 0;
        for (int i = 0; i < numBins; ++i)
        {
            count += histograms[c][i];
            if (count >= lowerCount && lowValues[c] == 0)
            {
                lowValues[c] = i * binSize[c];
            }
            if (count >= upperCount && highValues[c] == 0)
            {
                highValues[c] = i * binSize[c];
                break;
            }
        }
    }

    // Apply contrast adjustment with star protection
    float factor = percentage / 100.0f;

    // For very low contrast values, we'll use a gentler approach
    // This prevents the image from becoming completely gray at low contrast settings
    if (factor < 0.2f)
    {
        factor = 0.2f + (factor - 0.1f) * 0.5f; // Soften the effect for very low values
    }

    for (int y = 0; y < result->getHeight(); ++y)
    {
        for (int x = 0; x < result->getWidth(); ++x)
        {
            for (int c = 0; c < result->getChannels(); ++c)
            {
                T value = result->getPixel(x, y, c);
                float normalizedValue = (value - lowValues[c]) / (highValues[c] - lowValues[c]);

                // Apply contrast adjustment
                float midpoint = 0.5f;
                float adjustedValue = midpoint + (normalizedValue - midpoint) * factor;

                // Star protection: reduce contrast effect for very bright pixels
                // This prevents star bloating and preserves detail in bright areas
                if (value > highValues[c])
                {
                    // For values above the upper percentile, blend between adjusted and original
                    float blendFactor = std::min(1.0f, (value - highValues[c]) / (maxPossibleValue - highValues[c]));
                    float starProtectedValue = adjustedValue * (1.0f - blendFactor) + normalizedValue * blendFactor;
                    adjustedValue = starProtectedValue;
                }

                // Convert back to original range and clamp
                float newValue = adjustedValue * (highValues[c] - lowValues[c]) + lowValues[c];
                newValue = std::max(0.0f, std::min(static_cast<float>(maxPossibleValue), newValue));

                result->setPixel(x, y, static_cast<T>(newValue), c);
            }
        }
    }

    return result;
}

#endif // STACKER_CONTRAST_ADJUSTMENT_TPP
