/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

#include <iostream>
#include <string>
using namespace std;
#include <string.h> // strcmp

#include <pml/PhysicalModel.h>
#include <pml/StructuralComponent.h>
#include <pml/MultiComponent.h>
//#include <map>

// variables
string pmlFile;
string wrlFile;
string pgmName;
std::ofstream outputFile;
PhysicalModel* pm;
Cell* c;
bool vrml2;
vector<string> componentNames;

/** the elements classified by a rank,
* in an increasing way
*/
map <unsigned int, unsigned int> rankA;
map <unsigned int, unsigned int>::iterator it;
unsigned int globalRank;

//----------------------- processParam --------------------
void processParam(int argc, char** argv) {

    // get the name of the program
    pgmName = argv[0];

    pmlFile = "none";
    wrlFile = "";
    vrml2 = false;

    if (argc < 6) {
        cout << "Wrong number of argument (at least 4 needed):" << endl;
        cout << "pml2wrl -f document.pml -o document.wrl [-use SCName]* [-vrml2]" << endl;
        cout << "If no SC name are given, then 'Regions' is used by default." << endl;
        cout << "Default output is vrml v1.0 ASCII" << endl;
        exit(-1);
    }

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-f") == 0) { // get the input pml file name
            i++;
            pmlFile = argv[i];
            if (pmlFile == "none") {
                cout << "Argument error: -f is mandatory" << endl;
                exit(-1);
            }
        }

        if (strcmp(argv[i], "-o") == 0) { // output pdo
            i++;
            wrlFile = argv[i];
            wrlFile += ".wrl";
            // open the wrl file for writing
            outputFile.open(wrlFile.c_str());
            if (!outputFile.is_open()) {
                cout << "Argument error: cannot open file " << wrlFile << endl;
                exit(1);
            }
            // set fixed floating format
            outputFile.setf(ios::fixed, ios::floatfield);
            outputFile.precision(6);
        }

        if (strcmp(argv[i], "-use") == 0) {
            i++;
            componentNames.push_back(argv[i]);
        }

        if (strcmp(argv[i], "-vrml2") == 0) {
            vrml2 = true;
        }

    }

    if (componentNames.size() == 0) {
        componentNames.push_back("Regions");
    }

}

//----------------------- printParam ---------------------
void printParam() {
    cout << pgmName << " generating " << wrlFile << " from " << pmlFile;
    cout << " using components ";
    for (unsigned int i = 0; i < componentNames.size(); i++) {
        cout << componentNames[i] << "," ;
    }
    cout << endl;
}

//----------------------- printMaterial -----------------
void printMaterial(Cell* c) {

    double* color;

    // get the color of the region
    color = ((StructuralComponent*)c)->getColor();
    outputFile << "\t" << "Material {" << endl;

    outputFile << "\t" << "\t" << "diffuseColor " << color[0] << " " << color[1] << " " <<  color[2] << endl;
    outputFile << "\t" << "\t" << "ambientIntensity 0" << endl;
    outputFile << "\t" << "\t" << "specularColor 0.0 0.0 0.0" << endl;
    outputFile << "\t" << "\t" << "shininess 0.0 " << endl;
    outputFile << "\t" << "\t" << "transparency 0.0 " << endl;
    outputFile << "\t" << "}" << endl;
    outputFile << endl;

}

//----------------------- printCoordinates -----------------
void printCoordinates(Cell* c) {

    Atom* a;
    double pos[3];
    unsigned int index;

    for (unsigned int i = 0; i < c->getNumberOfStructures(); i++) {
        a = (Atom*)c->getStructure(i);
        // the points are indexed from 0 !!
        index = a->getIndex();
        bool isInserted = rankA.insert(pair<unsigned int, unsigned int>(index, globalRank)).second;
        if (isInserted) {
            globalRank++;
            a->getPosition(pos);
            outputFile << "\t" << "\t" << "\t" << "\t" << pos[0] << " " << pos[1] << " " << pos[2] << "," << endl;
        }
    }

    // Warning:the axes are differents with Blender
//        outputFile << "\t" << "\t" << "\t" << "\t" << pos[0] << " " << pos[2] << " " << pos[1] << "," << endl;

}



//------------------------ printSurfaces -------------------
void printSurfaces(Cell* c) {

    outputFile << "\t\t\t\t";
    for (int k = c->getNumberOfStructures() - 1; k >= 0; k--) {
        // look for the index of the elements and
        // take the corresponding rank
        it = rankA.find(c->getStructure(k)->getIndex());
        if (it == rankA.end()) {
            cout << "ERROR c#" << c->getIndex() << ": cannot find Atom#" << c->getStructure(0)->getIndex() << endl;
        }
        else {
            outputFile << it->second << ", ";
        }
    }
    outputFile  << " -1," << endl;

}

