#ifndef STACKER_BASE_TPP
#define STACKER_BASE_TPP

template <typename T>
Stacker<T>::Stacker()
    : m_settings(StackerSettings::create())
{
}

template <typename T>
Stacker<T>::~Stacker() = default;

template <typename T>
void Stacker<T>::setReferenceFrame(const std::shared_ptr<ImageBuffer<T>> &referenceFrame)
{
    m_referenceFrame = referenceFrame;
}

template <typename T>
void Stacker<T>::addFrame(const std::shared_ptr<ImageBuffer<T>> &frame)
{
    m_frames.push_back(frame);
}

template <typename T>
void Stacker<T>::setDarkFrame(const std::shared_ptr<ImageBuffer<T>> &darkFrame)
{
    m_darkFrame = darkFrame;
}

template <typename T>
void Stacker<T>::setAlignFrames(bool align)
{
    m_settings->setAlignFrames(align);
}

template <typename T>
void Stacker<T>::setMaxAlignmentStars(int maxStars)
{
    m_settings->setMaxAlignmentStars(maxStars);
}

template <typename T>
void Stacker<T>::setBackgroundAdjustment(float percentage)
{
    m_settings->setBackgroundAdjustment(percentage);
}

template <typename T>
void Stacker<T>::setContrastAdjustment(float percentage)
{
    m_settings->setContrastAdjustment(percentage);
}

template <typename T>
void Stacker<T>::setSaturationAdjustment(float percentage)
{
    m_settings->setSaturationAdjustment(percentage);
}

template <typename T>
void Stacker<T>::setCompressionPercentage(float percentage)
{
    m_settings->setCompressionPercentage(percentage);
}

template <typename T>
void Stacker<T>::setMedianFilterSize(int size)
{
    m_settings->setMedianFilterSize(size);
}

template <typename T>
void Stacker<T>::setLocalHistogramEqualizationParams(int windowSize, float clipLimit)
{
    m_settings->setLocalHistogramEqualizationParams(windowSize, clipLimit);
}

template <typename T>
void Stacker<T>::setDenoising(StackerSettings::DenoisingMethod method, int strength)
{
    m_settings->setDenoising(method, strength);
}

template <typename T>
void Stacker<T>::setSigmaClippingParams(int frames, float lower, float upper)
{
    m_settings->setSigmaClippingParams(frames, lower, upper);
}

template <typename T>
void Stacker<T>::setDiagnosticMode(bool enable, const std::string &outputPrefix)
{
    m_settings->setDiagnosticMode(enable, outputPrefix);
}

template <typename T>
void Stacker<T>::setQualityThreshold(float threshold)
{
    m_settings->setQualityThreshold(threshold);
}

template <typename T>
void Stacker<T>::setBenchmarkMode(bool enable)
{
    m_settings->setBenchmarkMode(enable);
}

// Original calculateFrameQuality implementation has been replaced by the optimized version in stacker_optimized_quality.tpp
// The optimized version uses SIMD operations and sampling for better performance
//
// template <typename T>
// float Stacker<T>::calculateFrameQuality(const std::shared_ptr<ImageBuffer<T>> &frame)
// {
//     // Implementation moved to stacker_optimized_quality.tpp
// }

template <typename T>
void Stacker<T>::saveIntermediateResult(const std::shared_ptr<ImageBuffer<T>> &result, int frameIndex)
{
    if (!m_settings->getDiagnosticMode())
    {
        return;
    }

    // Create a copy for post-processing
    auto processedResult = applyPostProcessing(result);

    // Save the result
    std::string filename = m_settings->getOutputPrefix() + std::to_string(frameIndex) + ".jpg";

    // Convert to 8-bit if necessary
    std::shared_ptr<ImageBuffer<uint8_t>> result8bit;
    if (std::is_same<T, uint8_t>::value)
    {
        result8bit = std::static_pointer_cast<ImageBuffer<uint8_t>>(processedResult);
    }
    else
    {
        result8bit = processedResult->template convertTo<uint8_t>();
    }

    // Save the image
    LOG_INFO("Saving intermediate result to " + filename);

    // Use the ImageLoader class to save the image
    try
    {
        ImageLoader::saveImage(filename, result8bit);
    }
    catch (const std::exception &e)
    {
        LOG_ERROR("Error saving intermediate result: " + std::string(e.what()));
    }
}

template <typename T>
std::shared_ptr<ImageBuffer<T>> Stacker<T>::applyDarkSubtraction(const std::shared_ptr<ImageBuffer<T>> &frame)
{
    Timer timer("applyDarkSubtraction");

    if (!m_darkFrame)
    {
        return frame;
    }

    // Check if dimensions match
    if (frame->getWidth() != m_darkFrame->getWidth() ||
            frame->getHeight() != m_darkFrame->getHeight() ||
            frame->getChannels() != m_darkFrame->getChannels())
    {
        throw std::runtime_error("Dark frame dimensions do not match frame dimensions");
    }

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

    // Check if we can use direct memory access for better performance
    if (result->isContiguous() && m_darkFrame->isContiguous())
    {
        // Get pointers to the data
        T* resultData = result->getData();
        const T* darkData = m_darkFrame->getData();

        // Calculate the total number of pixels
        size_t totalPixels = result->getWidth() * result->getHeight() * result->getChannels();

        // Process all pixels in a single loop with OpenMP parallelization
#ifdef HAVE_OPENMP
        #pragma omp parallel for schedule(static)
#endif
        for (size_t i = 0; i < totalPixels; ++i)
        {
            // Avoid underflow
            resultData[i] = (darkData[i] > resultData[i]) ? 0 : resultData[i] - darkData[i];
        }
    }
    else
    {
        // Fallback to pixel-by-pixel processing with OpenMP parallelization
#ifdef HAVE_OPENMP
        #pragma omp parallel for collapse(2) schedule(dynamic, 16)
#endif
        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 frameValue = result->getPixel(x, y, c);
                    T darkValue = m_darkFrame->getPixel(x, y, c);

                    // Avoid underflow
                    T newValue = (darkValue > frameValue) ? 0 : frameValue - darkValue;
                    result->setPixel(x, y, newValue, c);
                }
            }
        }
    }

    return result;
}

#endif // STACKER_BASE_TPP
