#include "GUI/Model/Job/FitParameterContainerItem.h"
#include "GUI/Model/Job/FitParameterItem.h"
#include "GUI/Model/Job/FitParameterLinkItem.h"
#include "GUI/Model/Job/FitSuiteItem.h"
#include "GUI/Model/Job/MinimizerItem.h"
#include "GUI/Model/Model/FitParameterModel.h"
#include "Tests/GTestWrapper/google_test.h"

TEST(TestFitParameterModel, InitialState)
{
    JobItem* jobItem = nullptr;
    FitSuiteItem fitSuiteItem;

    auto* container = fitSuiteItem.createFitParametersContainerItem();
    FitParameterModel proxy(dynamic_cast<FitParameterContainerItem*>(container), jobItem);

    EXPECT_EQ(0, proxy.rowCount(QModelIndex()));
    EXPECT_EQ(static_cast<int>(FitParameterModel::NUM_COLUMNS), proxy.columnCount(QModelIndex()));
    EXPECT_EQ(container, proxy.itemForIndex(QModelIndex()));
}

TEST(TestFitParameterModel, addFitParameter)
{
    JobItem* jobItem = nullptr;
    FitSuiteItem fitSuiteItem;
    auto* container = fitSuiteItem.createFitParametersContainerItem();
    FitParameterModel proxy(dynamic_cast<FitParameterContainerItem*>(container), jobItem);

    // adding fit parameter
    auto* fitPar0 = container->createBareFitParameterItem();
    fitPar0->setMinimum(1.0);
    fitPar0->setMaximum(2.0);
    fitPar0->setStartValue(3.0);

    // checking index of root
    EXPECT_EQ(1, proxy.rowCount(QModelIndex()));
    EXPECT_EQ(FitParameterModel::NUM_COLUMNS, proxy.columnCount(QModelIndex()));

    // accessing item at col=0 (original FitParameterItem)
    QModelIndex index = proxy.index(0, 0, QModelIndex());
    EXPECT_EQ(index.row(), 0);
    EXPECT_EQ(index.column(), 0);
    EXPECT_EQ(proxy.rowCount(index), 0);
    EXPECT_EQ(proxy.columnCount(index), 0); // non existing linkItem

    EXPECT_EQ(fitPar0, proxy.itemForIndex(index));
    EXPECT_EQ(fitPar0->displayName(), proxy.data(index).toString());
    EXPECT_EQ(index, proxy.indexOfItem(fitPar0));

    // accessing item at col=2
    index = proxy.index(0, FitParameterModel::COL_MIN, QModelIndex());
    EXPECT_EQ(index.row(), 0);
    EXPECT_EQ(index.column(), FitParameterModel::COL_MIN);
    EXPECT_EQ(proxy.rowCount(index), 0);
    EXPECT_EQ(proxy.columnCount(index), 0);

    EXPECT_EQ(fitPar0->minimumItem(), proxy.itemForIndex(index));
    EXPECT_EQ(fitPar0->minimum(), proxy.data(index).toDouble());
    EXPECT_EQ(index, proxy.indexOfItem(fitPar0->minimumItem()));

    // accessing item at col=3
    index = proxy.index(0, FitParameterModel::COL_VALUE, QModelIndex());
    EXPECT_EQ(index.row(), 0);
    EXPECT_EQ(index.column(), FitParameterModel::COL_VALUE);
    EXPECT_EQ(proxy.rowCount(index), 0);
    EXPECT_EQ(proxy.columnCount(index), 0);

    EXPECT_EQ(fitPar0->startValueItem(), proxy.itemForIndex(index));
    EXPECT_EQ(fitPar0->startValue(), proxy.data(index).toDouble());
    EXPECT_EQ(index, proxy.indexOfItem(fitPar0->startValueItem()));

    // accessing item at col=4
    index = proxy.index(0, FitParameterModel::COL_MAX, QModelIndex());
    EXPECT_EQ(index.row(), 0);
    EXPECT_EQ(index.column(), FitParameterModel::COL_MAX);
    EXPECT_EQ(proxy.rowCount(index), 0);
    EXPECT_EQ(proxy.columnCount(index), 0);

    EXPECT_EQ(fitPar0->maximumItem(), proxy.itemForIndex(index));
    EXPECT_EQ(fitPar0->maximum(), proxy.data(index).toDouble());
    EXPECT_EQ(index, proxy.indexOfItem(fitPar0->maximumItem()));

    // ----------------------------------------------------
    // adding second fit parameter
    // ----------------------------------------------------
    auto* fitPar1 = container->createBareFitParameterItem();
    fitPar1->setMinimum(10.0);
    fitPar1->setMaximum(20.0);
    fitPar1->setStartValue(30.0);

    // checking index of root
    EXPECT_EQ(2, proxy.rowCount(QModelIndex()));
    EXPECT_EQ(FitParameterModel::NUM_COLUMNS, proxy.columnCount(QModelIndex()));

    // accessing item at col=3 for fitPar0
    index = proxy.index(0, FitParameterModel::COL_VALUE, QModelIndex());
    EXPECT_EQ(index.row(), 0);
    EXPECT_EQ(index.column(), FitParameterModel::COL_VALUE);
    EXPECT_EQ(proxy.rowCount(index), 0);
    EXPECT_EQ(proxy.columnCount(index), 0);

    EXPECT_EQ(fitPar0->startValueItem(), proxy.itemForIndex(index));
    EXPECT_EQ(fitPar0->startValue(), proxy.data(index).toDouble());
    EXPECT_EQ(index, proxy.indexOfItem(fitPar0->startValueItem()));

    // accessing item at col=3 for fitPar1
    index = proxy.index(1, FitParameterModel::COL_VALUE, QModelIndex());
    EXPECT_EQ(index.row(), 1);
    EXPECT_EQ(index.column(), FitParameterModel::COL_VALUE);
    EXPECT_EQ(proxy.rowCount(index), 0);
    EXPECT_EQ(proxy.columnCount(index), 0);

    EXPECT_EQ(fitPar1->startValueItem(), proxy.itemForIndex(index));
    EXPECT_EQ(fitPar1->startValue(), proxy.data(index).toDouble());
    EXPECT_EQ(index, proxy.indexOfItem(fitPar1->startValueItem()));
}

