#include "cookhistory.h"

#include "autocookwindow.h"
#include "manualcookwindow.h"
#include "manualcooksettingwidget.h"
#include "autocooksettingwidget.h"
#include "cookbook.h"

namespace {

Define::CookType toCookType(QString type)
{
    if (type == "poutry")
        return Define::Poultry;
    if (type == "meat")
        return Define::Meat;
    if (type == "fish")
        return Define::Fish;
    if (type == "desert")
        return Define::Desert;
    if (type == "vegetable")
        return Define::Vegetable;
    if (type == "bread")
        return Define::Bread;
    if (type == "etc")
        return Define::Etc;

    return Define::InvalidCookType;
}

QString toString(Define::CookType type)
{
    switch (type)
    {
    case Define::Poultry:
        return "poultry";
    case Define::Meat:
        return "meat";
    case Define::Fish:
        return "fish";
    case Define::Desert:
        return "desert";
    case Define::Vegetable:
        return "vegetable";
    case Define::Bread:
        return "bread";
    case Define::Etc:
        return "etc";
    default:
        return QString();
    }
}

Define::Mode toMode(QString mode)
{
    if (mode == "steam")
        return Define::SteamMode;
    if (mode == "combi")
        return Define::CombiMode;
    if (mode == "dry")
        return Define::DryMode;

    return Define::InvalidMode;
}

QString toString(Define::Mode mode)
{
    switch (mode)
    {
    case Define::SteamMode:
        return "steam";
    case Define::CombiMode:
        return "combi";
    case Define::DryMode:
        return "dry";
    default:
        return QString();
    }
}

QString name(Define::CookType type, QString root)
{
    CookBook book(type);
    return book.name(root);
}

}

namespace {

const int maxMostCooked = 20;
const int maxRecents = 20;
const int maxFavorites = 20;

struct CountsEntry
{
    QString root;
    Define::CookType type;
    int count;
    int configs[5];
};

struct RecentsEntry
{
    enum Type { Manual, Auto } type;
    struct {
        Define::CookType type;
        QString root;
        int configs[5];
    } autoCook;
    struct {
        Define::Mode mode;
        int humidity;
        int temp;
        int time;
        int fan;
        int coreTemp;
        QDateTime cookedTime;
    } manualCook;

    bool operator==(const RecentsEntry &other);
};

struct FavoritesEntry
{
    int id;
    QString name;
    enum Type { Manual, Auto } type;
    struct {
        Define::CookType type;
        QString root;
        int configs[5];
    } autoCook;
    struct {
        Define::Mode mode;
        int humidity;
        int temp;
        int time;
        int fan;
        int coreTemp;
    } manualCook;

    bool operator==(const FavoritesEntry &other);
};

QMap<QString, CountsEntry> countMap;
QList<CountsEntry> countList;
QList<RecentsEntry> recentList;
QList<FavoritesEntry> favoriteList;

QTimer saveCounts;
QTimer saveRecents;
QTimer saveFavorites;

bool RecentsEntry::operator==(const RecentsEntry &other)
{
    if (type != other.type)
        return false;

    switch (type)
    {
    case Manual:
        if (manualCook.mode != other.manualCook.mode)
            return false;
        if (manualCook.humidity != other.manualCook.humidity)
            return false;
        if (manualCook.temp != other.manualCook.temp)
            return false;
        if (manualCook.time != other.manualCook.time)
            return false;
        if (manualCook.fan != other.manualCook.fan)
            return false;
        if (manualCook.coreTemp != other.manualCook.coreTemp)
            return false;
        if (manualCook.cookedTime != other.manualCook.cookedTime)
            return false;
        return true;
    case Auto:
        if (autoCook.type != other.autoCook.type)
            return false;
        if (autoCook.root != other.autoCook.root)
            return false;
        return true;
    default:
        return false;
    }
}

bool FavoritesEntry::operator==(const FavoritesEntry &other)
{
    return id == other.id;
}

void sortCounts()
{
    countList.clear();
    foreach (CountsEntry e, countMap)
    {
        bool inserted = false;
        for (int i = 0; i < countList.size(); i++)
            if (countList.at(i).count < e.count)
            {
                inserted = true;
                countList.insert(i, e);
                break;
            }

        if (!inserted)
            countList.append(e);

        while (countList.size() > maxMostCooked)
            countList.takeLast();
    }
}

void readCounts()
{
    QFile file("/prime/history/counts.csv");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "File not found: " + file.fileName();
        return;
    }

