#include "autocookwindow.h"
#include "ui_autocookwindow.h"

#include "keepwarmpopup.h"
#include "cookhistory.h"
#include "confirmpopup.h"
#include "favoritenamepopup.h"
#include "stringer.h"
#include "soundplayer.h"

AutoCookWindow::AutoCookWindow(QWidget *parent, Cook cook) :
    QMainWindow(parent),
    ui(new Ui::AutoCookWindow),
    cook(cook)
{
    ui->setupUi(this);

    ui->clockContainer->setParent(ui->upperStack);
    setAttribute(Qt::WA_DeleteOnClose);

    if (!this->cook.isLoaded())
        this->cook.load();

    autocook = AutoCook(this->cook);
    processSelected = false;

    setupUi();

    Oven *oven = Oven::getInstance();
    connect(oven, SIGNAL(changed(Oven*)), SLOT(updateView()));

    returnToCurrentStepTimer.setSingleShot(true);
    returnToCurrentStepTimer.setInterval(3000);
    connect(&returnToCurrentStepTimer, SIGNAL(timeout()), SLOT(returnToCurrentStep()));

    showCurrentHumidityTimer.setSingleShot(true);
    showCurrentHumidityTimer.setInterval(3000);
    connect(&showCurrentHumidityTimer, SIGNAL(timeout()), SLOT(showCurrentHumidity()));

    showCurrentTempTimer.setSingleShot(true);
    showCurrentTempTimer.setInterval(3000);
    connect(&showCurrentTempTimer, SIGNAL(timeout()), SLOT(showCurrentTemp()));

    connect(&checkCookTimer, SIGNAL(timeout()), SLOT(checkCook()));
    checkCookTimer.start(100);

    connect(&checkProcessTimer, SIGNAL(timeout()), SLOT(checkProcess()));

    AutoCookSetting setting;
    setting.type = cook.type;
    setting.root = cook.root;
    setting.name = cook.name;

    for (int i = 0; i < 5; i++)
        setting.configs[i] = cook.configs[i].current;

    CookHistory::record(setting);

    foreach (QPushButton *button, findChildren<QPushButton *>())
        connect(button, &QPushButton::pressed, SoundPlayer::playClick);

    connect(&updateViewTimer, SIGNAL(timeout()), SLOT(updateView()));
    updateViewTimer.start(100);
}

AutoCookWindow::~AutoCookWindow()
{
    delete ui;
}

