/*****************************************************************************
 * $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 <pml/PhysicalModel.h>
#include <pml/MultiComponent.h>
#include <pml/StructuralComponent.h>
#include <pml/Atom.h>
#include <math.h>
#include <limits.h>

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

    if (argc != 3) {
        cout << "Usage:" << endl;
        cout << "\t" << argv[0] << " file.pml component" << endl;
        cout << "If component is made of quad and triangle, transform all quads in triangle, output to: file-triangulated.pml" << endl;
        cout << "PML " << PhysicalModel::VERSION << endl;
        exit(-1);
    }

    try {
        // read the pml
        string filename(argv[1]);
        string component(argv[2]);
        cout << "-> please wait while reading " << filename << " for triangulation" << endl;

        PhysicalModel* pm = new PhysicalModel(filename.c_str());

        // create the new component
        StructuralComponent* moore;
        moore = new StructuralComponent(pm, component);

        // get the component
        Component* cpt = pm->getComponentByName(argv[2]);
        if (!cpt) {
            throw PMLAbortException("No component named " + component);
        }
        if (!cpt->isInstanceOf("StructuralComponent") || ((StructuralComponent*)cpt)->composedBy() != StructuralComponent::CELLS) {
            throw PMLAbortException(component + " is not a structural component made of cells");
        }
        StructuralComponent* sc = (StructuralComponent*) cpt;
        StructuralComponent* newSc = new StructuralComponent(pm, component + "-triangulated");
        unsigned int triangulated = 0;
        cout << "-> please wait while triangulating..." << endl;

        // check each cell, everytime a quad is found, triangulate it
        Cell* c;
        Cell* newCell;
        for (unsigned int i = 0; i < sc->getNumberOfStructures(); i++) {
            c = sc->getCell(i);
            switch (c->getType()) {
                case StructureProperties::QUAD:
                    triangulated++;
                    /*
                    * QUAD
                    *   3--------2      lines:  triangulation:
                    *   |        |      0,1     0,1,3
                    *   |        |      1,2     1,2,3
                    *   |        |      2,3
                    *   0--------1      3,0
                    */
                    newCell = new Cell(pm, StructureProperties::TRIANGLE);
                    newCell->addStructure(c->getStructure(0));
                    newCell->addStructure(c->getStructure(1));
                    newCell->addStructure(c->getStructure(3));
                    newSc->addStructure(newCell);
                    newCell = new Cell(pm, StructureProperties::TRIANGLE);
                    newCell->addStructure(c->getStructure(1));
                    newCell->addStructure(c->getStructure(2));
                    newCell->addStructure(c->getStructure(3));
                    newSc->addStructure(newCell);
                    break;

                case StructureProperties::TRIANGLE:
                    // create a new identical cell
                    newCell = new Cell(pm, c->getType());
                    for (unsigned int j = 0; j < c->getNumberOfStructures(); j++) {
                        newCell->addStructure(c->getStructure(j));
                    }
                    newSc->addStructure(newCell);
                    break;

                default:
                    cout << "Cannot triangulate cell" << c->getIndex() << " (" << StructureProperties::toString(c->getType()) << ")" << endl;
                    break;
            }
        }

        // add the new SC to the informative component
        if (!pm->getInformativeComponents()) {
            MultiComponent* mc = new MultiComponent(pm);
            pm->setInformativeComponents(mc);
        }

        pm->getInformativeComponents()->addSubComponent(newSc);

        // save the result
        unsigned int pLast = filename.rfind(".");
        if (pLast != string::npos) {
            filename.erase(pLast);
            filename += "-triangulated.pml";
        }
        else {
            filename = "triangulated.pml";
        }

        cout << "-> please wait while saving " << filename << " " << endl;

        ofstream outputFile(filename.c_str());
        pm->setName(pm->getName() + " Triangulated");
        // do not optimize output (do not touch cell and atom id)
        pm->xmlPrint(outputFile, false);

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