    QString errorMessage = QString("%3: %1, line %2").arg(file.fileName());
    int lineCount = 0;
    while (!file.atEnd())
    {
        lineCount++;

        QString line = QString::fromUtf8(file.readLine()).trimmed();
        if (line.isEmpty())
            continue;

        QString error = errorMessage.arg(lineCount);

        QString root = line.section(',', 0, 0).trimmed();
        if (root.isEmpty())
        {
            qDebug() << error.arg("Invalid root");
            continue;
        }

        QString type = line.section(',', 1, 1).trimmed();
        if (type.isEmpty())
        {
            qDebug() << error.arg("Invalid type");
            continue;
        }

        QString count = line.section(',', 2, 2).trimmed();
        if (count.isEmpty())
        {
            qDebug() << error.arg("Invalid count");
            continue;
        }

        QString config1 = line.section(',', 3, 3).trimmed();
        if (config1.isEmpty())
        {
            qDebug() << error.arg("Invalid config1");
            continue;
        }

        QString config2 = line.section(',', 4, 4).trimmed();
        if (config2.isEmpty())
        {
            qDebug() << error.arg("Invalid config2");
            continue;
        }

        QString config3 = line.section(',', 5, 5).trimmed();
        if (config3.isEmpty())
        {
            qDebug() << error.arg("Invalid config3");
            continue;
        }

        QString config4 = line.section(',', 6, 6).trimmed();
        if (config4.isEmpty())
        {
            qDebug() << error.arg("Invalid config4");
            continue;
        }

        QString config5 = line.section(',', 7, 7).trimmed();
        if (config5.isEmpty())
        {
            qDebug() << error.arg("Invalid config5");
            continue;
        }

        CountsEntry e;
        e.root = root;

        e.type = toCookType(type);
        if (e.type == Define::InvalidCookType)
        {
            qDebug() << error.arg("Invalid type");
            continue;
        }

        bool ok;
        e.count = count.toInt(&ok);
        if (!ok)
        {
            qDebug() << error.arg("Invalid count");
            continue;
        }

        e.configs[0] = config1.toInt(&ok);
        if (!ok)
        {
            qDebug() << error.arg("Invalid config1");
            continue;
        }

        e.configs[1] = config2.toInt(&ok);
        if (!ok)
        {
            qDebug() << error.arg("Invalid config2");
            continue;
        }

        e.configs[2] = config3.toInt(&ok);
        if (!ok)
        {
            qDebug() << error.arg("Invalid config3");
            continue;
        }

        e.configs[3] = config4.toInt(&ok);
        if (!ok)
        {
            qDebug() << error.arg("Invalid config4");
            continue;
        }

        e.configs[4] = config5.toInt(&ok);
        if (!ok)
        {
            qDebug() << error.arg("Invalid config5");
            continue;
        }

        countMap.insert(root, e);
    }

    sortCounts();
}

void writeCounts()
{
    qDebug() << "Writing counts";
    QFile file("/prime/history/counts.csv");
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << "File not opened: " + file.fileName();
        return;
    }

    QTextStream stream(&file);
    foreach (CountsEntry e, countMap)
    {
        stream << e.root << "," << toString(e.type) << "," << e.count;
        for (int i = 0; i < 5; i++)
            stream << "," << e.configs[i];

        stream << "\n";
    }
}

void appendRecent(RecentsEntry e)
{
    if (e.type == RecentsEntry::Auto)
    {
        for (int i = 0; i < recentList.size(); i++)
        {
            RecentsEntry entry = recentList.at(i);
            if (entry.autoCook.root == e.autoCook.root)
            {
                recentList.removeAt(i);
                break;
            }
        }
    }

    recentList.prepend(e);

    while (recentList.size() > maxRecents)
        recentList.takeLast();
}

