//#####################################################################
// Copyright 2020, Craig Schroeder.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
#include <Core/Interpolation/INTERPOLATED_COLOR_MAP.h>
#include <Core/Log/LOG.h>
#include "../QT_COLOR_MAP.h"
#include "../QT_MATH.h"
#include "QT_COMPONENT.h"
#include "QT_COMPONENT_OPTIONS.h"
#include <QCheckBox>
#include <QColorDialog>
#include <QGridLayout>
#include <QIntValidator>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
namespace PhysBAM{
//#####################################################################
// Constructor
//#####################################################################
QT_COMPONENT_OPTIONS::
QT_COMPONENT_OPTIONS(QT_WINDOW& window,QT_COMPONENT& comp)
    :window(window),comp(comp)
{
    layout = new QGridLayout(this);
}
//#####################################################################
// Destructor
//#####################################################################
QT_COMPONENT_OPTIONS::
~QT_COMPONENT_OPTIONS()
{
}
//#####################################################################
// Function Action_Cancel
//#####################################################################
void QT_COMPONENT_OPTIONS::
Action_Cancel()
{
    QDialog::done(QDialog::Rejected);
}
//#####################################################################
// Function Action_Apply
//#####################################################################
void QT_COMPONENT_OPTIONS::
Action_Apply()
{
    Save();
    window.update();
}
//#####################################################################
// Function Action_Accept
//#####################################################################
void QT_COMPONENT_OPTIONS::
Action_Accept()
{
    Action_Apply();
    QDialog::done(QDialog::Accepted);
}
//#####################################################################
// Function Add_Button
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add_Button(const std::string& desc,std::function<void()> func)
{
    QPushButton *button = new QPushButton("",this);
    connect(button,&QAbstractButton::clicked,this,
        [this,func]()
        {
            Save();
            func();
            Load();
            window.update();
        });
    layout->addWidget(button,next_row,0);
    layout->addWidget(new QLabel(QString::fromStdString(desc),this),next_row++,1,1,2);
}
//#####################################################################
// Function Add
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add(const std::string& desc,OPTION<bool>& full,OPTION<bool>& user)
{
    QCheckBox* check=new QCheckBox("",this);
    save_contents.Append([check,f=&full,u=&user](){auto z=check->isChecked();if(f->Value()!=z){f->Set(z);*u=*f;}});
    load_contents.Append([check,f=&full](){check->setChecked(f->Value());});
    load_contents.Last()();
    layout->addWidget(check,next_row,0);
    layout->addWidget(new QLabel(QString::fromStdString(desc),this),next_row++,1,1,2);
    connect(check,&QCheckBox::stateChanged,this,&QT_COMPONENT_OPTIONS::Action_Apply);
}
//#####################################################################
// Function Add
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add(const std::string& desc,OPTION<int>& full,OPTION<int>& user)
{
    QLineEdit* edit=new QLineEdit("",this);
    edit->setValidator(new QIntValidator(this));
    save_contents.Append([edit,f=&full,u=&user](){auto z=edit->text().toInt();if(f->Value()!=z){f->Set(z);*u=*f;}});
    load_contents.Append([edit,f=&full](){edit->setText(QString::number(f->Value()));});
    load_contents.Last()();
    layout->addWidget(edit,next_row,0);
    layout->addWidget(new QLabel(QString::fromStdString(desc),this),next_row++,1,1,2);
    connect(edit,&QLineEdit::textEdited,this,&QT_COMPONENT_OPTIONS::Action_Apply);
}
//#####################################################################
// Function Add
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add(const std::string& desc,OPTION<float>& full,OPTION<float>& user)
{
    QLineEdit* edit=new QLineEdit("",this);
    edit->setValidator(new QDoubleValidator(this));
    save_contents.Append([edit,f=&full,u=&user](){auto z=edit->text().toDouble();if(f->Value()!=z){f->Set(z);*u=*f;}});
    load_contents.Append([edit,f=&full](){edit->setText(QString::number(f->Value()));});
    load_contents.Last()();
    layout->addWidget(edit,next_row,0);
    layout->addWidget(new QLabel(QString::fromStdString(desc),this),next_row++,1,1,2);
    connect(edit,&QLineEdit::textEdited,this,&QT_COMPONENT_OPTIONS::Action_Apply);
}
//#####################################################################
// Function Add
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add(const std::string& desc,OPTION<std::string>& full,OPTION<std::string>& user)
{
    QLineEdit* edit=new QLineEdit(QString::fromStdString(full.Value()),this);
    save_contents.Append([edit,f=&full,u=&user](){auto z=edit->text().toStdString();if(f->Value()!=z){f->Set(z);*u=*f;}});
    load_contents.Append([edit,f=&full](){edit->setText(QString::fromStdString(f->Value()));});
    load_contents.Last()();
    layout->addWidget(edit,next_row,0);
    layout->addWidget(new QLabel(QString::fromStdString(desc)),next_row++,1,1,2);
    connect(edit,&QLineEdit::textEdited,this,&QT_COMPONENT_OPTIONS::Action_Apply);
}
//#####################################################################
// Function Add
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add(const std::string& desc,OPTION<C>& full,OPTION<C>& user)
{
    auto* col=new QPushButton("",this);
    layout->addWidget(col,next_row,0,Qt::Alignment());
    layout->addWidget(new QLabel(QString::fromStdString(desc)),next_row++,1,1,2);
    QObject::connect(col,&QPushButton::clicked,this,
        [this,col]()
        {
            QPalette palette=col->palette();
            QColor new_color=QColorDialog::getColor(
                palette.color(QPalette::Button),this,"Select a color");
            palette.setColor(QPalette::Button,new_color);
            col->setAutoFillBackground(true);
            col->setPalette(palette);
            Action_Apply();
        });
    save_contents.Append(
        [col,f=&full,u=&user]()
        {
            C z;
            from_qt(z,col->palette().color(QPalette::Button));
            if(f->Value()!=z){f->Set(z);*u=*f;}
        });
    load_contents.Append(
        [col,f=&full]()
        {
            QPalette palette;
            palette.setColor(QPalette::Button,to_qt_color(f->Value()));
            col->setAutoFillBackground(true);
            col->setPalette(palette);
        });
    load_contents.Last()();
}
//#####################################################################
// Function Update_Button_Color
//#####################################################################
void QT_COMPONENT_OPTIONS::
Update_Button_Color(QPushButton* button,
    const INTERPOLATED_COLOR_MAP<float>& out)
{
    QSize size=button->contentsRect().size();
    // The -4 is a workaround to avoid growing buttons.
    int width=size.width()-4,height=size.height();
    QImage img(width,height,QImage::Format_RGB32);
    for(int i=0;i<width;i++)
    {
        auto m=out((float)i/(width-1));
        auto c=QColor::fromRgbF(m(0),m(1),m(2));
        for(int j=0;j<height;j++)
            img.setPixelColor(i,j,c);
    }
    button->setIcon(QIcon(QPixmap::fromImage(img)));
    button->setIconSize({width,height});
    button->setStyleSheet("border: none;");
}
//#####################################################################
// Function Add
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add(const std::string& desc,OPTION<INTERPOLATED_COLOR_MAP<float>>& full,
    OPTION<INTERPOLATED_COLOR_MAP<float>>& user)
{
    auto* col=new QPushButton("",this);
    layout->addWidget(col,next_row,0,Qt::Alignment());
    layout->addWidget(new QLabel(QString::fromStdString(desc)),next_row++,1,1,2);
    save_contents.Append(
        [col,f=&full,u=&user]()
        {
        });
    load_contents.Append(
        [this,col,f=&full]()
        {
            Update_Button_Color(col,f->Value());
        });
    load_contents.Last()();
    QObject::connect(col,&QPushButton::clicked,this,
        [this,col,f=&full,u=&user]()
        {
            auto f2(*f);
            bool changed=false;
            auto* dialog=new QT_COLOR_MAP(window,comp,f->Value(),changed);
            dialog->setWindowTitle(QString::fromStdString(comp.Name()));
            dialog->apply_cmd=[this](){Action_Apply();};
            int result=dialog->exec();
            if(result==QDialog::Accepted)
            {
                if(changed) *u=*f;
            }
            else
            {
                *f=f2;
            }
            Update_Button_Color(col,f->Value());
            Action_Apply();
        });
}
//#####################################################################
// Function Add
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add(const std::string& desc,OPTION<ARRAY<C>>& full,OPTION<ARRAY<C>>& user)
{
    PHYSBAM_FATAL_ERROR("TODO");
}
//#####################################################################
// Function Add_Token
//#####################################################################
void QT_COMPONENT_OPTIONS::
Add_End_Buttons()
{
    QPushButton *cancel = new QPushButton("Cancel",this);
    QPushButton *accept = new QPushButton("Accept",this);
    connect(cancel,&QAbstractButton::clicked,this,&QT_COMPONENT_OPTIONS::Action_Cancel);
    connect(accept,&QAbstractButton::clicked,this,&QT_COMPONENT_OPTIONS::Action_Accept);
    layout->addWidget(cancel,next_row,0);
    layout->addWidget(accept,next_row++,1);
}
//#####################################################################
// Function Load
//#####################################################################
void QT_COMPONENT_OPTIONS::
Load()
{
    for(const auto& f:load_contents) f();
}
//#####################################################################
// Function Save
//#####################################################################
void QT_COMPONENT_OPTIONS::
Save()
{
    for(const auto& f:save_contents) f();
}
}
