/***************************************************************************
 *   Copyright (C) 2008-2009 by Heiko Koehn - KoehnHeiko@googlemail.com    *
 *   Copyright (C) 2014 by Ahmed Charles - acharles@outlook.com            *
 *   Copyright (C) 2015, 2018-2019, 2021 by Stephen Lyons                  *
 *                                               - slysven@virginmedia.com *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


#include "dlgColorTrigger.h"


#include "dlgTriggerEditor.h"
#include "Host.h"
#include "TTextEdit.h"
#include "TTrigger.h"
#include "mudlet.h"


dlgColorTrigger::dlgColorTrigger(QWidget* pParentWidget, TTrigger* pT, const bool isBackGround, const QString& title)
: QDialog(pParentWidget)
, mpTrigger(pT)
, mIsBackground(isBackGround)
{
    // init generated dialog
    setupUi(this);

    // The buttonBox's Cancel button has been connect up to this class's
    // rejected() signal to close it without changing anything in the caller!
    // The Ignore button means do not consider this fore or back ground (as per
    // mIsBackground) for the colour trigger - only use the other one (which
    // must be set)
    // The Apply button unhides the display of the colors codes 16 - 255 - it
    // is set up as a one-shot operation, and is effectively "fired" if the
    // existing color is in that range of numbers:
    buttonBox->button(QDialogButtonBox::Apply)->setText(tr("More colors"));
    buttonBox->button(QDialogButtonBox::Apply)->setCheckable(true);
    buttonBox->button(QDialogButtonBox::Apply)->setToolTip(utils::richText(tr("Click to access all 256 ANSI colors.")));
    connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, this, &dlgColorTrigger::slot_moreColorsClicked);

    connect(buttonBox->button(QDialogButtonBox::Ignore), &QAbstractButton::clicked, this, &dlgColorTrigger::slot_resetColorClicked);
    buttonBox->button(QDialogButtonBox::Ignore)
            ->setToolTip(utils::richText(mIsBackground
                                                 ? tr("Click to make the color trigger ignore the text's background color - however choosing this for both foreground and background is an error.")
                                                 : tr("Click to make the color trigger ignore the text's foreground color - however choosing this for both foreground and background is an error.")));
    // The Reset button means trigger only if the text is not set to anything
    // so is in the "default" colour - what ever THAT is:
    connect(buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, this, &dlgColorTrigger::slot_defaultColorClicked);
    buttonBox->button(QDialogButtonBox::Reset)->setText(tr("Default"));
    buttonBox->button(QDialogButtonBox::Reset)
            ->setToolTip(utils::richText(mIsBackground ? tr("Click to make the color trigger when the text's background color has not been modified from its normal value.")
                                                       : tr("Click to make the color trigger when the text's foreground color has not been modified from its normal value.")));
    groupBox_basicColors->setToolTip(utils::richText(mIsBackground ? tr("Click a color to make the trigger fire only when the text's background color matches the color number indicated.")
                                                                   : tr("Click a color to make the trigger fire only when the text's foreground color matches the color number indicated.")));

    connect(pushButton_setUsingRgbValue, &QAbstractButton::clicked, this, &dlgColorTrigger::slot_rgbColorClicked);
    connect(pushButton_setUsingGrayValue, &QAbstractButton::clicked, this, &dlgColorTrigger::slot_grayColorClicked);

    setupBasicButton(pushButton_black, 0, mpTrigger->mpHost->mBlack, tr("Black"));
    setupBasicButton(pushButton_red, 1, mpTrigger->mpHost->mRed, tr("Red"));
    setupBasicButton(pushButton_green, 2, mpTrigger->mpHost->mGreen, tr("Green"));
    setupBasicButton(pushButton_yellow, 3, mpTrigger->mpHost->mYellow, tr("Yellow"));
    setupBasicButton(pushButton_blue, 4, mpTrigger->mpHost->mBlue, tr("Blue"));
    setupBasicButton(pushButton_magenta, 5, mpTrigger->mpHost->mMagenta, tr("Magenta"));
    setupBasicButton(pushButton_cyan, 6, mpTrigger->mpHost->mCyan, tr("Cyan"));
    setupBasicButton(pushButton_white, 7, mpTrigger->mpHost->mWhite, tr("White (Light gray)"));

    setupBasicButton(pushButton_Lblack, 8, mpTrigger->mpHost->mLightBlack, tr("Light black (Dark gray)"));
    setupBasicButton(pushButton_Lred, 9, mpTrigger->mpHost->mLightRed, tr("Light red"));
    setupBasicButton(pushButton_Lgreen, 10, mpTrigger->mpHost->mLightGreen, tr("Light green"));
    setupBasicButton(pushButton_Lyellow, 11, mpTrigger->mpHost->mLightYellow, tr("Light yellow"));
    setupBasicButton(pushButton_Lblue, 12, mpTrigger->mpHost->mLightBlue, tr("Light blue"));
    setupBasicButton(pushButton_Lmagenta, 13, mpTrigger->mpHost->mLightMagenta, tr("Light magenta"));
    setupBasicButton(pushButton_Lcyan, 14, mpTrigger->mpHost->mLightCyan, tr("Light cyan"));
    setupBasicButton(pushButton_Lwhite, 15, mpTrigger->mpHost->mLightWhite, tr("Light white"));

    // These will correctly set the colours on the value label in their area:
    if (mIsBackground) {
        if (mpTrigger->mColorTriggerBgAnsi >= 16 && mpTrigger->mColorTriggerBgAnsi <= 231) {
            slot_moreColorsClicked();
            horizontalSlider_red->setSliderPosition((mpTrigger->mColorTriggerBgAnsi - 16) / 36);
            horizontalSlider_green->setSliderPosition((mpTrigger->mColorTriggerBgAnsi - 16 - horizontalSlider_red->value() * 36) / 6);
            horizontalSlider_blue->setSliderPosition((mpTrigger->mColorTriggerBgAnsi - 16 - horizontalSlider_red->value() * 36) - horizontalSlider_green->value() * 6);
            slot_rgbColorChanged();
            slot_grayColorChanged(0);
            slot_setRBGButtonFocus();

        } else if (mpTrigger->mColorTriggerBgAnsi >= 232 && mpTrigger->mColorTriggerBgAnsi <= 255) {
            slot_moreColorsClicked();
            // The RGB controls will be at zeros so this will set the things up
            // to those
            slot_rgbColorChanged();
            // We can set the gray directly
            slot_grayColorChanged(mpTrigger->mColorTriggerBgAnsi - 232);
            slot_setGreyButtonFocus();

        } else {
            // In the case of default / ignore or one of the basic 16 - colors
            // we will hide the extra ones:
            groupBox_rgbScale->setVisible(false);
            groupBox_grayScale->setVisible(false);

            // The color is either not set (is reset) or a basic colour - we do
            // not provide a means to set a colour trigger based on a 16M value
            // (yet) from the GUI?

            // The RGB controls will be at zeros so this will set the things up
            // to those
            slot_rgbColorChanged();
            // We can set the gray directly
            slot_grayColorChanged(0);
        }

    } else {
        if (mpTrigger->mColorTriggerFgAnsi >= 16 && mpTrigger->mColorTriggerFgAnsi <= 231) {
            slot_moreColorsClicked();
            // Current background for this trigger is a RGB one so set that
            // control to the matching value:

            horizontalSlider_red->setSliderPosition((mpTrigger->mColorTriggerFgAnsi - 16) / 36);
            horizontalSlider_green->setSliderPosition((mpTrigger->mColorTriggerFgAnsi - 16 - horizontalSlider_red->value() * 36) / 6);
            horizontalSlider_blue->setSliderPosition((mpTrigger->mColorTriggerFgAnsi - 16 - horizontalSlider_red->value() * 36) - horizontalSlider_green->value() * 6);
            slot_rgbColorChanged();
            slot_grayColorChanged(0);
            slot_setRBGButtonFocus();

        } else if (mpTrigger->mColorTriggerFgAnsi >= 232 && mpTrigger->mColorTriggerFgAnsi <= 255) {
            slot_moreColorsClicked();
            // Current background for this trigger is a Gray one so set that
            // control to the matching value:

            // The RGB controls will be at zeros so this will set the things up
            // to those
            slot_rgbColorChanged();
            // We can set the gray directly
            slot_grayColorChanged(mpTrigger->mColorTriggerFgAnsi - 232);
            slot_setGreyButtonFocus();

        } else {
            // In the case of default / ignore or one of the basic 16 - colors
            // we will hide the extra ones:
            groupBox_rgbScale->setVisible(false);
            groupBox_grayScale->setVisible(false);

            // The color is either not set (is reset) or a basic colour - we do
            // not provide a means to set a colour trigger based on a 16M value
            // (yet) from the GUI?

            // The RGB controls will be at zeros so this will set the things up
            // to those
            slot_rgbColorChanged();
            // We can set the gray directly
            slot_grayColorChanged(0);
        }
    }

    if (!title.isNull()) {
        setWindowTitle(title);
    }

    label_rgbValue->setEnabled(false);
    label_grayValue->setEnabled(false);

    // We set up the controls into the default state before wiring up the slots
    // that get run on changing the controls - the "set" button in each of these
    // groupboxes gets the focus once any of the sliders are adjusted:
    connect(horizontalSlider_red, &QAbstractSlider::valueChanged, this, &dlgColorTrigger::slot_rgbColorChanged);
    connect(horizontalSlider_red, &QAbstractSlider::valueChanged, this, &dlgColorTrigger::slot_setRBGButtonFocus);
    connect(horizontalSlider_green, &QAbstractSlider::valueChanged, this, &dlgColorTrigger::slot_rgbColorChanged);
    connect(horizontalSlider_green, &QAbstractSlider::valueChanged, this, &dlgColorTrigger::slot_setRBGButtonFocus);
    connect(horizontalSlider_blue, &QAbstractSlider::valueChanged, this, &dlgColorTrigger::slot_rgbColorChanged);
    connect(horizontalSlider_blue, &QAbstractSlider::valueChanged, this, &dlgColorTrigger::slot_setRBGButtonFocus);
    connect(horizontalSlider_gray, &QAbstractSlider::valueChanged, this, &dlgColorTrigger::slot_grayColorChanged);
    connect(horizontalSlider_gray, &QAbstractSlider::valueChanged, this, &dlgColorTrigger::slot_setGreyButtonFocus);

    // If the current is ignored or default set the focus onto them:
    if ((mIsBackground && mpTrigger->mColorTriggerBgAnsi == TTrigger::scmDefault) || (!mIsBackground && mpTrigger->mColorTriggerFgAnsi == TTrigger::scmDefault)) {
        buttonBox->button(QDialogButtonBox::Reset)->setFocus();

    } else if ((mIsBackground && mpTrigger->mColorTriggerBgAnsi == TTrigger::scmIgnored) || (!mIsBackground && mpTrigger->mColorTriggerFgAnsi == TTrigger::scmIgnored)) {
        buttonBox->button(QDialogButtonBox::Ignore)->setFocus();
    }
}

void dlgColorTrigger::setupBasicButton(QPushButton* pButton, const int ansiColor, const QColor& color, const QString& colorText)
{
    connect(pButton, &QPushButton::clicked, this, [this, ansiColor]() {
        slot_basicColorClicked(ansiColor);
    });

    if ((mIsBackground && (mpTrigger->mColorTriggerBgAnsi == ansiColor)) || (!mIsBackground && (mpTrigger->mColorTriggerFgAnsi == ansiColor))) {
        pButton->setFocus();
    }

    //: Color Trigger dialog button in basic 16-color set, the first value is the name of the color, the second is the ANSI color number - for most languages modification is not likely to be needed - this text is used in two places
    pButton->setText(tr("%1 [%2]").arg(colorText, QString::number(ansiColor)));
    pButton->setStyleSheet(dlgTriggerEditor::generateButtonStyleSheet(color));
}

void dlgColorTrigger::slot_rgbColorChanged()
{
    mRgbAnsiColorNumber = 16 + 36 * horizontalSlider_red->value() + 6 * horizontalSlider_green->value() + horizontalSlider_blue->value();
    mRgbAnsiColor = QColor(horizontalSlider_red->value() * 51, horizontalSlider_green->value() * 51, horizontalSlider_blue->value() * 51);
    label_rgbValue->setText(qsl("[%1]").arg(QString::number(mRgbAnsiColorNumber)));
    // Use the same stylesheet code as in the main editor and for the basic 16
    // color buttons but because this is a QLabel we need to replace one word in
    // the generated stylesheet:
    label_rgbValue->setStyleSheet(dlgTriggerEditor::generateButtonStyleSheet(mRgbAnsiColor).replace(QLatin1String("QPushButton"), QLatin1String("QLabel")));
}

void dlgColorTrigger::slot_setRBGButtonFocus()
{
    pushButton_setUsingRgbValue->setFocus();
}

void dlgColorTrigger::slot_grayColorChanged(int sliderValue)
{
    mGrayAnsiColorNumber = 232 + sliderValue;
    const int value = sliderValue * 10 + 8;

    mGrayAnsiColor = QColor(value, value, value);
    label_grayValue->setText(qsl("[%1]").arg(QString::number(mGrayAnsiColorNumber)));
    // Use the same stylesheet code as in the main editor and for the basic 16
    // color buttons but because this is a QLabel we need to replace one word in
    // the generated stylesheet:
    label_grayValue->setStyleSheet(dlgTriggerEditor::generateButtonStyleSheet(mGrayAnsiColor).replace(QLatin1String("QPushButton"), QLatin1String("QLabel")));

    if (horizontalSlider_gray->value() != sliderValue) {
        // This slot is also used to set the value so it may need to reposition
        // the slider as well - but we do not want to get into a loop so only
        // do this if the values differ:
        horizontalSlider_gray->setValue(sliderValue);
    }
}

void dlgColorTrigger::slot_setGreyButtonFocus()
{
    pushButton_setUsingGrayValue->setFocus();
}

void dlgColorTrigger::slot_basicColorClicked(int ansiColor)
{
    if (!mpTrigger) {
        return;
    }

    QColor choosenColor;
    // clang-format off
    switch (ansiColor) {
    case 0:     choosenColor = mpTrigger->mpHost->mBlack;           break;
    case 1:     choosenColor = mpTrigger->mpHost->mRed;             break;
    case 2:     choosenColor = mpTrigger->mpHost->mGreen;           break;
    case 3:     choosenColor = mpTrigger->mpHost->mYellow;          break;
    case 4:     choosenColor = mpTrigger->mpHost->mBlue;            break;
    case 5:     choosenColor = mpTrigger->mpHost->mMagenta;         break;
    case 6:     choosenColor = mpTrigger->mpHost->mCyan;            break;
    case 7:     choosenColor = mpTrigger->mpHost->mWhite;           break;
    case 8:     choosenColor = mpTrigger->mpHost->mLightBlack;      break;
    case 9:     choosenColor = mpTrigger->mpHost->mLightRed;        break;
    case 10:    choosenColor = mpTrigger->mpHost->mLightGreen;      break;
    case 11:    choosenColor = mpTrigger->mpHost->mLightYellow;     break;
    case 12:    choosenColor = mpTrigger->mpHost->mLightBlue;       break;
    case 13:    choosenColor = mpTrigger->mpHost->mLightMagenta;    break;
    case 14:    choosenColor = mpTrigger->mpHost->mLightCyan;       break;
    case 15:    choosenColor = mpTrigger->mpHost->mLightWhite;      break;
    default:
        Q_UNREACHABLE();
    }
    // clang-format on

    mpTrigger->mColorTrigger = true;
    if (mIsBackground) {
        mpTrigger->mColorTriggerBgAnsi = ansiColor;
        mpTrigger->mColorTriggerBgColor = choosenColor;
    } else {
        mpTrigger->mColorTriggerFgAnsi = ansiColor;
        mpTrigger->mColorTriggerFgColor = choosenColor;
    }

    close();
}

void dlgColorTrigger::slot_rgbColorClicked()
{
    mpTrigger->mColorTrigger = true;
    if (mIsBackground) {
        mpTrigger->mColorTriggerBgAnsi = mRgbAnsiColorNumber;
        mpTrigger->mColorTriggerBgColor = mRgbAnsiColor;
    } else {
        mpTrigger->mColorTriggerFgAnsi = mRgbAnsiColorNumber;
        mpTrigger->mColorTriggerFgColor = mRgbAnsiColor;
    }

    close();
}

void dlgColorTrigger::slot_grayColorClicked()
{
    mpTrigger->mColorTrigger = true;
    if (mIsBackground) {
        mpTrigger->mColorTriggerBgAnsi = mGrayAnsiColorNumber;
        mpTrigger->mColorTriggerBgColor = mGrayAnsiColor;
    } else {
        mpTrigger->mColorTriggerFgAnsi = mGrayAnsiColorNumber;
        mpTrigger->mColorTriggerFgColor = mGrayAnsiColor;
    }

    close();
}

void dlgColorTrigger::slot_resetColorClicked()
{
    if (mIsBackground) {
        mpTrigger->mColorTriggerBgAnsi = TTrigger::scmIgnored;
        mpTrigger->mColorTriggerBgColor = QColor();
    } else {
        mpTrigger->mColorTriggerFgAnsi = TTrigger::scmIgnored;
        mpTrigger->mColorTriggerFgColor = QColor();
    }

    // Reset mColorTrigger if NEITHER this one OR the other are set
    mpTrigger->mColorTrigger = (mpTrigger->mColorTriggerFgAnsi != TTrigger::scmIgnored || mpTrigger->mColorTriggerBgAnsi != TTrigger::scmIgnored);

    close();
}

void dlgColorTrigger::slot_defaultColorClicked()
{
    if (mIsBackground) {
        mpTrigger->mColorTriggerBgAnsi = TTrigger::scmDefault;
        mpTrigger->mColorTriggerBgColor = QColor();
    } else {
        mpTrigger->mColorTriggerFgAnsi = TTrigger::scmDefault;
        mpTrigger->mColorTriggerFgColor = QColor();
    }

    close();
}

void dlgColorTrigger::slot_moreColorsClicked()
{
    // Used to pull the button down if this slot is called from the constructor:
    buttonBox->button(QDialogButtonBox::Apply)->setChecked(true);

    // Implement a one-shot action by disabling it once it is pressed:
    buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);

    buttonBox->button(QDialogButtonBox::Apply)->setToolTip(utils::richText(tr("All color options are showing.")));

    if (groupBox_rgbScale->isHidden()) {
        groupBox_rgbScale->show();
    }

    if (groupBox_grayScale->isHidden()) {
        groupBox_grayScale->show();
    }
}