void readRecents()
{
    QFile file("/prime/history/recents.csv");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "File not found: " + file.fileName();
        return;
    }

    QString errorMessage = QString("%3: %1, line %2").arg(file.fileName());
    int lineCount = 0;
    while (!file.atEnd())
    {
        lineCount++;

        QString line = QString::fromUtf8(file.readLine()).trimmed();
        if (line.isEmpty())
            continue;

        QString error = errorMessage.arg(lineCount);

        RecentsEntry e;

        QString type = line.section(',', 0, 0).trimmed();
        if (type.isEmpty())
        {
            qDebug() << error.arg("Invalid type");
            continue;
        }

        if (type == "manual")
        {
            QString mode = line.section(',', 1, 1).trimmed();
            if (mode.isEmpty())
            {
                qDebug() << error.arg("Invalid mode");
                continue;
            }

            QString humidity = line.section(',', 2, 2).trimmed();
            if (humidity.isEmpty())
            {
                qDebug() << error.arg("Invalid humidity");
                continue;
            }

            QString temp = line.section(',', 3, 3).trimmed();
            if (temp.isEmpty())
            {
                qDebug() << error.arg("Invalid temp");
                continue;
            }

            QString time = line.section(',', 4, 4).trimmed();
            if (time.isEmpty())
            {
                qDebug() << error.arg("Invalid time");
                continue;
            }

            QString fan = line.section(',', 5, 5).trimmed();
            if (fan.isEmpty())
            {
                qDebug() << error.arg("Invalid fan");
                continue;
            }

            QString coreTemp = line.section(',', 6, 6).trimmed();
            if (coreTemp.isEmpty())
            {
                qDebug() << error.arg("Invalid coreTemp");
                continue;
            }

            QString cookedTime = line.section(',', 7, 7).trimmed();
            if (cookedTime.isEmpty())
            {
                qDebug() << error.arg("Invalid cookedTime");
                continue;
            }

            e.type = RecentsEntry::Manual;

            e.manualCook.mode = toMode(mode);
            if (e.manualCook.mode == Define::InvalidMode)
            {
                qDebug() << error.arg("Invalid mode");
                continue;
            }

            bool ok;
            e.manualCook.humidity = humidity.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid humidity");
                continue;
            }

            e.manualCook.temp = temp.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid temp");
                continue;
            }

            e.manualCook.time = time.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid time");
                continue;
            }

            e.manualCook.fan = fan.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid fan");
                continue;
            }

            e.manualCook.coreTemp = coreTemp.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid coreTemp");
                continue;
            }

            e.manualCook.cookedTime = QDateTime::fromString(cookedTime);
            if (!e.manualCook.cookedTime.isValid())
            {
                qDebug() << error.arg("Invalid cookedTime");
                continue;
            }
        }
        else if (type == "auto")
        {
            QString type = line.section(',', 1, 1).trimmed();
            if (type.isEmpty())
            {
                qDebug() << error.arg("Invalid cooktype");
                continue;
            }

            QString root = line.section(',', 2, 2).trimmed();
            if (root.isEmpty())
            {
                qDebug() << error.arg("Invalid root");
                continue;
            }

            QString config1 = line.section(',', 3, 3).trimmed();
            if (config1.isEmpty())
            {
                qDebug() << error.arg("Invalid config1");
                continue;
            }

            QString config2 = line.section(',', 4, 4).trimmed();
            if (config2.isEmpty())
            {
                qDebug() << error.arg("Invalid config2");
                continue;
            }

            QString config3 = line.section(',', 5, 5).trimmed();
            if (config3.isEmpty())
            {
                qDebug() << error.arg("Invalid config3");
                continue;
            }

            QString config4 = line.section(',', 6, 6).trimmed();
            if (config4.isEmpty())
            {
                qDebug() << error.arg("Invalid config4");
                continue;
            }

            QString config5 = line.section(',', 7, 7).trimmed();
            if (config5.isEmpty())
            {
                qDebug() << error.arg("Invalid config5");
                continue;
            }

            e.type = RecentsEntry::Auto;

            e.autoCook.type = toCookType(type);
            if (e.autoCook.type == Define::InvalidCookType)
            {
                qDebug() << error.arg("Invalid cooktype");
                continue;
            }

            e.autoCook.root = root;

            bool ok;
            e.autoCook.configs[0] = config1.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config1");
                continue;
            }

            e.autoCook.configs[1] = config2.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config2");
                continue;
            }

            e.autoCook.configs[2] = config3.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config3");
                continue;
            }

            e.autoCook.configs[3] = config4.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config4");
                continue;
            }

            e.autoCook.configs[4] = config5.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config5");
                continue;
            }
        }
        else
            continue;

        appendRecent(e);
    }
}

