/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_PLY_IO_H
#define _GCTL_PLY_IO_H

#include "../poly/triangle.h"
#include "file_io.h"

namespace gctl
{
    template <typename E, typename A>
    void read_ply_ascii(std::string filename, array<vertex<point3dc, E>> &nodes, 
        array<type_triangle<A>> &facets)
    {
        std::ifstream infile;
        open_infile(infile, filename, ".ply");

        int n, f;
        std::string line;
        std::stringstream ss;
        while (std::getline(infile, line))
        {
            if (sscanf(line.c_str(), "element vertex %d", &n)==1) nodes.resize(n);
            if (sscanf(line.c_str(), "element face %d", &f)==1) facets.resize(f);
            if (line == "end_header")
            {
                if (nodes.empty() || facets.empty())
                {
                    infile.close();
                    throw std::runtime_error("[gctl::read_ply_ascii] Invalid node or facet sizes.");
                }

                for (size_t i = 0; i < nodes.size(); ++i)
                {
                    std::getline(infile, line);
                    sscanf(line.c_str(), "%lf %lf %lf", &nodes[i].x, &nodes[i].y, &nodes[i].z);
                    nodes[i].id = i;
                }

                for (size_t i = 0; i < facets.size(); ++i)
                {
                    std::getline(infile, line);
                    int d, v1, v2, v3;
                    sscanf(line.c_str(), "%d %d %d %d", &d, &v1, &v2, &v3);
                    facets[i].vert[0] = &nodes[v1];
                    facets[i].vert[1] = &nodes[v2];
                    facets[i].vert[2] = &nodes[v3];
                    facets[i].id = i;
                }
                break;
            }
        }

        infile.close();
        return;
    }

    template <typename E, typename A>
    void save_ply_ascii(std::string filename, const array<vertex<point3dc, E>> &nodes, 
        const array<type_triangle<A>> &facets)
    {
        time_t now = time(0);
        char* dt = ctime(&now);

        std::ofstream outfile;
        open_outfile(outfile, filename, ".ply");

        outfile << "ply" << std::endl;
        outfile << "format ascii 1.0" << std::endl;
        outfile << "comment Created by GCTL at " << dt;
        outfile << "element vertex " << nodes.size() << std::endl;
        outfile << "property float x" << std::endl;
        outfile << "property float y" << std::endl;
        outfile << "property float z" << std::endl;
        outfile << "element face " << facets.size() << std::endl;
        outfile << "property list uchar int vertex_indices" << std::endl;
        outfile << "end_header" << std::endl;

        for (size_t i = 0; i < nodes.size(); ++i)
        {
            outfile << nodes[i].x << " " << nodes[i].y << " " << nodes[i].z << std::endl;
        }

        for (size_t i = 0; i < facets.size(); ++i)
        {
            outfile << "3 " << facets[i].vert[0]->id << " " << facets[i].vert[1]->id << " " << facets[i].vert[2]->id << std::endl;
        }
        outfile.close();
        return;
    }

    template <typename E, typename A>
    void read_ply_binary(std::string filename, array<vertex<point3dc, E>> &nodes, 
        array<type_triangle<A>> &facets)
    {
        std::ifstream infile;
        open_infile(infile, filename, ".ply", std::ios::binary);

        int n, f;
        std::string line;
        std::stringstream ss;
        while (std::getline(infile, line))
        {
            if (sscanf(line.c_str(), "element vertex %d", &n)==1) nodes.resize(n);
            if (sscanf(line.c_str(), "element face %d", &f)==1) facets.resize(f);
            if (line == "end_header")
            {
                if (nodes.empty() || facets.empty())
                {
                    infile.close();
                    throw std::runtime_error("[gctl::read_ply_ascii] Invalid node or facet sizes.");
                }

                float x, y, z;
                for (size_t i = 0; i < nodes.size(); ++i)
                {
                    infile.read((char*)&x, sizeof(float));
                    infile.read((char*)&y, sizeof(float));
                    infile.read((char*)&z, sizeof(float));
                    nodes[i].x = x;
                    nodes[i].y = y;
                    nodes[i].z = z;
                    nodes[i].id = i;
                }

                unsigned char num_vertices;
                int v1, v2, v3;
                for (size_t i = 0; i < facets.size(); ++i)
                {
                    infile.read((char*)&num_vertices, sizeof(unsigned char));
                    infile.read((char*)&v1, sizeof(int));
                    infile.read((char*)&v2, sizeof(int));
                    infile.read((char*)&v3, sizeof(int));
                    facets[i].vert[0] = &nodes[v1];
                    facets[i].vert[1] = &nodes[v2];
                    facets[i].vert[2] = &nodes[v3];
                    facets[i].id = i;
                }
                break;
            }
        }

        infile.close();
        return;
    }

    template <typename E, typename A>
    void save_ply_binary(std::string filename, const array<vertex<point3dc, E>> &nodes, 
        const array<type_triangle<A>> &facets)
    {
        std::ofstream outfile;
        open_outfile(outfile, filename, ".ply", std::ios::binary);

        outfile << "ply\n"
                << "format binary_little_endian 1.0\n"
                << "comment Created by GCTL\n"
                << "element vertex " << nodes.size() << "\n"
                << "property float x\n"
                << "property float y\n"
                << "property float z\n"
                << "element face " << facets.size() << "\n"
                << "property list uchar int vertex_indices\n"
                << "end_header\n";

        float x, y, z;
        for (size_t i = 0; i < nodes.size(); ++i)
        {
            x = nodes[i].x;
            y = nodes[i].y;
            z = nodes[i].z;
            outfile.write((char*)&x, sizeof(float));
            outfile.write((char*)&y, sizeof(float));
            outfile.write((char*)&z, sizeof(float));
        }

        unsigned char num_vertices = 3;
        for (size_t i = 0; i < facets.size(); ++i)
        {
            outfile.write((char*)&num_vertices, sizeof(unsigned char));
            outfile.write((char*)&facets[i].vert[0]->id, sizeof(int));
            outfile.write((char*)&facets[i].vert[1]->id, sizeof(int));
            outfile.write((char*)&facets[i].vert[2]->id, sizeof(int));
        }
        outfile.close();
        return;
    }
};

#endif // _GCTL_PLY_IO_H