#include "HELIX_IO.h"
#include <iostream>
#include <fstream>
#include <cstdlib>

bool writeMultSupHelixStrand(const vector<vector<superHelix>>& strands, 
                          const vector<VEC3>& roots, const vector<VEC3>& startTans, const char* filename)
{
    std::ofstream s(filename);
    if (!s.is_open())
    {
        std::cerr << "writeMultSupHelixParams cannot open at: " << filename << std::endl;
        return false;
    }

    if(strands.empty())
    {
        std::cerr << "strands is empty" << std::endl;
        return false;
    }
    assert(strands.size() == roots.size() && roots.size() == startTans.size());

    for(unsigned int i = 0; i < strands.size(); i++)
    {
        const vector<superHelix>& curStrand = strands[i];
        s << "p " << roots[i][0] << " " << roots[i][1] << " " << roots[i][2] << endl;
        s << "t " << startTans[i][0] << " " << startTans[i][1] << " " << startTans[i][2] << endl;
        for(unsigned int j = 0; j < curStrand.size(); j++)
        {
            const superHelix& h = curStrand[j];
            s << "sh " << h.darboux[0] << " " << h.darboux[1] << " " << h.darboux[2] << " " 
                      << h.sQ 
                      << endl;
        }
    }
    return true;
}

bool writeBasicSupHelixStrand(const vector<superHelix>& strand, 
                          const VEC3& root, const VEC3& startTan, const char* filename)
{
    std::ofstream s(filename);
    if (!s.is_open())
    {
        std::cerr << "writeSingleSupHelixParams cannot open at: " << filename << std::endl;
        return false;
    }

    if(strand.empty())
    {
        std::cerr << "strand is empty" << std::endl;
        return false;
    }

    s << "p " << root[0] << " " << root[1] << " " << root[2] << endl;
    s << "t " << startTan[0] << " " << startTan[1] << " " << startTan[2] << endl;

    for(unsigned int i = 0; i < strand.size(); i++)
    {
            const superHelix& h = strand[i];
            s << "sh " << h.darboux[0] << " " << h.darboux[1] << " " << h.darboux[2] << " " 
                      << h.sQ 
                      << endl;
    }
    return true;
}

bool readMultSupHelixStrand(const char* filename, vector<vector<superHelix>>& strands,
                          vector<VEC3>& roots, vector<VEC3>& tans)
{
    roots.clear();
    tans.clear();
    strands.clear();
    
    std::ifstream ifs(filename);
    if(!ifs.good())
    {
        std::cerr << "readMultSupHelixParams cannot read file at path: " << filename << std::endl;
        return false;
    }

    const std::string p("p");
    const std::string t("t");
    const std::string h("sh");
    vector<superHelix> curStrand;

    for(std::string line; std::getline(ifs, line);)
    {
        std::istringstream iss(line);
        std::string key;
        iss >> key;
        if(key == p)
        {
            double x, y, z;
            iss >> x >> y >> z;
            roots.emplace_back(x, y, z);
            if(!curStrand.empty())
            {
                strands.push_back(curStrand);
                curStrand.clear();
            }
        }
        if(key == t)
        {
            double x, y, z;
            iss >> x >> y >> z;
            tans.emplace_back(x,y,z);
        }
        if(key == h)
        {
            double x, y, z, sQ;
            iss >> x >> y >> z >> sQ;
            superHelix curHelix;
            curHelix.darboux = VEC3(x,y,z);
            curHelix.sQ = sQ;
            curStrand.push_back(curHelix);
        }
    }
    strands.push_back(curStrand);
    return true;
}

bool readBasicSupHelixStrand(const char* filename, vector<superHelix>& strand,
                          VEC3& root, VEC3& tans)
{
    strand.clear();
    
    std::ifstream ifs(filename);
    if(!ifs.good())
    {
        std::cerr << "readBasicSupHelixParams cannot read file at path: " << filename << std::endl;
        return false;
    }

    const std::string p("p");
    const std::string t("t");
    const std::string h("sh");

    for(std::string line; std::getline(ifs, line);)
    {
        std::istringstream iss(line);
        std::string key;
        iss >> key;
        if(key == p)
        {
            double x, y, z;
            iss >> x >> y >> z;
            root = VEC3(x, y, z);
        }
        if(key == t)
        {
            double x, y, z;
            iss >> x >> y >> z;
            tans = VEC3(x,y,z);
        }
        if(key == h)
        {
            double x, y, z, sQ;
            iss >> x >> y >> z >> sQ;
            superHelix curHelix;
            curHelix.darboux = VEC3(x,y,z);
            curHelix.sQ = sQ;
            strand.push_back(curHelix);
        }
    }
    return true;
}