TEST(TestFitParameterModel, addFitParameterAndLink)
{
    JobItem* jobItem = nullptr;
    FitSuiteItem fitSuiteItem;
    auto* container = fitSuiteItem.createFitParametersContainerItem();
    FitParameterModel proxy(dynamic_cast<FitParameterContainerItem*>(container), jobItem);

    // adding fit parameter
    auto* fitPar0 = container->createBareFitParameterItem();
    fitPar0->setMinimum(1.0);
    fitPar0->setMaximum(2.0);
    fitPar0->setStartValue(3.0);

    // adding link
    fitPar0->addLinkItem("TitleOfLink0", "link0");
    auto* link0 = fitPar0->linkItems().last();

    // checking index of root
    EXPECT_EQ(1, proxy.rowCount(QModelIndex()));
    EXPECT_EQ(FitParameterModel::NUM_COLUMNS, proxy.columnCount(QModelIndex()));

    // accessing item at col=0 (original FitParameterItem)
    QModelIndex index = proxy.index(0, 0, QModelIndex());
    EXPECT_EQ(index.row(), 0);
    EXPECT_EQ(index.column(), 0);
    EXPECT_EQ(proxy.rowCount(index), 1);
    EXPECT_EQ(proxy.columnCount(index), 1); // linkItem

    // testing link0 index
    QModelIndex linkIndex = proxy.index(0, 0, index);
    EXPECT_EQ(linkIndex.row(), 0);
    EXPECT_EQ(linkIndex.column(), 0);
    EXPECT_EQ(linkIndex.parent(), index);
    EXPECT_EQ(proxy.rowCount(linkIndex), 0);
    EXPECT_EQ(proxy.columnCount(linkIndex), 0);

    EXPECT_EQ(proxy.parent(linkIndex), index);
    EXPECT_EQ(proxy.itemForIndex(linkIndex), link0->linkItem());

    EXPECT_EQ(link0->title(), proxy.data(linkIndex).toString());
    EXPECT_EQ(linkIndex, proxy.indexOfItem(link0->linkItem()));

    // adding second link
    fitPar0->addLinkItem("TitleOfLink1", "link1");
    auto* link1 = fitPar0->linkItems().last();
    EXPECT_EQ(proxy.rowCount(index), 2);
    EXPECT_EQ(proxy.columnCount(index), 1); // linkItem

    linkIndex = proxy.index(1, 0, index);
    EXPECT_EQ(linkIndex.row(), 1);
    EXPECT_EQ(linkIndex.column(), 0);
    EXPECT_EQ(linkIndex.parent(), index);
    EXPECT_EQ(proxy.rowCount(linkIndex), 0);
    EXPECT_EQ(proxy.columnCount(linkIndex), 0);
    EXPECT_EQ(proxy.parent(linkIndex), index);

    EXPECT_EQ(proxy.parent(linkIndex), index);
    EXPECT_EQ(proxy.itemForIndex(linkIndex), link1->linkItem());
}

TEST(TestFitParameterModel, addTwoFitParameterAndLinks)
{
    JobItem* jobItem = nullptr;
    FitSuiteItem fitSuiteItem;
    auto* container = fitSuiteItem.createFitParametersContainerItem();
    FitParameterModel proxy(dynamic_cast<FitParameterContainerItem*>(container), jobItem);

    // adding fit parameters
    auto* fitPar0 = container->createBareFitParameterItem();
    fitPar0->addLinkItem("TitleOfLink0", "link0");

    auto* fitPar1 = container->createBareFitParameterItem();
    fitPar1->addLinkItem("TitleOfLink1", "link1");

    // checking index of root
    EXPECT_EQ(2, proxy.rowCount(QModelIndex()));
    EXPECT_EQ(FitParameterModel::NUM_COLUMNS, proxy.columnCount(QModelIndex()));

    // accessing fitPar1
    QModelIndex index1 = proxy.index(1, 0, QModelIndex());
    EXPECT_EQ(index1.row(), 1);
    EXPECT_EQ(index1.column(), 0);
    EXPECT_EQ(index1.parent(), QModelIndex());
    EXPECT_EQ(proxy.rowCount(index1), 1);
    EXPECT_EQ(proxy.columnCount(index1), 1);

    EXPECT_EQ(fitPar1, proxy.itemForIndex(index1));
    EXPECT_EQ(fitPar1->displayName(), proxy.data(index1).toString());
    EXPECT_EQ(index1, proxy.indexOfItem(fitPar1));

    // accessing link1
    QModelIndex linkIndex1 = proxy.index(0, 0, index1);
    EXPECT_EQ(linkIndex1.row(), 0);
    EXPECT_EQ(linkIndex1.column(), 0);
    EXPECT_EQ(linkIndex1.parent(), index1);
    EXPECT_EQ(proxy.rowCount(linkIndex1), 0);
    EXPECT_EQ(proxy.columnCount(linkIndex1), 0);
}