// ------------------------ Prologue for VRML2 ---------------------------
void vrml2Prologue() {
    outputFile << "# pml2wrl" << endl;
    outputFile << "    Background {\n"
               "      skyColor [1.000000 1.000000 1.000000, ]\n"
               "    }\n"
               "    Viewpoint {\n"
               "      fieldOfView 0.523599\n"
               "      position 222.803372 287.424506 199.848172\n"
               "      description \"Default View\"\n"
               "      orientation 0.590285 -0.769274 -0.244504 0.987861\n"
               "    }\n"
               "    NavigationInfo {\n"
               "      type [\"EXAMINE\",\"FLY\"]\n"
               "      speed 4.000000\n"
               "      headlight FALSE"
               "     }\n"
               "\n"
               "    DirectionalLight { ambientIntensity 1 intensity 0 # ambient light\n"
               "      color 1.000000 1.000000 1.000000 }\n"
               "\n"
               "    DirectionalLight {\n"
               "      direction -0.577350 -0.577350 -0.577350\n"
               "      color 1.000000 1.000000 1.000000\n"
               "      intensity 1.000000\n"
               "      on TRUE\n"
               "      }\n"
               "    Transform {\n"
               "      translation 0 0 0\n"
               "      rotation 0 0 1 0\n"
               "      scale 1 1 1\n"
               "      children [\n"
               "        Shape {\n"
               "          appearance Appearance {\n"
               "            material Material {\n"
               "              ambientIntensity 0\n"
               "              diffuseColor 0.8 0.2 0.2\n"
               "              specularColor 0 0 0\n"
               "              shininess 0.0078125\n"
               "              transparency 0\n"
               "              }\n"
               "            }\n"
               "          geometry IndexedFaceSet {\n"
               "            solid FALSE\n";
    outputFile << "            coord DEF pml2wrl Coordinate {" << endl;


}

void vrml1Prologue(StructuralComponent* sc) {
    outputFile << "DEF pml2wrl Separator {\n";
    printMaterial((Cell*)sc->getCell(0));
    outputFile << "\tMaterialBinding { value OVERALL }\n"
               "\tShapeHints { shapeType SOLID vertexOrdering CLOCKWISE }\n"
               "\tSeparator {\n"
               "\tCoordinate3 {\n";
}

//------------------------ main ---------------------------
int main(int argc, char** argv) {

    StructuralComponent* sc;

    processParam(argc, argv);
    printParam();

    // line mandatory
    if (vrml2) {
        outputFile << "#VRML V2.0 utf8" << endl;//"#VRML V1.0 ascii" << endl;
        outputFile << "#WARNING: pml2wrl may have inverted backface culling..." << endl;
    }
    else {
        outputFile << "#VRML V1.0 ascii" << endl;
    }

    try {
        cout << "-> Please wait while reading pml" << endl;
        pm = new PhysicalModel(pmlFile.c_str());

        cout << "-> Please wait while converting to wrl" << endl;

        for (unsigned int cptId = 0; cptId < componentNames.size(); cptId++) {
            // find the multiComponent component
            Component* cpt = pm->getComponentByName(componentNames[cptId]);
            if (!cpt) {
                cout << "Cannot find any component named \"" << componentNames[cptId] << "\". Skipping" << endl;
            }
            else {
                // check if it is a correct one
                if (!cpt->isInstanceOf("StructuralComponent")) {
                    cout << "Cannot translate the \"" << componentNames[cptId] << "\" component: not a structural component. Skipping" << endl;
                }
                else {
                    sc = (StructuralComponent*)cpt;

                    // prologue
                    if (vrml2) {
                        vrml2Prologue();
                    }
                    else {
                        vrml1Prologue(sc);
                    }

                    // nodes
                    outputFile << "\tpoint [\n";

                    // build rankA
                    globalRank = 0;
                    for (unsigned int i = 0; i < sc->getNumberOfStructures(); i++) {
                        c = (Cell*)sc->getCell(i);
                        printCoordinates(c);
                    }
                    outputFile << "\t\t\t]" << endl;
                    outputFile << "\t\t}" << endl;

                    if (!vrml2) {
                        outputFile << "\tIndexedFaceSet {" << endl;
                    }

                    // print facets
                    outputFile << "\tcoordIndex  [" << endl;
                    for (unsigned int i = 0; i < sc->getNumberOfStructures(); i++) {
                        c = (Cell*)sc->getCell(i);
                        printSurfaces(c);
                    }

                    outputFile << "\t\t\t]" << endl;
                    outputFile << "\t\t}" << endl;
                    outputFile << "\t}" << endl;

                    // end
                    if (vrml2) {
                        outputFile << "       ]" << endl;
                        outputFile << "     }" << endl;
                    }
                    else {
                        outputFile << "}" << endl;
                    }
                    outputFile << endl;
                }
            }
        }

        outputFile.close();

    }
    catch (const PMLAbortException& ae) {
        cout << "AbortException : Physical model aborted:" << endl;
        cout << ae.what() << endl;
    }


}