void writeRecents()
{
    qDebug() << "Writing recents";
    QFile file("/prime/history/recents.csv");
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << "File not opened: " + file.fileName();
        return;
    }

    QTextStream stream(&file);
    foreach (RecentsEntry e, recentList)
    {
        switch (e.type)
        {
        case RecentsEntry::Manual:
            stream << "manual,"
                   << toString(e.manualCook.mode) << ","
                   << e.manualCook.humidity << ","
                   << e.manualCook.temp << ","
                   << e.manualCook.time << ","
                   << e.manualCook.fan << ","
                   << e.manualCook.coreTemp << ","
                   << e.manualCook.cookedTime.toString() << "\n";
            break;
        case RecentsEntry::Auto:
            stream << "auto,"
                   << toString(e.autoCook.type) << ","
                   << e.autoCook.root;
            for (int i = 0; i < 5; i++)
                stream << "," << e.autoCook.configs[i];

            stream << "\n";
            break;
        }
    }
}

void appendFavorite(FavoritesEntry e)
{
    favoriteList.prepend(e);
    while (favoriteList.size() > maxFavorites)
        favoriteList.takeLast();
}

int newFavoriteId()
{
    for (int i = 0; i < 10000; i++)
    {
        bool absent = true;
        foreach (FavoritesEntry e, favoriteList)
            if (e.id == i)
            {
                absent = false;
                break;
            }

        if (absent)
            return i;
    }

    return -1;
}

void readFavorites()
{
    QFile file("/prime/history/favorites.csv");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "File not found: " + file.fileName();
        return;
    }

    QString errorMessage = QString("%3: %1, line %2").arg(file.fileName());
    int lineCount = 0;
    while (!file.atEnd())
    {
        lineCount++;

        QString line = QString::fromUtf8(file.readLine()).trimmed();
        if (line.isEmpty())
            continue;

        QString error = errorMessage.arg(lineCount);

        FavoritesEntry e;

        QString id = line.section(',', 0, 0).trimmed();
        if (id.isEmpty())
        {
            qDebug() << error.arg("Invalid id");
            continue;
        }

        QString name = line.section(',', 1, 1).trimmed();
        if (name.isEmpty())
        {
            qDebug() << error.arg("Invalid name");
            continue;
        }

        bool ok;
        e.id = id.toInt(&ok);
        if (!ok)
        {
            qDebug() << error.arg("Invalid id");
            continue;
        }

        e.name = name.replace("\\c", ",");

        QString type = line.section(',', 2, 2).trimmed();
        if (type.isEmpty())
        {
            qDebug() << error.arg("Invalid type");
            continue;
        }

        if (type == "manual")
        {
            QString mode = line.section(',', 3, 3).trimmed();
            if (mode.isEmpty())
            {
                qDebug() << error.arg("Invalid mode");
                continue;
            }

            QString humidity = line.section(',', 4, 4).trimmed();
            if (humidity.isEmpty())
            {
                qDebug() << error.arg("Invalid humidity");
                continue;
            }

            QString temp = line.section(',', 5, 5).trimmed();
            if (temp.isEmpty())
            {
                qDebug() << error.arg("Invalid temp");
                continue;
            }

            QString time = line.section(',', 6, 6).trimmed();
            if (time.isEmpty())
            {
                qDebug() << error.arg("Invalid time");
                continue;
            }

            QString fan = line.section(',', 7, 7).trimmed();
            if (fan.isEmpty())
            {
                qDebug() << error.arg("Invalid fan");
                continue;
            }

            QString coreTemp = line.section(',', 8, 8).trimmed();
            if (coreTemp.isEmpty())
            {
                qDebug() << error.arg("Invalid coreTemp");
                continue;
            }

            e.type = FavoritesEntry::Manual;

            e.manualCook.mode = toMode(mode);
            if (e.manualCook.mode == Define::InvalidMode)
            {
                qDebug() << error.arg("Invalid mode");
                continue;
            }

            bool ok;
            e.manualCook.humidity = humidity.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid humidity");
                continue;
            }

            e.manualCook.temp = temp.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid temp");
                continue;
            }

            e.manualCook.time = time.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid time");
                continue;
            }

            e.manualCook.fan = fan.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid fan");
                continue;
            }

            e.manualCook.coreTemp = coreTemp.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid coreTemp");
                continue;
            }
        }
        else if (type == "auto")
        {
            QString type = line.section(',', 3, 3).trimmed();
            if (type.isEmpty())
            {
                qDebug() << error.arg("Invalid cooktype");
                continue;
            }

            QString root = line.section(',', 4, 4).trimmed();
            if (root.isEmpty())
            {
                qDebug() << error.arg("Invalid root");
                continue;
            }

            QString config1 = line.section(',', 5, 5).trimmed();
            if (config1.isEmpty())
            {
                qDebug() << error.arg("Invalid config1");
                continue;
            }

            QString config2 = line.section(',', 6, 6).trimmed();
            if (config2.isEmpty())
            {
                qDebug() << error.arg("Invalid config2");
                continue;
            }

            QString config3 = line.section(',', 7, 7).trimmed();
            if (config3.isEmpty())
            {
                qDebug() << error.arg("Invalid config3");
                continue;
            }

            QString config4 = line.section(',', 8, 8).trimmed();
            if (config4.isEmpty())
            {
                qDebug() << error.arg("Invalid config4");
                continue;
            }

            QString config5 = line.section(',', 9, 9).trimmed();
            if (config5.isEmpty())
            {
                qDebug() << error.arg("Invalid config5");
                continue;
            }

            e.type = FavoritesEntry::Auto;

            e.autoCook.type = toCookType(type);
            if (e.autoCook.type == Define::InvalidCookType)
            {
                qDebug() << error.arg("Invalid cooktype");
                continue;
            }

            e.autoCook.root = root;

            bool ok;
            e.autoCook.configs[0] = config1.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config1");
                continue;
            }

            e.autoCook.configs[1] = config2.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config2");
                continue;
            }

            e.autoCook.configs[2] = config3.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config3");
                continue;
            }

            e.autoCook.configs[3] = config4.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config4");
                continue;
            }

            e.autoCook.configs[4] = config5.toInt(&ok);
            if (!ok)
            {
                qDebug() << error.arg("Invalid config5");
                continue;
            }
        }
        else
            continue;

        if (favoriteList.size() < maxFavorites)
            favoriteList.append(e);
    }
}

