// $Id: setgamma.cpp,v 1.30 2008/04/20 19:01:15 hvengel Exp $
//  Little cms Profiler
// Copyright (C) 2005-2008 Hal V. Engel
//
// THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//
// IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
// LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
// OF THIS SOFTWARE.
//
// 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 3 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, see <http://www.gnu.org/licenses/>.
//
// Version 1.99

#include "monitorvalues.h"
#include "setgamma.h"
#include "lcmsprf.h"
#include "qtlcmswidgets.h"
#include "main.h"

#include <qlineedit.h>
#include <qpixmap.h>
#include <QDoubleSpinBox>
#include <qtooltip.h>
#include <qstringlist.h>
#include <q3buttongroup.h>
#include <qlayout.h>
#include <qradiobutton.h>
#include <qcheckbox.h>
#include <q3groupbox.h>
#include <qpushbutton.h>
#include <qspinbox.h>
#include <qcombobox.h>
#include <qlabel.h>
#include <qslider.h>
#include <q3filedialog.h>
#include <q3multilineedit.h>
#include <qtabwidget.h>
#include <q3textview.h>
#include <q3progressbar.h>
#include <qdir.h>
#include <qapplication.h>
#include <q3table.h>
#include <qvalidator.h>

static PROFILERDATA sys;
Q3ButtonGroup* RGB_button_group;

monitorVal mongamma;

SetGamma::SetGamma(QWidget *parent )
    : QDialog(parent,"", Qt::WStyle_NoBorder | Qt::FramelessWindowHint)
{
    setupUi(this);
    QObject::connect(GammaR, SIGNAL(valueChanged(double)), this, SLOT(slotGammaRadio()));
    QObject::connect(GammaG, SIGNAL(valueChanged(double)), this, SLOT(slotGammaRadio()));
    QObject::connect(GammaB, SIGNAL(valueChanged(double)), this, SLOT(slotGammaRadio()));
    QObject::connect(GammaSlider, SIGNAL(valueChanged(int)), this, SLOT(slotGammaSlider()));
    QObject::connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept()));
    QObject::connect(buttonOk, SIGNAL(clicked()), this, SLOT(slotOK()));
    QObject::connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
    QObject::connect(AdjustB, SIGNAL(toggled(bool)), this, SLOT(slotChangeGamma()));
    QObject::connect(AdjustG, SIGNAL(toggled(bool)), this, SLOT(slotChangeGamma()));
    QObject::connect(AdjustR, SIGNAL(toggled(bool)), this, SLOT(slotChangeGamma()));
    QObject::connect(CheckLinkGamma, SIGNAL(toggled(bool)), this, SLOT(slotChangeGamma()));
    QObject::connect(buttonHelp, SIGNAL(clicked()), this, SLOT(slotHelpButton()));
    
    smallChart.load(QString::fromLocal8Bit(":src/images/Norman_Koren-small.png"));
    smallChartCorrected.load(QString::fromLocal8Bit(":src/images/Norman_Koren-small.png"));
    
    bigChart.load(QString::fromLocal8Bit(":src/images/Norman_Koren-big.png"));
    bigChartCorrected.load(QString::fromLocal8Bit(":src/images/Norman_Koren-big.png"));

    mongamma.get(&mongamma);

    RGB_button_group = new Q3ButtonGroup(this);
    RGB_button_group->insert(AdjustR,0);
    RGB_button_group->insert(AdjustG,1);
    RGB_button_group->insert(AdjustB,2);
    RGB_button_group->setExclusive(TRUE);

    // Fill primaries combo

    if (mongamma.items.linked==1)
    {
        CheckLinkGamma -> setChecked(true);
    }
    else
    {
        CheckLinkGamma -> setChecked(false);
    }

    GammaR -> setMinimum(1.61);
    GammaR -> setSingleStep(0.01);
    GammaR -> setMaximum(4.0);

    GammaG -> setMinimum(1.61);
    GammaG -> setSingleStep(0.01);
    GammaG -> setMaximum(4.0);

    GammaB -> setMinimum(1.61);
    GammaB -> setSingleStep(0.01);
    GammaB -> setMaximum(4.0);

    GammaR -> setValue(mongamma.items.rgamma);
    GammaG -> setValue(mongamma.items.ggamma);
    GammaB-> setValue(mongamma.items.bgamma);

    AdjustR->setChecked(mongamma.items.rchecked);
    AdjustG->setChecked(mongamma.items.gchecked);
    AdjustB->setChecked(mongamma.items.bchecked);

    slotChangeGamma();
    this -> showFullScreen ();
    this -> setWindowState(Qt::WindowFullScreen);
}