void AutoCookWindow::setupUi()
{
    steamModeIcon.load(":/images/cook_mode/small_steam.png");
    dryModeIcon.load(":/images/cook_mode/small_dryheat.png");
    combiModeIcon.load(":/images/cook_mode/small_combi.png");

    ui->cookTypeIcon->setPixmap(Define::icon(cook.type));
    ui->selectCookButton->setText(cook.name);

    ui->stepIndicator->setMaximum(cook.steps.size() - 1);

    ui->cookStepAnimation->start(300);

    ui->humidityGauge->setMinimum(0);
    ui->humidityGauge->setMaximum(100);
    ui->humidityGauge->setValue(0);

    ui->heatGauge->setMinimum(30);
    ui->heatGauge->setMaximum(300);
    ui->heatGauge->setValue(30);

    ui->openDoorAnimation->load(":/images/animation/door_01.png");
    ui->openDoorAnimation->load(":/images/animation/door_02.png");
    ui->openDoorAnimation->load(":/images/animation/door_03.png");
    ui->openDoorAnimation->load(":/images/animation/door_04.png");
    ui->openDoorAnimation->load(":/images/animation/door_05.png");
    ui->openDoorAnimation->load(":/images/animation/door_06.png");
    ui->openDoorAnimation->load(":/images/animation/door_07.png");
    ui->openDoorAnimation->load(":/images/animation/door_08.png");
    ui->openDoorAnimation->load(":/images/animation/door_09.png");
    ui->openDoorAnimation->start(300);
    ui->openDoorAnimation->hide();
    ui->openDoorArrow->hide();

    ui->closeDoorAnimation->load(":/images/animation/door_09.png");
    ui->closeDoorAnimation->load(":/images/animation/door_08.png");
    ui->closeDoorAnimation->load(":/images/animation/door_07.png");
    ui->closeDoorAnimation->load(":/images/animation/door_06.png");
    ui->closeDoorAnimation->load(":/images/animation/door_05.png");
    ui->closeDoorAnimation->load(":/images/animation/door_04.png");
    ui->closeDoorAnimation->load(":/images/animation/door_03.png");
    ui->closeDoorAnimation->load(":/images/animation/door_02.png");
    ui->closeDoorAnimation->load(":/images/animation/door_01.png");
    ui->closeDoorAnimation->start(300);
    ui->closeDoorAnimation->hide();
    ui->closeDoorArrow->hide();

    lastViewCookMode = Define::InvalidMode;
    lastViewCookType = Define::Invalid;
    lastViewCoreTemp = 999;
    lastViewDoorType = Define::Invalid;
    lastViewTime = 0;
    lastViewStepIndex = -1;
    selectedStepIndex = 0;
    showingCurrentHumidity = false;
    showingCurrentTemp = false;

    if (autocook.cook.processes.isEmpty())
    {
        ui->processTitleLabel->hide();
        ui->processTypeLabel->hide();
        ui->processButton_1->hide();
        ui->processButton_2->hide();
        ui->processButton_3->hide();
    }
    else
    {
        QString typeText;
        QSignalMapper *sm = NULL;
        for (int i = 0; i < 3; i++)
        {
            QPushButton *pb;
            switch (i)
            {
            case 0:
                pb = ui->processButton_1;
                break;
            case 1:
                pb = ui->processButton_2;
                break;
            case 2:
                pb = ui->processButton_3;
                break;
            }

            if (i < autocook.cook.processes.size())
            {
                if (sm == NULL)
                {
                    sm = new QSignalMapper(this);
                    connect(sm, SIGNAL(mapped(int)), SLOT(startProcess(int)));
                }

                Define::Process process = autocook.cook.processes[i];

                QString text = Define::name(process);
                if (typeText.isEmpty())
                    typeText = text;
                else
                    typeText += ", " + text;

                QString styleSheet = QString("QPushButton { border-image: url(%1) } QPushButton:pressed { border-image: url(%2) }")
                        .arg(Define::icon(process))
                        .arg(Define::iconOverlay(process));

                pb->setStyleSheet(styleSheet);

                sm->setMapping(pb, (int) process);
                connect(pb, SIGNAL(clicked()), sm, SLOT(map()));
            }
            else
            {
                pb->hide();
            }
        }

        ui->processTypeLabel->setText(typeText);
    }

    ui->processContainer->hide();
    ui->doorStepLabel->hide();

    updateView();
}