void writeFavorites()
{
    qDebug() << "Writing favorites";
    QFile file("/prime/history/favorites.csv");
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        qDebug() << "File not opened: " + file.fileName();
        return;
    }

    QTextStream stream(&file);
    stream.setCodec("UTF-8");
    foreach (FavoritesEntry e, favoriteList)
    {
        stream << e.id << "," << e.name.replace(",", "\\c").toUtf8() << ",";
        switch (e.type)
        {
        case FavoritesEntry::Manual:
            stream << "manual,"
                   << toString(e.manualCook.mode) << ","
                   << e.manualCook.humidity << ","
                   << e.manualCook.temp << ","
                   << e.manualCook.time << ","
                   << e.manualCook.fan << ","
                   << e.manualCook.coreTemp << "\n";
            break;
        case FavoritesEntry::Auto:
            stream << "auto,"
                   << toString(e.autoCook.type) << ","
                   << e.autoCook.root;
            for (int i = 0; i < 5; i++)
                stream << "," << e.autoCook.configs[i];

            stream << "\n";
            break;
        }
    }
}

bool initialized = false;
void initialize()
{
    initialized = true;

    readCounts();
    readRecents();
    readFavorites();

    saveCounts.setSingleShot(true);
    saveCounts.setInterval(1 * 1000);
    QObject::connect(&saveCounts, &QTimer::timeout, writeCounts);

    saveRecents.setSingleShot(true);
    saveRecents.setInterval(1 * 1000);
    QObject::connect(&saveRecents, &QTimer::timeout, writeRecents);

    saveFavorites.setSingleShot(true);
    saveFavorites.setInterval(1 * 1000);
    QObject::connect(&saveFavorites, &QTimer::timeout, writeFavorites);
}

void checkInitialized()
{
    if (!initialized)
        initialize();
}

}

void CookHistory::record(ManualCookSetting cook)
{
    checkInitialized();

    RecentsEntry e;
    e.type = RecentsEntry::Manual;
    e.manualCook.mode = cook.mode;
    e.manualCook.humidity = cook.humidity;
    e.manualCook.temp = cook.temp;
    e.manualCook.time = cook.time;
    e.manualCook.fan = cook.fan;
    e.manualCook.coreTemp = cook.coreTempEnabled ? cook.coreTemp : -1;
    e.manualCook.cookedTime = QDateTime::currentDateTime();

    appendRecent(e);
    saveRecents.start();
}