SetGamma::~SetGamma()
{
    delete RGB_button_group;
    RGB_button_group = 0;
}

void SetGamma::slotChangeGamma()
{
    if (CheckLinkGamma -> isChecked())
    {
        AdjustR -> hide();
        AdjustG -> hide();
        AdjustB -> hide();
        GammaR -> setEnabled(TRUE);
        GammaG -> hide();
        GammaB -> hide();
    }
    else
    {
        AdjustR -> show();
        AdjustG -> show();
        AdjustB -> show();
        GammaR -> show();
        GammaG -> show();
        GammaB -> show();

        GammaR -> setEnabled(AdjustR -> isChecked());
        GammaG -> setEnabled(AdjustG -> isChecked());
        GammaB -> setEnabled(AdjustB -> isChecked());
    }

    slotGammaRadio();
}

void SetGamma::slotGammaRadio()
{
    double v;
    
    // qDebug("SetGamma::slotGammaRadio() %f", GammaR -> value()); 
    if (CheckLinkGamma -> isChecked())
    {
        // set all three values
        // GammaR is the active control when 
        // these are linked.  So no need to 
        // set GammaR
 
        GammaG-> setValue(GammaR -> value());
        GammaB -> setValue(GammaR -> value());
    }
    if (AdjustR -> isChecked())  
        v = GammaR -> value();
    else if (AdjustG -> isChecked()) 
        v = GammaG -> value();
    else
        v = GammaB -> value();

    GammaSlider -> setValue((int)((v + 0.005) * 100.0));

    RenderGamma();
}


void SetGamma::slotGammaSlider()
{
    double val = ((double) GammaSlider -> value()) / 100.0;
    
    // qDebug("SetGamma::slotGammaSlider() %f", val);

    if (CheckLinkGamma -> isChecked())
    {
        // set all three values
        GammaR -> setValue(val);
        GammaG-> setValue(val);
        GammaB -> setValue(val);
    }
    else
    {
        if (AdjustR -> isChecked())
            GammaR -> setValue(val);
        else
            if (AdjustG -> isChecked())
                GammaG -> setValue(val);
        else
            // it must be blue
            GammaB -> setValue(val);
    }

    RenderGamma();
}

void SetGamma::RenderGamma()
{
    // qDebug("RenderGamma %f", GammaR -> value());
        
    const double offset = -1.25;

    if (sys.Prelinearization[0]) cmsFreeGamma(sys.Prelinearization[0]);

    if (sys.Prelinearization[1]) cmsFreeGamma(sys.Prelinearization[1]);

    if (sys.Prelinearization[2]) cmsFreeGamma(sys.Prelinearization[2]);

    if (CheckLinkGamma -> isChecked())
    {
        double val;
        val = GammaR -> value() + offset;
        
        // the -1.25 is an offset to make this work with the Norman Koren gamma chart
        // This has now been checked on an instrument calibrate display
        // and appears to be giving correct results.

        sys.Prelinearization[0] = cmsBuildGamma(256, val);
        sys.Prelinearization[1] = cmsBuildGamma(256, val);
        sys.Prelinearization[2] = cmsBuildGamma(256, val);
    }
    else
    {
        sys.Prelinearization[0] = cmsBuildGamma(256, GammaR -> value() + offset);
        sys.Prelinearization[1] = cmsBuildGamma(256, GammaG -> value() + offset);
        sys.Prelinearization[2] = cmsBuildGamma(256, GammaB -> value() + offset);
    }
    ProofGamma();
}


