// bytran -|- spectral calculations for portable devices using the HITRAN database
//
// Developed by Dzianis Pliutau (Denis Plutov) and Kostya Roslyakov
// www.bytran.org | bytran@bytran.org
// Copyright (c) 2016 Dzianis Pliutau
// Distributed under the MIT license
//
// The MIT License (MIT)
// Copyright (c) 2016 Dzianis Pliutau
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.



// Class to implement the color plot used to select the color value to be used for displaying molecular spectra (color-encoded)
#include "colorplot.h"
#include <QMessageBox>
#include <math.h>

ColorPlot::ColorPlot(QQuickItem *parent)
             : QQuickPaintedItem(parent)
{
}

// Coordinates of the mouse
int ColorPlot::x() const { return this->m_x; }
void ColorPlot::setX(const int &x)
{
    if ((x != m_x) && (x <= colorRect.right()) && (x >= colorRect.left()))
    {
        m_x = x;
        emit xChanged();
    }
}
int ColorPlot::y() const { return this->m_y; }
void ColorPlot::setY(const int &y)
{
    if ((y != m_y) && (y >= colorRect.top()) && (y <= colorRect.bottom()))
    {
        m_y = y;
        emit yChanged();
        changeLightness();        
    }
}

// Getting and setting the selector width
int ColorPlot::selectorWidth() const { return this->m_selectorWidth; }
void ColorPlot::setSelectorWidth(const int &selectorWidth)
{
    if (selectorWidth != m_selectorWidth)
    {
        m_selectorWidth = selectorWidth;
        emit selectorWidthChanged();
    }
}

// Method is executed when the lightness has been changed
void ColorPlot::changeLightness()
{
    QColor color;
    int h;  // h
    int s;  // s
    int l;  // l
    int l2; // l2 to avoid copying the old l into the new color variable
    int a;  // a


    if (m_x < ((colorRect.right() + colorRect.left()) * 0.5)) { l = 254 - 126 * ((colorRect.height() - m_y) / (colorRect.height())); }
    else { l = 2 + 126 * ((colorRect.height() - m_y) / (colorRect.height())); }
    // Assigning the color and hue
    color = QColor(m_color);
    color.getHsl(&h, &s, &l2, &a);  // This extra step was added to take into account the possible gray scale when the hue value is 0
    color = color.fromHsl(m_hueValue, s, l, 255);
    m_color = color.name();
    emit colorChanged();
    this->update();
}

// Methods to get and set the current color
QString ColorPlot::color() const { return this->m_color; }
void ColorPlot::setColor(const QString &color)
{
    QColor auxColor;

    if (color != m_color)
    {
        // Getting the hue component
        auxColor = QColor(color);
        m_color = color;
        m_hueValue = auxColor.hue();

        // Emitting signals
        emit hueValueChanged();
        emit colorChanged();
        this->update();
    }
}

// Methods to get and set the slider value
qreal ColorPlot::hueValue() const { return this->m_hueValue; }
void ColorPlot::setHueValue(const qreal &hueValue)
{
    QColor color;
    int h;  // h
    int s;  // s
    int l;  // l
    int a;  // a


    if (hueValue != m_hueValue)
    {        
        color = QColor(m_color);
        color.getHsl(&h, &s, &l, &a);
        m_hueValue = hueValue;
        // This if statement takes into account that if the hue is 0, then gray scale is used
        if (m_hueValue == 0) { color = color.fromHsl(m_hueValue, 0, l, a); }
                        else { color = color.fromHsl(m_hueValue, 255, l, a); }
        m_color = color.name();
        emit hueValueChanged();
        emit colorChanged();
        this->update();
    }
}

// Methods to get and set the color bar height
int ColorPlot::colorBarHeight() const { return this->m_colorBarHeight; }
void ColorPlot::setColorBarHeight(const int &colorBarHeight)
{
    if (colorBarHeight != m_colorBarHeight)
    {
        m_colorBarHeight = colorBarHeight;
        emit colorBarHeightChanged();
    }
}

// Plot padding (distance between the edges and the axes area)
double ColorPlot::xMinPadding() const { return this->m_xMinPadding; }
void ColorPlot::setXMinPadding(const double &xMinPadding)
{
    if (xMinPadding != m_xMinPadding)
    {
        m_xMinPadding = xMinPadding;
        emit xMinPaddingChanged();
    }
}
double ColorPlot::xMaxPadding() const { return this->m_xMaxPadding; }
void ColorPlot::setXMaxPadding(const double &xMaxPadding)
{
    if (xMaxPadding != m_xMaxPadding)
    {
        m_xMaxPadding = xMaxPadding;
        emit xMaxPaddingChanged();
    }
}
double ColorPlot::yMinPadding() const { return this->m_yMinPadding; }
void ColorPlot::setYMinPadding(const double &yMinPadding)
{
    if (yMinPadding != m_yMinPadding)
    {
        m_yMinPadding = yMinPadding;
        emit yMinPaddingChanged();
    }
}
double ColorPlot::yMaxPadding() const { return this->m_yMaxPadding; }
void ColorPlot::setYMaxPadding(const double &yMaxPadding)
{
    if (yMaxPadding != m_yMaxPadding)
    {
        m_yMaxPadding = yMaxPadding;
        emit yMaxPaddingChanged();
    }
}