void CookHistory::record(AutoCookSetting cook)
{
    checkInitialized();

    RecentsEntry e;
    e.type = RecentsEntry::Auto;
    e.autoCook.type = cook.type;
    e.autoCook.root = cook.root;

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

    appendRecent(e);
    saveRecents.start();

    if (countMap.contains(cook.root))
    {
        CountsEntry &e = countMap[cook.root];
        e.count++;

        for (int i = 0; i < 5; i++)
            e.configs[i] = cook.configs[i];
    }
    else
    {
        CountsEntry e;
        e.type = cook.type;
        e.root = cook.root;

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

        e.count = 1;

        countMap.insert(cook.root, e);
    }

    sortCounts();
    saveCounts.start();
}

int CookHistory::addFavorite(ManualCookSetting cook, QString name)
{
    checkInitialized();

    FavoritesEntry e;
    e.id = newFavoriteId();
    e.name = name;
    e.type = FavoritesEntry::Manual;
    e.manualCook.mode = cook.mode;
    e.manualCook.humidity = cook.humidity;
    e.manualCook.temp = cook.temp;
    e.manualCook.time = cook.time;
    e.manualCook.fan = cook.fan;
    e.manualCook.coreTemp = cook.coreTempEnabled ? cook.coreTemp : -1;

    appendFavorite(e);
    saveFavorites.start();

    return e.id;
}

int CookHistory::addFavorite(AutoCookSetting cook, QString name)
{
    checkInitialized();

    FavoritesEntry e;
    e.id = newFavoriteId();
    e.name = name;
    e.type = FavoritesEntry::Auto;
    e.autoCook.type = cook.type;
    e.autoCook.root = cook.root;

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

    appendFavorite(e);
    saveFavorites.start();

    return e.id;
}

QList<CookRecord> CookHistory::listMostCooked()
{
    checkInitialized();

    QList<CookRecord> list;
    foreach (CountsEntry e, countList)
    {
        CookRecord r;
        r.type = CookRecord::Auto;
        r.name = name(e.type, e.root);
        r.autoRecord.setting.name = r.name;
        r.autoRecord.setting.root = e.root;
        r.autoRecord.setting.type = e.type;

        for (int i = 0; i < 5; i++)
            r.autoRecord.setting.configs[i] = e.configs[i];

        list.append(r);
    }

    return list;
}

QList<CookRecord> CookHistory::listRecents()
{
    checkInitialized();

    QList<CookRecord> list;
    foreach (RecentsEntry e, recentList)
    {
        CookRecord r;

        switch (e.type)
        {
        case RecentsEntry::Auto:
            r.type = CookRecord::Auto;
            r.name = name(e.autoCook.type, e.autoCook.root);
            r.autoRecord.setting.name = r.name;
            r.autoRecord.setting.type = e.autoCook.type;
            r.autoRecord.setting.root = e.autoCook.root;

            for (int i = 0; i < 5; i++)
                r.autoRecord.setting.configs[i] = e.autoCook.configs[i];

            break;
        case RecentsEntry::Manual:
            r.type = CookRecord::Manual;
            r.name = e.manualCook.cookedTime.toString("yy.MM.dd HH:mm:ss");
            switch (e.manualCook.mode)
            {
            case Define::SteamMode:
                r.name += " - " + QObject::tr("스팀");
                break;
            case Define::CombiMode:
                r.name += " - " + QObject::tr("콤비");
                break;
            case Define::DryMode:
                r.name += " - " + QObject::tr("건열");
                break;
            default:
                break;
            }

            r.manualRecord.cookedTime = e.manualCook.cookedTime;
            r.manualRecord.setting.mode = e.manualCook.mode;
            r.manualRecord.setting.humidity = e.manualCook.humidity;
            r.manualRecord.setting.temp = e.manualCook.temp;
            r.manualRecord.setting.time = e.manualCook.time;
            r.manualRecord.setting.fan = e.manualCook.fan;
            r.manualRecord.setting.coreTempEnabled = e.manualCook.coreTemp > 0;
            r.manualRecord.setting.coreTemp = e.manualCook.coreTemp;
            break;
        default:
            continue;
        }

        list.append(r);
    }

    return list;
}