bool writeMultLocalSupHelixStrand(const vector<vector<superHelix>>& strands, 
                          const vector<VEC3>& roots, const vector<MATRIX3>& startFrames, const char* filename)
{
    std::ofstream s(filename);
    if (!s.is_open())
    {
        std::cerr << "writeMultLocalSupHelixParams cannot open at: " << filename << std::endl;
        return false;
    }

    if(strands.empty())
    {
        std::cerr << "strands is empty" << std::endl;
        return false;
    }
    assert(strands.size() == roots.size() && roots.size() == startFrames.size());

    for(unsigned int i = 0; i < strands.size(); i++)
    {
        const vector<superHelix>& curStrand = strands[i];
        s << "p " << roots[i][0] << " " << roots[i][1] << " " << roots[i][2] << endl;
        s << "f " << startFrames[i](0,0) << " " << startFrames[i](0,1) << " " << startFrames[i](0,2) << " "
                  << startFrames[i](1,0) << " " << startFrames[i](1,1) << " " << startFrames[i](1,2) << " "
                  << startFrames[i](2,0) << " " << startFrames[i](2,1) << " " << startFrames[i](2,2) << endl;
        for(unsigned int j = 0; j < curStrand.size(); j++)
        {
            const superHelix& h = curStrand[j];
            s << "sh " << h.darboux[0] << " " << h.darboux[1] << " " << h.darboux[2] << " " 
                      << h.sQ 
                      << endl;
        }
    }
    return true;
}

bool writeBasicLocalSupHelixStrand(const vector<superHelix>& helixParams, 
                            const VEC3& root, const MATRIX3& startFrame, const char* filename)
{
    std::ofstream s(filename);
    if (!s.is_open())
    {
        std::cerr << "writeLocalSingleSupHelixParams cannot open at: " << filename << std::endl;
        return false;
    }

    if(helixParams.empty())
    {
        std::cerr << "strand is empty" << std::endl;
        return false;
    }

    s << "p " << root[0] << " " << root[1] << " " << root[2] << endl;
    s << "f " << startFrame(0,0) << " " << startFrame(0,1) << " " << startFrame(0,2) << " "
              << startFrame(1,0) << " " << startFrame(1,1) << " " << startFrame(1,2) << " "
              << startFrame(2,0) << " " << startFrame(2,1) << " " << startFrame(2,2) << endl;

    for(unsigned int i = 0; i < helixParams.size(); i++)
    {
            const superHelix& h = helixParams[i];
            s << "sh " << h.darboux[0] << " " << h.darboux[1] << " " << h.darboux[2] << " " 
                      << h.sQ 
                      << endl;
    }
    return true;
}

bool readMultLocalSupHelixStrand(const char* filename, vector<vector<superHelix>>& strands,
                          vector<VEC3>& roots, vector<MATRIX3>& frames)
{
    roots.clear();
    frames.clear();
    strands.clear();
    
    std::ifstream ifs(filename);
    if(!ifs.good())
    {
        std::cerr << "readMultLocalSupHelixParams cannot read file at path: " << filename << std::endl;
        return false;
    }

    const std::string p("p");
    const std::string f("f");
    const std::string h("sh");
    vector<superHelix> curStrand;

    for(std::string line; std::getline(ifs, line);)
    {
        std::istringstream iss(line);
        std::string key;
        iss >> key;
        if(key == p)
        {
            double x, y, z;
            iss >> x >> y >> z;
            roots.emplace_back(x, y, z);
            if(!curStrand.empty())
            {
                strands.push_back(curStrand);
                curStrand.clear();
            }
        }
        if(key == f)
        {
            double zz, zo, zt,
                 oz, oo, ot,
                 tz, to, tt;
            iss >> zz >> zo >> zt >> oz >> oo >> ot >> tz >> to >> tt;
            MATRIX3 nf;
            nf << zz, zo, zt, oz, oo, ot, tz, to, tt;
            frames.push_back(nf);
        }
        if(key == h)
        {
            double x, y, z, sQ;
            iss >> x >> y >> z >> sQ;
            superHelix curHelix;
            curHelix.darboux = VEC3(x,y,z);
            curHelix.sQ = sQ;
            curStrand.push_back(curHelix);
        }
    }
    strands.push_back(curStrand);
    return true;

}

bool readBasicLocalSupHelixStrand(const char* filename, vector<superHelix>& strand,
                          VEC3& root, MATRIX3& frame)
{
    strand.clear();
    
    std::ifstream ifs(filename);
    if(!ifs.good())
    {
        std::cerr << "readBasicSupHelixParams cannot read file at path: " << filename << std::endl;
        return false;
    }

    const std::string p("p");
    const std::string f("f");
    const std::string h("sh");

    for(std::string line; std::getline(ifs, line);)
    {
        std::istringstream iss(line);
        std::string key;
        iss >> key;
        if(key == p)
        {
            double x, y, z;
            iss >> x >> y >> z;
            root = VEC3(x, y, z);
        }
        if(key == f)
        {
            double zz, zo, zt,
                 oz, oo, ot,
                 tz, to, tt;
            iss >> zz >> zo >> zt >> oz >> oo >> ot >> tz >> to >> tt;
            frame << zz, zo, zt, oz, oo, ot, tz, to, tt;
        }
        if(key == h)
        {
            double x, y, z, sQ;
            iss >> x >> y >> z >> sQ;
            superHelix curHelix;
            curHelix.darboux = VEC3(x,y,z);
            curHelix.sQ = sQ;
            strand.push_back(curHelix);
        }
    }
    return true;

}