// Paint event to draw the spectra etc.
void ColorPlot::paint(QPainter *painter)
{
    QRectF rect;  // Rectangle representing the entire area


    // Painting the spectra to be displayed in a QML component
    const qreal halfPenWidth = qMax(painter->pen().width() / 2.0, 1.0);
    rect = boundingRect();
    rect.adjust(halfPenWidth, halfPenWidth, -halfPenWidth, -halfPenWidth);
    painter->setPen(Qt::black);

    // Adjusting rectangles
    colorBar = rect;
    colorBar.setTop(rect.bottom() - this->m_colorBarHeight);
    colorRect.setTop(rect.top());
    colorRect.setLeft(rect.left());
    colorRect.setRight(rect.right());
    colorRect.setBottom(rect.bottom() - this->m_colorBarHeight - 5);

    // Drawing the color information
    drawHslColor(painter);
}

// Method to draw the color to select
void ColorPlot::drawHslColor(QPainter *painter)
{
    QColor color;
    int i;  // Loop variable
    int j;  // Loop variable
    int h;  // h - Hue
    int s;  // s - Saturation
    int l;  // l - Lightness
    int a;  // a - Transparency
    double auxDouble;  // Auxiliary double variable
    QPen pen;
    int height;  // Integer height
    int width;   // Integer width


    // Getting the color values (h, s, l)
    color = QColor(m_color);
    color.getHsl(&h, &s, &l, &a);

    // Setting the color of the plot depending of on the color, brightness saturation
    for (i = 0; i <= colorRect.height(); i++)
    {
        l = 254 - 126 * ((colorRect.height() - i) / colorRect.height());
        color = color.fromHsl(m_hueValue, s, l, 255);
        painter->setPen(color);
        painter->drawLine(colorRect.left(), i, (colorRect.right() - colorRect.left()) * 0.5, i);
    }
    for (i = 0; i <= colorRect.height(); i++)
    {
        l = 2 + 126 * ((colorRect.height() - i) / colorRect.height());
        color = color.fromHsl(m_hueValue, s, l, 255);
        painter->setPen(color);
        painter->drawLine((colorRect.right() - colorRect.left()) * 0.5, i, colorRect.right(), i);
    }

    // Getting the color values (h, s, l)
    color = QColor(m_color);
    color.getHsl(&h, &s, &l, &a);

    // Plotting the selection rectangle based on the current color
    pen.setWidth(m_selectorWidth * 0.35);
    pen.setColor(Qt::white);
    pen.setJoinStyle(Qt::MiterJoin);
    painter->setPen(pen);
    if (l > 128)
    {
        auxDouble = colorRect.height() - (colorRect.height() * (254.0 - l)  / 126.0);
        i = (int) auxDouble;
        height = painter->fontMetrics().boundingRect(m_color).height();
        width = painter->fontMetrics().boundingRect(m_color).width();
        painter->fillRect(colorRect.left() + m_selectorWidth * 0.25, i - m_selectorWidth * 0.5, m_selectorWidth, m_selectorWidth, color);
        painter->drawRect(colorRect.left() + m_selectorWidth * 0.25, i - m_selectorWidth * 0.5, m_selectorWidth, m_selectorWidth);
    }
    else
    {
        auxDouble = colorRect.height() - (colorRect.height() * (l - 2.0) / 126.0);
        i = (int) auxDouble;
        height = painter->fontMetrics().boundingRect(m_color).height();
        width = painter->fontMetrics().boundingRect(m_color).width();
        painter->fillRect((colorRect.right() + colorRect.left()) * 0.5 + m_selectorWidth * 0.25, i - m_selectorWidth * 0.5, m_selectorWidth, m_selectorWidth, color);
        painter->drawRect((colorRect.right() * colorRect.left()) * 0.5 + m_selectorWidth * 0.25, i - m_selectorWidth * 0.5, m_selectorWidth, m_selectorWidth);
    }

    // Plotting the color bar (HSV scale)
    for (j = 0; j < 1; j++)  // This segment is for the gray scale
    {
        color.setNamedColor("black");
        painter->setPen(color);
        painter->drawLine(j, colorBar.top(), j, colorBar.bottom());
    }
    for (j = 1; j <= colorBar.width(); j++)
    {
        h = (int) (359 - 359 * (colorRect.width() - j) / (colorRect.width()));
        color.setHsv(h, 255, 255, 255);
        painter->setPen(color);
        painter->drawLine(j, colorBar.top(), j, colorBar.bottom());
    }
}
