/*
 *  SPDX-FileCopyrightText: 2005 Adrian Page <adrian@pagenet.plus.com>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

#include "KoCompositeOp.h"

#include <KLocalizedString>
#include <KoID.h>
#include <QList>

#include "KoColorSpace.h"
#include "KoColorSpaceMaths.h"

QString KoCompositeOp::categoryColor()
{
    return i18n("Color");
}

QString KoCompositeOp::categoryArithmetic()
{
    return i18n("Arithmetic");
}
QString KoCompositeOp::categoryNegative()
{
    return i18n("Negative");
}
QString KoCompositeOp::categoryLight()
{
    return i18n("Lighten");
}
QString KoCompositeOp::categoryDark()
{
    return i18n("Darken");
}
QString KoCompositeOp::categoryHSY()
{
    return i18n("HSY");
}
QString KoCompositeOp::categoryHSI()
{
    return i18n("HSI");
}
QString KoCompositeOp::categoryHSL()
{
    return i18n("HSL");
}
QString KoCompositeOp::categoryHSV()
{
    return i18n("HSV");
}
QString KoCompositeOp::categoryMix()
{
    return i18n("Mix");
}
QString KoCompositeOp::categoryMisc()
{
    return i18n("Misc");
}

KoCompositeOp::ParameterInfo::ParameterInfo()
    : opacity(1.0f)
    , flow(1.0f)
    , lastOpacity(&opacity)
{
}

KoCompositeOp::ParameterInfo::ParameterInfo(const ParameterInfo &rhs)
{
    copy(rhs);
}

KoCompositeOp::ParameterInfo &KoCompositeOp::ParameterInfo::operator=(const ParameterInfo &rhs)
{
    copy(rhs);
    return *this;
}

void KoCompositeOp::ParameterInfo::copy(const ParameterInfo &rhs)
{
    dstRowStart = rhs.dstRowStart;
    dstRowStride = rhs.dstRowStride;
    srcRowStart = rhs.srcRowStart;
    srcRowStride = rhs.srcRowStride;
    maskRowStart = rhs.maskRowStart;
    maskRowStride = rhs.maskRowStride;
    rows = rhs.rows;
    cols = rhs.cols;
    opacity = rhs.opacity;
    flow = rhs.flow;
    _lastOpacityData = rhs._lastOpacityData;
    channelFlags = rhs.channelFlags;

    lastOpacity = rhs.lastOpacity == &rhs.opacity ? &opacity : &_lastOpacityData;
}

void KoCompositeOp::ParameterInfo::updateOpacityAndAverage(float value)
{
    const float exponent = 0.1;

    opacity = value;

    if (*lastOpacity < opacity) {
        lastOpacity = &opacity;
    } else {
        _lastOpacityData = exponent * opacity + (1.0 - exponent) * (*lastOpacity);
        lastOpacity = &_lastOpacityData;
    }
}

struct Q_DECL_HIDDEN KoCompositeOp::Private {
    const KoColorSpace *colorSpace;
    QString id;
    QString description;
    QString category;
    QBitArray defaultChannelFlags;
};

KoCompositeOp::KoCompositeOp()
    : d(new Private)
{
}

KoCompositeOp::~KoCompositeOp()
{
    delete d;
}

KoCompositeOp::KoCompositeOp(const KoColorSpace *cs, const QString &id, const QString &description, const QString &category)
    : d(new Private)
{
    d->colorSpace = cs;
    d->id = id;
    d->description = description;
    d->category = category;
    if (d->category.isEmpty()) {
        d->category = categoryMisc();
    }
}

void KoCompositeOp::composite(quint8 *dstRowStart,
                              qint32 dstRowStride,
                              const quint8 *srcRowStart,
                              qint32 srcRowStride,
                              const quint8 *maskRowStart,
                              qint32 maskRowStride,
                              qint32 rows,
                              qint32 numColumns,
                              quint8 opacity,
                              const QBitArray &channelFlags) const
{
    KoCompositeOp::ParameterInfo params;
    params.dstRowStart = dstRowStart;
    params.dstRowStride = dstRowStride;
    params.srcRowStart = srcRowStart;
    params.srcRowStride = srcRowStride;
    params.maskRowStart = maskRowStart;
    params.maskRowStride = maskRowStride;
    params.rows = rows;
    params.cols = numColumns;
    params.opacity = float(opacity) / 255.0f;
    params.flow = 1.0f;
    params.channelFlags = channelFlags;
    composite(params);
}

void KoCompositeOp::composite(const KoCompositeOp::ParameterInfo &params) const
{
    using namespace Arithmetic;

    composite(params.dstRowStart,
              params.dstRowStride,
              params.srcRowStart,
              params.srcRowStride,
              params.maskRowStart,
              params.maskRowStride,
              params.rows,
              params.cols,
              scale<quint8>(params.opacity),
              params.channelFlags);
}

QString KoCompositeOp::category() const
{
    return d->category;
}

QString KoCompositeOp::id() const
{
    return d->id;
}

QString KoCompositeOp::description() const
{
    return d->description;
}

const KoColorSpace *KoCompositeOp::colorSpace() const
{
    return d->colorSpace;
}