LPGAMMATABLE Gamma;
DWORD dwMask;

void SetGamma::Set_dwMask_Gamma ()
{

    if (Gamma) cmsFreeGamma(Gamma);
    
    if (CheckLinkGamma -> isChecked())
    {
        Gamma = cmsReverseGamma(256, sys.Prelinearization[0]);
        dwMask = 0x00FFFFFF;
    }
    else if (AdjustR -> isChecked())
    {
        Gamma = cmsReverseGamma(256, sys.Prelinearization[0]);
        dwMask = 0x00FF0000;
    }
    else if (AdjustG -> isChecked())
    {
        Gamma = cmsReverseGamma(256, sys.Prelinearization[1]);
        dwMask = 0x0000FF00;
    }
    else
    {
        Gamma = cmsReverseGamma(256, sys.Prelinearization[2]);
        dwMask = 0x000000FF;
    }
}

void SetGamma::Gamma_loop (QImage in, QImage &out)
{
    L16PARAMS Lut16;
    cmsCalcL16Params(Gamma -> nEntries, &Lut16);
    int height = in.height();
    int width  = in.width();
    
    // qDebug("Gamma_loop");
    for (int i=0; i < height; i++)
    {   
        for (int j=0; j < width; j++)
        {
            DWORD Out;
            // get rgb value and shift it to eliminate alpha channel
            QRgb InVal = (in.pixel(j, i) << 8);
            // apply the gamma curve
            uint OutVal = cmsLinearInterpLUT16(InVal , Gamma -> GammaTable, &Lut16);
            //  Only interested in one byte since image is gray
            DWORD OutByte = (DWORD) (OutVal >> 8);
            // DWORD 
            Out = OutByte;
            // shift that byte into all three channels
            OutByte <<= 8;
            Out |= OutByte;
            OutByte <<= 8;
            Out |= OutByte;
            // mask only those channels needed
            Out &= dwMask;
            // Add alpha channel and move to output pixmap
            out.setPixel( j, i, (QRgb) (Out | 0xFF000000));
         } 
     }
}

void SetGamma::ProofGamma()
{
    Set_dwMask_Gamma ();

    Gamma_loop (smallChart, smallChartCorrected);
    SmallChart -> setPixmap(SmallChart -> pixmap() -> fromImage(smallChartCorrected, 
                            Qt::ColorOnly | Qt::ThresholdDither | Qt::AvoidDither));
    SmallChart -> repaint();
    lprofApp -> processEvents();

    Gamma_loop (bigChart, bigChartCorrected);
    BigChart -> setPixmap(BigChart -> pixmap() -> fromImage(bigChartCorrected, 
                          Qt::ColorOnly | Qt::ThresholdDither | Qt::AvoidDither));
    BigChart -> repaint();
    lprofApp -> processEvents();
}

void SetGamma::slotOK()
{
    // qDebug("In slot OK");

    if (CheckLinkGamma -> isChecked())
    {
        mongamma.items.linked=1;
    }
    else
    {
        mongamma.items.linked=0;
    }

    mongamma.items.rgamma=GammaR -> value();
    mongamma.items.ggamma=GammaG -> value();
    mongamma.items.bgamma=GammaB -> value();
    mongamma.items.rchecked=AdjustR->isChecked();
    mongamma.items.gchecked=AdjustG->isChecked();
    mongamma.items.bchecked=AdjustB->isChecked();
    mongamma.put(mongamma);
}

void SetGamma::slotHelpButton()
{
    QString path = get_global_help_path() + QString::fromLocal8Bit("/help/gamma.html");
     qDebug(path.toLocal8Bit());
     getHelp()->openAssistant ();
     getHelp()->showPage(path);
}
