#pragma once

#include <fstream>

#include <graphene/surface_mesh/data_structure/Surface_mesh.h>
#include <graphene/surface_mesh/algorithms/surface_mesh_tools/misc.h>

namespace graphene {
namespace surface_mesh {

//
// API
//

static bool load_vertexweighting(Surface_mesh* mesh, const std::string& filename, const std::string& property_name, bool multiply=true);
static bool load_vertexweighting(Surface_mesh* mesh, const std::string& filename, const std::string& filename_mapping, const std::string& property_name, bool multiply=true);
static bool clear_vertexweighting(Surface_mesh* mesh, const std::string& property_name);


//
// Implementation
//

static bool load_vertexweighting(Surface_mesh* mesh, const std::string& filename, const std::string& property_name, bool multiply)
{
    if (mesh == NULL)
    {
        std::cerr << "load_vertexweighting: [ERROR] No template mesh available." << std::endl;
        return false;
    }

    std::ifstream ifs( filename.c_str() );
    if( ifs.is_open() != true )
    {
        std::cerr << "load_vertexweighting: [ERROR] Cannot load weighting from file:\"" << filename << "\"." << std::endl;
        return false;
    }

    //perform sanity check before load
    //count lines and check if #lines == #vertices of mesh
    size_t n_lines = 0;
    std::string line;
    while (ifs)
    {
        std::getline(ifs, line);
        if (line.empty())
            break;

        ++n_lines;
    }

    if (n_lines != mesh->n_vertices())
    {
        std::cerr << "load_vertexweighting: [ERROR] Wrong number of vertexweightings " << n_lines << " vs " << mesh->n_vertices() << "." << std::endl;
        return false;
    }

    Surface_mesh::Vertex_property<double> property_of_interest = mesh->vertex_property<double>(property_name.c_str(), 1.0);

    //clear and rewind to beginning of file
    ifs.clear();
    ifs.seekg(0);

    double value;

    if (multiply)
    {
        for (auto v : mesh->vertices())
        {
            ifs >> value;
            if (ifs.eof()) break;
            property_of_interest[v] *= value;
        }
    }
    else
    {
        for (auto v : mesh->vertices())
        {
            ifs >> value;
            if (ifs.eof()) break;
            property_of_interest[v] = value;
        }
    }
    return true;
}

//-----------------------------------------------------------------------------

static bool load_vertexweighting(Surface_mesh* mesh, const std::string& filename, const std::string& filename_mapping, const std::string& property_name, bool multiply)
{
    if (mesh == NULL)
    {
        std::cerr << "load_vertexweighting: [ERROR] No template mesh available." << std::endl;
        return false;
    }

    // load mapping
    std::vector<int> mapping_vec;
    if (!graphene::surface_mesh::load_selection_from_file(mapping_vec, filename_mapping))
    {
        std::cerr << "[ERROR] While loading mapping." << std::endl;
        return false;
    }

    std::ifstream ifs( filename.c_str() );
    if( ifs.is_open() != true )
    {
        std::cerr << "load_vertexweighting: [ERROR] Cannot load weighting from file:\"" << filename << "\"." << std::endl;
        return false;
    }

    // load values
    std::vector<double> values_vec;
    double value;
    while (true)
    {
        ifs >> value;
        if (!ifs) break;

        values_vec.push_back(value);
    }

    //perform sanity check
    //#valid_mappingss == #vertices of mesh
    size_t n_lines = 0;
    for (unsigned int i = 0; i < mapping_vec.size(); ++i)
    {
        if (mapping_vec[i] == -1)
        {
            continue;
        }

        ++n_lines;
    }
    if (n_lines != mesh->n_vertices())
    {
        std::cerr << "load_vertexweighting: [ERROR] Wrong number of valid mappings " << n_lines << " vs " << mesh->n_vertices() << "." << std::endl;
        return false;
    }
    if (mapping_vec.size() != values_vec.size())
    {
        std::cerr << "load_vertexweighting: [ERROR] Can't load vertexweighting: mapping_vec.size() != values_vec.size()." << std::endl;
        return false;
    }

    Surface_mesh::Vertex_property<double> property_of_interest = mesh->vertex_property<double>(property_name.c_str(), 1.0);

    if (multiply)
    {
        for (unsigned int i = 0; i < mapping_vec.size(); ++i)
        {
            const int map = mapping_vec[ i ];

            if (map != -1)
            {
                property_of_interest[Surface_mesh::Vertex(map)] *= values_vec[i];
            }
        }
    }
    else
    {
        for (unsigned int i = 0; i < mapping_vec.size(); ++i)
        {
            const int map = mapping_vec[ i ];

            if (map != -1)
            {
                property_of_interest[Surface_mesh::Vertex(map)] = values_vec[i];
            }
        }
    }

    return true;
}

//-----------------------------------------------------------------------------

static bool clear_vertexweighting(Surface_mesh* mesh, const std::string& property_name)
{
    if (mesh == NULL)
    {
        std::cerr << "clear_vertexweighting: [ERROR] No template mesh available." << std::endl;
        return false;
    }

    Surface_mesh::Vertex_property<double> property_of_interest = mesh->get_vertex_property<double>(property_name.c_str());

    if (property_of_interest)
    {
        mesh->remove_vertex_property<double>(property_of_interest);
    }

    return true;
}


} // namespace graphene
} // namespace surface_mesh