void AutoCookWindow::updateView()
{
    Oven *oven = Oven::getInstance();

    if (!autocook.done())
    {
        int remainingTime = qMax(0, autocook.msecs());
        ui->timeLabel->setText(Stringer::remainingTime(remainingTime));
    }

    int coreTemp = oven->currentInterTemp();
    if (coreTemp != lastViewCoreTemp)
    {
        lastViewCoreTemp = coreTemp;
        if (cook.isCoreTempValid())
            ui->interTempLabel->setText(Stringer::temperature(coreTemp, cook.coreTemp(), Stringer::fontSize14));
        else
            ui->interTempLabel->setText(Stringer::temperature(coreTemp, Stringer::fontSize14));
    }

    if (autocook.done())
    {
        if (!oven->door())
        {
            if (ui->openDoorAnimation->isHidden())
                ui->openDoorAnimation->show();

            if (ui->openDoorArrow->isHidden())
                ui->openDoorArrow->show();

            if (ui->closeDoorAnimation->isVisible())
                ui->closeDoorAnimation->hide();

            if (ui->closeDoorArrow->isVisible())
                ui->closeDoorArrow->hide();
        }
        else
        {
            if (ui->openDoorAnimation->isVisible())
                ui->openDoorAnimation->hide();

            if (ui->openDoorArrow->isVisible())
                ui->openDoorArrow->hide();

            if (ui->closeDoorAnimation->isVisible())
                ui->closeDoorAnimation->hide();

            if (ui->closeDoorArrow->isVisible())
                ui->closeDoorArrow->hide();
        }

        if (ui->processContainer->isHidden())
            ui->processContainer->show();
    }
    else if (autocook.isWaitingDoorOpened() && !oven->door())
    {
        if (ui->openDoorAnimation->isHidden())
            ui->openDoorAnimation->show();

        if (ui->openDoorArrow->isHidden())
            ui->openDoorArrow->show();

        if (ui->closeDoorAnimation->isVisible())
            ui->closeDoorAnimation->hide();

        if (ui->closeDoorArrow->isVisible())
            ui->closeDoorArrow->hide();
    }
    else if (!autocook.isWaitingDoorOpened() && oven->door())
    {
        if (ui->openDoorAnimation->isVisible())
            ui->openDoorAnimation->hide();

        if (ui->openDoorArrow->isVisible())
            ui->openDoorArrow->hide();

        if (ui->closeDoorAnimation->isHidden())
            ui->closeDoorAnimation->show();

        if (ui->closeDoorArrow->isHidden())
            ui->closeDoorArrow->show();
    }
    else
    {
        if (ui->openDoorAnimation->isVisible())
            ui->openDoorAnimation->hide();

        if (ui->openDoorArrow->isVisible())
            ui->openDoorArrow->hide();

        if (ui->closeDoorAnimation->isVisible())
            ui->closeDoorAnimation->hide();

        if (ui->closeDoorArrow->isVisible())
            ui->closeDoorArrow->hide();
    }

    if (autocook.cook.steps[autocook.currentStepIndex].type == Define::Preheat)
    {
        if (ui->preheatIcon->isHidden())
            ui->preheatIcon->show();

        if (ui->preheatLabel->isHidden())
            ui->preheatLabel->show();

        if (ui->preheatGauge->isHidden())
            ui->preheatGauge->show();

        ui->preheatGauge->setMaximum(autocook.cook.steps[autocook.currentStepIndex].temp);
        ui->preheatGauge->setMinimum(autocook.startTemp);
        ui->preheatGauge->setValue(oven->currentTemp());
    }
    else
    {
        if (ui->preheatIcon->isVisible())
            ui->preheatIcon->hide();

        if (ui->preheatLabel->isVisible())
            ui->preheatLabel->hide();

        if (ui->preheatGauge->isVisible())
            ui->preheatGauge->hide();
    }

    if (selectedStepIndex != autocook.currentStepIndex)
    {
        if (!returnToCurrentStepTimer.isActive())
        {
            selectedStepIndex = autocook.currentStepIndex;
        }
    }

    if (selectedStepIndex != lastViewStepIndex)
    {
        lastViewStepIndex = selectedStepIndex;
        ui->stepIndicator->setCurrentIndex(selectedStepIndex);
    }

    CookStep showingStep = autocook.cook.steps[selectedStepIndex];
    if (Define::classify(showingStep.type) == Define::DoorClass)
    {
        if (ui->cookStepIcon->isVisible())
            ui->cookStepIcon->hide();

        if (ui->cookStepLabel->isVisible())
            ui->cookStepLabel->hide();

        if (ui->cookModeIcon->isVisible())
            ui->cookModeIcon->hide();

        if (ui->humidityGauge->isVisible())
            ui->humidityGauge->hide();

        if (ui->humidityGaugeButton->isVisible())
            ui->humidityGaugeButton->hide();

        if (ui->humidityLabel->isVisible())
            ui->humidityLabel->hide();

        if (ui->heatGauge->isVisible())
            ui->heatGauge->hide();

        if (ui->heatGaugeButton->isVisible())
            ui->heatGaugeButton->hide();

        if (ui->heatLabel->isVisible())
            ui->heatLabel->hide();

        if (ui->doorStepLabel->isHidden())
            ui->doorStepLabel->show();

        if (ui->cookStepAnimation->isHidden())
            ui->cookStepAnimation->show();

        if (showingStep.type != lastViewDoorType)
        {
            lastViewDoorType = showingStep.type;

            ui->doorStepLabel->setText(Define::name(showingStep.type));
            ui->cookStepAnimation->clear();
            switch (showingStep.type)
            {
            case Define::PutThermometer:
                ui->doorStepLabel->setText("중심 온도계 삽입");
                ui->cookStepAnimation->load(":/images/animation/thermometer_01.png");
                ui->cookStepAnimation->load(":/images/animation/thermometer_02.png");
                ui->cookStepAnimation->setGeometry((900-210)/2, 800, 210, 307);
                ui->cookStepAnimation->start(300);
                break;
            case Define::Load:
                ui->doorStepLabel->setText("식재료 적재");
                ui->cookStepAnimation->load(":/images/animation/load_01.png");
                ui->cookStepAnimation->load(":/images/animation/load_02.png");
                ui->cookStepAnimation->load(":/images/animation/load_03.png");
                ui->cookStepAnimation->load(":/images/animation/load_04.png");
                ui->cookStepAnimation->load(":/images/animation/load_03.png");
                ui->cookStepAnimation->load(":/images/animation/load_02.png");
                ui->cookStepAnimation->load(":/images/animation/load_01.png");
                ui->cookStepAnimation->setGeometry((900-264)/2, 800, 264, 307);
                ui->cookStepAnimation->start(300);
                break;
            case Define::Cut:
                ui->doorStepLabel->setText("자르기");
                ui->cookStepAnimation->load(":/images/animation/cut_01.png");
                ui->cookStepAnimation->load(":/images/animation/cut_02.png");
                ui->cookStepAnimation->load(":/images/animation/cut_03.png");
                ui->cookStepAnimation->setGeometry((900-264)/2, 800, 264, 307);
                ui->cookStepAnimation->start(300);
                break;
            case Define::Pour:
                ui->doorStepLabel->setText("물 붓기");
                ui->cookStepAnimation->load(":/images/animation/pour_01.png");
                ui->cookStepAnimation->load(":/images/animation/pour_02.png");
                ui->cookStepAnimation->load(":/images/animation/pour_03.png");
                ui->cookStepAnimation->load(":/images/animation/pour_04.png");
                ui->cookStepAnimation->setGeometry((900-264)/2, 800, 264, 307);
                ui->cookStepAnimation->start(300);
                break;
            default:
                ui->doorStepLabel->hide();
                ui->cookStepAnimation->hide();
            }
        }
    }
    else
    {
        if (ui->doorStepLabel->isVisible())
            ui->doorStepLabel->hide();

        if (ui->cookStepAnimation->isVisible())
            ui->cookStepAnimation->hide();

        if (ui->cookStepIcon->isHidden())
            ui->cookStepIcon->show();

        if (ui->cookStepLabel->isHidden())
            ui->cookStepLabel->show();

        if (ui->cookModeIcon->isHidden())
            ui->cookModeIcon->show();

        if (ui->humidityGauge->isHidden())
            ui->humidityGauge->show();

        if (ui->humidityGaugeButton->isHidden())
            ui->humidityGaugeButton->show();

        if (ui->humidityLabel->isHidden())
            ui->humidityLabel->show();

        if (ui->heatGauge->isHidden())
            ui->heatGauge->show();

        if (ui->heatGaugeButton->isHidden())
            ui->heatGaugeButton->show();

        if (ui->heatLabel->isHidden())
            ui->heatLabel->show();

        if (showingStep.type != lastViewCookType)
        {
            lastViewCookType = showingStep.type;

            ui->cookStepIcon->setPixmap(Define::icon(showingStep.type));
            ui->cookStepLabel->setText(Define::name(showingStep.type));
        }

        if (showingStep.mode != lastViewCookMode)
        {
            lastViewCookMode = showingStep.mode;

            switch (showingStep.mode)
            {
            case Define::SteamMode:
                ui->cookModeIcon->setPixmap(steamModeIcon);
                break;
            case Define::CombiMode:
                ui->cookModeIcon->setPixmap(combiModeIcon);
                break;
            case Define::DryMode:
                ui->cookModeIcon->setPixmap(dryModeIcon);
                break;
            case Define::InvalidClass:
                ui->cookModeIcon->hide();
                break;
            }
        }

        int humidity;
        if (showingCurrentHumidity)
            humidity = oven->currentHumidity();
        else
            humidity = showingStep.humidity;

        if (humidity != lastViewHumidity)
        {
            lastViewHumidity = humidity;
            ui->humidityLabel->setText(QString("%1%").arg(humidity));
            ui->humidityGauge->setValue(humidity);
        }

        int temp;
        if (showingCurrentTemp)
            temp = oven->currentTemp();
        else
            temp = showingStep.temp;

        if (temp != lastViewTemp)
        {
            lastViewTemp = temp;
            ui->heatLabel->setText(Stringer::temperature(temp));
            ui->heatGauge->setValue(temp);
        }
    }
}

