#include "SPData.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include "misc/miscFunctions.hpp"
#include "vtkForCenos/Interpolator.h"
#include "cenos_exception.h"

SPData::SPData()
{

}

SPData::~SPData()
{

}

SPData::SPData(std::string filename)
{
    if (!misc::fileExist(filename)) 
        return;

    ryml::Tree tree = misc::parseYaml(filename);

    if (tree["Time"] == nullptr )
    {
        throw cenos_exception("Internal error: Data required for steel phase calculation not found");
    }

    tree["Domain"] >> domain_name;
    tree["Time"] >> time;
    if (tree["NodeNr"] != nullptr)
    {
        tree["NodeNr"] >> node_nr;
        tree["NodeX"] >> node_x;
        tree["NodeY"] >> node_y;
        tree["NodeZ"] >> node_z;
    }


    if (tree["AusteniteFraction"] != nullptr)
        tree["AusteniteFraction"] >> austenite_fraction;

    if (tree["BainiteFraction"] != nullptr)
        tree["BainiteFraction"] >> bainite_fraction;

    if (tree["StartTime"] != nullptr)
        tree["StartTime"] >> start_time;

    if (tree["RealTime"] != nullptr)
        tree["RealTime"] >> real_time;

    if (tree["NodeActivity"] != nullptr)
        tree["NodeActivity"] >> node_activity;

    if (tree["MartensiteFraction"] != nullptr)
        tree["MartensiteFraction"] >> martensite_fraction;
}

SPData& SPData::operator= (const SPData& that)
{
    // do the copy
    domain_name = that.domain_name;
    time = that.time;
    node_nr = that.node_nr;
    node_x = that.node_x;
    node_y = that.node_y;
    node_z = that.node_z;

    real_time = that.real_time;
    austenite_fraction = that.austenite_fraction;
    bainite_fraction = that.bainite_fraction;
    martensite_fraction = that.martensite_fraction;
    start_time = that.start_time;
    real_time = that.real_time;
    node_activity = that.node_activity;

    return *this;
}

void SPData::addNodes(std::vector< std::shared_ptr<Node >> nodes, double newtime)
{
    // do interpolation if does not match!
    std::vector<int> new_node_nr;
    std::vector<double> new_node_x;
    std::vector<double> new_node_y;
    std::vector<double> new_node_z;

    for (auto nd : nodes)
    {
        new_node_nr.push_back(nd->getId());
        new_node_x.push_back(nd->x());
        new_node_y.push_back(nd->y());
        new_node_z.push_back(nd->z());
 
    }

    if (newtime - time < 0 )
    {

        //do the assignement, SPData does not match this calculation and will not be used
        node_nr = new_node_nr;
        node_x = new_node_x;
        node_y = new_node_y;
        node_z = new_node_z;
        austenite_fraction = {};
        bainite_fraction = {};
        martensite_fraction = {};
        start_time = {};
        real_time = {};
        node_activity = {};
    }
    else
    {
        //do the interpolation if needed, SPData is from previous time step

        // is this check enough? Maybe compare coordinates?
       // std::cout << "node_nr " << node_nr.size() << " new_node_nr " << new_node_nr.size() << std::endl;
      //  if (node_nr == new_node_nr)
      //      return;

        std::vector<std::vector<double>> old_points;
        for (int i = 0; i < node_x.size(); i++)
            old_points.push_back({ node_x[i], node_y[i], node_z[i] });

        std::vector<std::vector<double>> new_points;
        for (int i = 0; i < new_node_x.size(); i++)
            new_points.push_back({ new_node_x[i], new_node_y[i], new_node_z[i] });

        
        if (!austenite_fraction.empty())
        {
            Interpolator austenite_interpolator;
            austenite_interpolator.setOldPoints(old_points);
            austenite_interpolator.setNewPoints(new_points);
            austenite_interpolator.setOldData(austenite_fraction);
            austenite_fraction = austenite_interpolator.getInterpolatedData();
        }


        if (!bainite_fraction.empty())
        {
            Interpolator bainite_interpolator;
            bainite_interpolator.setOldPoints(old_points);
            bainite_interpolator.setNewPoints(new_points);
            bainite_interpolator.setOldData(bainite_fraction);
            bainite_fraction = bainite_interpolator.getInterpolatedData();
        }

        if (!martensite_fraction.empty())
        {
            Interpolator martensite_interpolator;
            martensite_interpolator.setOldPoints(old_points);
            martensite_interpolator.setNewPoints(new_points);
            martensite_interpolator.setOldData(martensite_fraction);
            martensite_fraction = martensite_interpolator.getInterpolatedData();
        }

        if (!start_time.empty())
        {
            Interpolator starttime_interpolator;
            starttime_interpolator.setOldPoints(old_points);
            starttime_interpolator.setNewPoints(new_points);
            starttime_interpolator.setOldData(start_time);
            start_time = starttime_interpolator.getInterpolatedData();
        }

        if (!real_time.empty())
        {
            Interpolator realtime_interpolator;
            realtime_interpolator.setOldPoints(old_points);
            realtime_interpolator.setNewPoints(new_points);
            realtime_interpolator.setOldData(real_time);
            real_time = realtime_interpolator.getInterpolatedData();
        }

        if (!node_activity.empty())
        {
            Interpolator nodeactivity_interpolator;
            nodeactivity_interpolator.setOldPoints(old_points);
            nodeactivity_interpolator.setNewPoints(new_points);
            nodeactivity_interpolator.setOldData(node_activity);
            node_activity = nodeactivity_interpolator.getInterpolatedData();
        }


        node_nr = new_node_nr;
        node_x = new_node_x;
        node_y = new_node_y;
        node_z = new_node_z;
    }
}