QList<CookRecord> CookHistory::listFavorites()
{
    checkInitialized();

    QList<CookRecord> list;
    foreach (FavoritesEntry e, favoriteList)
    {
        CookRecord r;
        r.id = e.id;
        r.name = e.name;

        switch (e.type)
        {
        case FavoritesEntry::Auto:
            r.type = CookRecord::Auto;
            r.autoRecord.setting.name = name(e.autoCook.type, e.autoCook.root);
            r.autoRecord.setting.type = e.autoCook.type;
            r.autoRecord.setting.root = e.autoCook.root;

            for (int i = 0; i < 5; i++)
                r.autoRecord.setting.configs[i] = e.autoCook.configs[i];

            break;
        case FavoritesEntry::Manual:
            r.type = CookRecord::Manual;
            r.manualRecord.setting.mode = e.manualCook.mode;
            r.manualRecord.setting.humidity = e.manualCook.humidity;
            r.manualRecord.setting.temp = e.manualCook.temp;
            r.manualRecord.setting.time = e.manualCook.time;
            r.manualRecord.setting.fan = e.manualCook.fan;
            r.manualRecord.setting.coreTempEnabled = e.manualCook.coreTemp > 0;
            r.manualRecord.setting.coreTemp = e.manualCook.coreTemp;
            break;
        default:
            continue;
        }

        list.append(r);
    }

    return list;
}

QPixmap CookHistory::render(CookRecord record)
{
    if (record.type == CookRecord::Manual)
    {
        ManualCookSettingWidget *w = new ManualCookSettingWidget(record.manualRecord.setting);

        QPixmap p = w->grab().scaledToHeight(630, Qt::SmoothTransformation);

        w->deleteLater();

        return p;
    }
    else if (record.type == CookRecord::Auto)
    {
        AutoCookSettingWidget *w = new AutoCookSettingWidget(record.autoRecord.setting);

        QPixmap p = w->grab().scaledToHeight(630, Qt::SmoothTransformation);

        w->deleteLater();

        return p;
    }

    return QPixmap();
}

void CookHistory::start(CookRecord record, QWidget *parent)
{
    if (record.type == CookRecord::Manual)
    {
        ManualCookWindow *w = new ManualCookWindow(parent, record.manualRecord.setting);
        w->setWindowModality(Qt::WindowModal);
        w->showFullScreen();
        w->raise();
    }
    else if (record.type == CookRecord::Auto)
    {
        Cook cook(record.autoRecord.setting.type, record.autoRecord.setting.root, record.autoRecord.setting.name);
        int *configs = record.autoRecord.setting.configs;
        cook.setConfig(configs[0],
                       configs[1],
                       configs[2],
                       configs[3],
                       configs[4]);

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

void CookHistory::removeMostCooked(CookRecord record)
{
    if (countMap.contains(record.autoRecord.setting.root))
    {
        countMap.remove(record.autoRecord.setting.root);
        sortCounts();

        saveCounts.start();
    }
}

void CookHistory::removeRecent(CookRecord record)
{
    RecentsEntry e;
    switch (record.type)
    {
    case CookRecord::Manual:
        e.type = RecentsEntry::Manual;
        e.manualCook.mode = record.manualRecord.setting.mode;
        e.manualCook.humidity = record.manualRecord.setting.humidity;
        e.manualCook.temp = record.manualRecord.setting.temp;
        e.manualCook.time = record.manualRecord.setting.time;
        e.manualCook.fan = record.manualRecord.setting.fan;
        e.manualCook.coreTemp = record.manualRecord.setting.coreTempEnabled ? record.manualRecord.setting.coreTemp : -1;
        e.manualCook.cookedTime = record.manualRecord.cookedTime;
        break;
    case CookRecord::Auto:
        e.type = RecentsEntry::Auto;
        e.autoCook.type = record.autoRecord.setting.type;
        e.autoCook.root = record.autoRecord.setting.root;
        break;
    default:
        return;
    }

    recentList.removeAll(e);

    saveRecents.start();
}

void CookHistory::removeFavorite(CookRecord record)
{
    FavoritesEntry e;
    e.id = record.id;

    favoriteList.removeAll(e);

    saveFavorites.start();
}