void AutoCookWindow::checkCook()
{
    int lastStepIndex = autocook.currentStepIndex;
    if (autocook.advance())
    {
        if (lastStepIndex != autocook.currentStepIndex)
            selectedStepIndex = autocook.currentStepIndex;

        if (autocook.done())
            checkCookTimer.stop();
    }

    updateView();
}

void AutoCookWindow::startProcess(int process)
{
    if (processSelected)
        return;

    processSelected = true;

    Define::Process p = (Define::Process) process;
    switch (p)
    {
    case Define::CookAgain:
    {
        close();

        AutoCookWindow *w = new AutoCookWindow(parentWidget(), cook);
        w->setWindowModality(Qt::WindowModal);
        w->showFullScreen();
        w->raise();

        break;
    }
    case Define::MakeCrisper:
    {
        selectedProcess = (Define::Process) process;

        CookStep &step = autocook.cook.steps[autocook.cook.steps.size() - 1];

        CookStep newStep = step;
        newStep.type = Define::MakeCrispy;
        newStep.humidity = 0;
        newStep.temp += 10;
        newStep.time = 0;
        newStep.humidification = 0;
        newStep.dehumidification = 0;
        autocook.cook.steps.append(newStep);
        autocook.currentStepIndex = autocook.cook.steps.size() - 1;
        ui->stepIndicator->setMaximum(autocook.cook.steps.size() - 1);
        returnToCurrentStep();

        Oven *oven = Oven::getInstance();
        oven->setHumidity(0);
        oven->setTemp(step.temp + 10);
        oven->startCooking();

        checkProcessTimer.start(100);
        break;
    }
    case Define::KeepWarm:
    {
        processSelected = false;
        selectedProcess = (Define::Process) process;

        KeepWarmPopup *p = new KeepWarmPopup(this);
        p->showFullScreen();
        p->raise();
        break;
    }
    default:
        return;
    }
}

