#include "ElmerMesh_IO.h"
#include "cenos_exception.h"
#include "misc/miscFunctions.hpp"


ElmerMesh_IO::ElmerMesh_IO()
{
    etype_to_elmer[1] = 202;
    etype_to_elmer[2] = 303;
    etype_to_elmer[3] = 404;
    etype_to_elmer[4] = 504;
    etype_to_elmer[5] = 808;
    etype_to_elmer[6] = 706;
    etype_to_elmer[7] = 605;
}

void ElmerMesh_IO::readMesh(std::string& filename, Mesh* mesh)
{
}

void ElmerMesh_IO::writeMesh(std::string& dirname, Mesh* mesh)
{
    misc::createFolder(dirname);
    misc::createFolder(dirname + "/meshElmer");

    writeHeader(dirname + "/meshElmer/mesh.header", mesh);
    writeNodes(dirname + "/meshElmer/mesh.nodes", mesh);
    writeElements(dirname + "/meshElmer/mesh.elements", mesh);
    writeBoundary(dirname + "/meshElmer/mesh.boundary", mesh);


}

void ElmerMesh_IO::writeHeader(std::string& filename, Mesh* mesh)
{
    std::ofstream file_stream;
    file_stream.open(filename.c_str());
    if (!file_stream.is_open())
    {
        throw cenos_exception("Elmer mesh header file cannot be opened");
    }

    int nr_of_nodes = mesh->getSizeNodes();
    int nr_of_elements = 0;
    int nr_of_boundaries = 0;
    MeshStats stats = mesh->getMeshStats();

    for (auto el : mesh->getElements())
    {
        if (el->getDimension() == mesh->getDimension())
            nr_of_elements++;
        else if (el->getDimension() == mesh->getDimension() - 1)
            nr_of_boundaries++;
    }

    file_stream << mesh->getSizeNodes() << " " << nr_of_elements << " " << nr_of_boundaries << "\n";
    file_stream << stats.total_stats.element_type_count.size() << "\n";


    for (auto etype : stats.total_stats.element_type_count)
    {
        if (etype_to_elmer.find(etype.first) == etype_to_elmer.end())
            throw cenos_exception("Element of type " + std::to_string(etype.first) + " not implemented in Elmer mesh writing");

        file_stream << etype_to_elmer[etype.first] << " " <<  etype.second << "\n";

    }
    file_stream.close();
}



void ElmerMesh_IO::writeNodes(std::string& filename, Mesh* mesh)
{
    std::ofstream file_stream;
    file_stream.open(filename.c_str());
    if (!file_stream.is_open())
    {
        throw cenos_exception("Elmer mesh node file cannot be opened");
    }

   
    int nr = 1;
    double scale = mesh->getMeshScale();
    for (auto nd : mesh->getNodes())
    {
        file_stream << nr << "  " << -1  << "  " << nd->x() * scale << "  " << nd->y() * scale << "  " << nd->z() * scale << std::endl;
        node_id_map[nd->getId()] = nr;
        nr++;
    }

    file_stream.close();
}


void ElmerMesh_IO::writeElements(std::string& filename, Mesh* mesh)
{
    std::ofstream file_stream;
    file_stream.open(filename.c_str());
    if (!file_stream.is_open())
    {
        throw cenos_exception("Elmer mesh element file cannot be opened");
    }


    int nr = 1;
    for (auto el : mesh->getElements())
    {
        if (el->getDimension() != mesh->getDimension())
            continue;

        file_stream << nr << " " << el->getOwnerEntity()->getId() << "  " << etype_to_elmer[el->getElementType()] << "  ";
        for (auto nd : el->getNodes())
        {
            file_stream << node_id_map[nd->getId()] << "  ";
        }

        file_stream << "\n";
        element_id_map[el->getId()] = nr;
        nr++;
    }

    file_stream.close();
}


void ElmerMesh_IO::writeBoundary(std::string& filename, Mesh* mesh)
{
    std::ofstream file_stream;
    file_stream.open(filename.c_str());
    if (!file_stream.is_open())
    {
        throw cenos_exception("Elmer mesh boundary file cannot be opened");
    }



    int nr = 1;
    for (auto el : mesh->getElements())
    {
        if (el->getDimension() != mesh->getDimension() - 1)
            continue;

        int el_owner_id = el->getOwnerEntity()->getId();
        

        //find attached elements
        std::map<int, int> owner_element_count;
        for (auto nd : el->getNodes())
        {
            for (auto o_el : nd->getOwnerElements())
            {
                if (owner_element_count.find(o_el->getId()) == owner_element_count.end())
                    owner_element_count[o_el->getId()] = 1;
                else
                    owner_element_count[o_el->getId()]++;
            }
        }

        std::vector<int> neighbour_ids;
        for (auto o : owner_element_count)
        {
            if (o.second == el->getTypeNodeCount())
                neighbour_ids.push_back(o.first);
        }

        if (neighbour_ids.size() == 0)
        {
            throw(cenos_exception("Free faces are not supported in Elmer mesh writer"));
        }
        else if (neighbour_ids.size() == 1)
            neighbour_ids.push_back(0);


        file_stream << nr << " " << el_owner_id << " " << element_id_map[neighbour_ids[0]]  << " "  << element_id_map[neighbour_ids[1]] << " " << etype_to_elmer[el->getElementType()] << " ";
        for (auto nd : el->getNodes())
            file_stream << node_id_map[nd->getId()] << " ";

        file_stream << "\n";
        nr++;
    }

    file_stream.close();
}