void SPData::writeData(std::string filename)
{
    ryml::Tree tree;
    ryml::NodeRef r = tree.rootref();
    r |= ryml::MAP;
    r["Domain"] << domain_name.c_str();
    r["Time"] << time;
    r["NodeNr"] << node_nr;
    r["NodeX"] << node_x;
    r["NodeY"] << node_y;
    r["NodeZ"] << node_z;
    r["AusteniteFraction"] << austenite_fraction;
    r["BainiteFraction"] << bainite_fraction;
    r["MartensiteFraction"] << martensite_fraction;
    r["StartTime"] << start_time;
    r["RealTime"] << real_time;
    r["NodeActivity"] << node_activity;

    FILE* yaml_file;

    char buffer[100];

    yaml_file = fopen(filename.c_str(), "w");

    if (yaml_file == NULL)
    {
        std::string message = "Error opening " + filename + " file";
        throw cenos_exception(message);
    }

    emit(tree, yaml_file);


    fclose(yaml_file);
}


std::string SPData::getDomainName()
{
    return domain_name;
}

double SPData::getTime()
{
    return time;
}



std::vector<double> SPData::getAusteniteFraction()
{
    return austenite_fraction;
}

std::vector<double> SPData::getBainiteFraction()
{
    return bainite_fraction;
}

std::vector<double> SPData::getMartensiteFraction()
{
    return martensite_fraction;
}

std::vector<double> SPData::getRealTime()
{
    return real_time;
}

std::vector<double> SPData::getNodeActivity()
{
    return node_activity;
}

std::vector<int> SPData::getNodeNr()
{
    return node_nr;
}

std::vector<double> SPData::getStartTime()
{
    return start_time;
}

void SPData::setAusteniteFraction(std::vector<double> af)
{
    austenite_fraction = af;
}

void SPData::setBainiteFraction(std::vector<double> bf)
{
    bainite_fraction = bf;
}

void SPData::setMartensiteFraction(std::vector<double> mf)
{
    martensite_fraction = mf;
}

void SPData::setStartTime(std::vector<double> st)
{
    start_time = st;
}

void SPData::setRealTime(std::vector<double> rt)
{
    real_time = rt;
}

void SPData::setNodeActivity(std::vector<double> n_a)
{
    node_activity = n_a;
}

void SPData::setTime(double t)
{
    time = t;
}

void SPData::setName(std::string n)
{
    domain_name = n;
}