void AutoCookWindow::checkProcess()
{
    if (!processSelected)
        return;

    switch (selectedProcess)
    {
    case Define::MakeCrisper:
    {
        Oven *oven = Oven::getInstance();
        if (oven->currentTemp() >= oven->temp())
        {
            oven->stopCooking();
            checkProcessTimer.stop();
            processSelected = false;
        }
        break;
    }
    default:
        break;
    }

    updateView();
}

void AutoCookWindow::returnToCurrentStep()
{
    selectedStepIndex = autocook.currentStepIndex;
    showingDifferentStep = false;
    updateView();
}

void AutoCookWindow::showCurrentHumidity()
{
    showingCurrentHumidity = true;
    updateView();
}

void AutoCookWindow::showCurrentTemp()
{
    showingCurrentTemp = true;
    updateView();
}

void AutoCookWindow::addFavorite()
{
    AutoCookSetting s;
    s.type = cook.type;
    s.root = cook.root;
    for (int i = 0; i < 5; i++)
        s.configs[i] = cook.configs[i].current;

    FavoriteNamePopup *p = new FavoriteNamePopup(this, s);
    p->showFullScreen();
}

void AutoCookWindow::on_humidityGaugeButton_pressed()
{
    showCurrentHumidityTimer.start();
}

void AutoCookWindow::on_humidityGaugeButton_released()
{
    if (showCurrentHumidityTimer.isActive())
        showCurrentHumidityTimer.stop();

    if (showingCurrentHumidity)
    {
        showingCurrentHumidity = false;
        updateView();
    }
}

void AutoCookWindow::on_heatGaugeButton_pressed()
{
    showCurrentTempTimer.start();
}

void AutoCookWindow::on_heatGaugeButton_released()
{
    if (showCurrentTempTimer.isActive())
        showCurrentTempTimer.stop();

    if (showingCurrentTemp)
    {
        showingCurrentTemp = false;
        updateView();
    }
}

void AutoCookWindow::on_backButton_clicked()
{
    Oven::getInstance()->stop();
    close();
}

void AutoCookWindow::on_showPrevStepButton_clicked()
{
    returnToCurrentStepTimer.start();

    if (selectedStepIndex > 0)
    {
        selectedStepIndex--;
        updateView();
    }
}

void AutoCookWindow::on_showNextStepButton_clicked()
{
    returnToCurrentStepTimer.start();

    if (selectedStepIndex + 1 < autocook.cook.steps.size())
    {
        selectedStepIndex++;
        updateView();
    }
}

void AutoCookWindow::on_favoritesButton_clicked()
{
    if (!autocook.done())
        return;

    ConfirmPopup *p = new ConfirmPopup(this, tr("즐겨찾기 항목에 추가하시겠습니까?"));
    p->showFullScreen();

    connect(p, SIGNAL(accepted()), SLOT(addFavorite()));
}