/*****************************************************************************
 * $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 "SurfaceExtractor.h"

// -------------------- constructor ------------------------
SurfaceExtractor::SurfaceExtractor(PhysicalModel* pm) {
    this->pm = pm;
}

// -------------------- destructor ------------------------
SurfaceExtractor::~SurfaceExtractor() {
    for (unsigned int i = 0; i < facets.size(); i++) {
        delete facets[i];
    }
    facets.clear();
}

// -------------------- init ------------------------
void SurfaceExtractor::init() {
    for (unsigned int i = 0; i < facets.size(); i++) {
        delete facets[i];
    }
    facets.clear();
}

// -------------------- AddCell ------------------------
void SurfaceExtractor::AddCell(Cell* c) {
    switch (c->getType()) {
        case StructureProperties::TRIANGLE: {
            // TRIANGLE (as viewed from outside)
            //       1      facet:   lines:
            //      / \     0,1,2    0,1
            //     /   \             1,2
            //    2-----0            2,0
            unsigned idT[3];
            idT[0] = c->getStructure(0)->getIndex();
            idT[1] = c->getStructure(1)->getIndex();
            idT[2] = c->getStructure(2)->getIndex();
            equivalent(3, idT);
            break;
        }
        case StructureProperties::QUAD: {
            // QUAD
            //    3--------2      lines:
            //    |        |      0,1
            //    |        |      1,2
            //    |        |      2,3
            //    0--------1      3,0

            unsigned idT[4];
            idT[0] = c->getStructure(0)->getIndex();
            idT[1] = c->getStructure(1)->getIndex();
            idT[2] = c->getStructure(2)->getIndex();
            idT[3] = c->getStructure(3)->getIndex();
            equivalent(4, idT);
            break;
        }
        case StructureProperties::WEDGE: {
            // WEDGE
            //     1-------------4       facets (quad):   facets (triangles):     lines:
            //     /\           . \       2,5,4,1          0,2,1                   0,1      2,5
            //    /  \         /   \      0,1,4,3          3,4,5                   0,2      3,4
            //   0- - \ - - - 3     \     2,0,3,5                                  1,2      4,5
            //     \   \         \   \                                             0,3      5,3
            //       \ 2-----------\--5                                            1,4
            unsigned int idQ[4];
            idQ[0] = c->getStructure(2)->getIndex();
            idQ[1] = c->getStructure(5)->getIndex();
            idQ[2] = c->getStructure(4)->getIndex();
            idQ[3] = c->getStructure(1)->getIndex();
            equivalent(4, idQ);

            idQ[0] = c->getStructure(0)->getIndex();
            idQ[1] = c->getStructure(1)->getIndex();
            idQ[2] = c->getStructure(4)->getIndex();
            idQ[3] = c->getStructure(3)->getIndex();
            equivalent(4, idQ);

            idQ[0] = c->getStructure(2)->getIndex();
            idQ[1] = c->getStructure(0)->getIndex();
            idQ[2] = c->getStructure(3)->getIndex();
            idQ[3] = c->getStructure(5)->getIndex();
            equivalent(4, idQ);

            unsigned int idT[3];
            idT[0] = c->getStructure(0)->getIndex();
            idT[1] = c->getStructure(2)->getIndex();
            idT[2] = c->getStructure(1)->getIndex();
            equivalent(3, idT);

            idT[0] = c->getStructure(3)->getIndex();
            idT[1] = c->getStructure(4)->getIndex();
            idT[2] = c->getStructure(5)->getIndex();
            equivalent(3, idT);
            break;
        }
        case StructureProperties::TETRAHEDRON: {
            // tetrahedron are defined as follow:
            //                    3
            //                  /| \                  So to generate the facet list,
            //                 / |  \                 we just have to loop on all the
            //                1__|___\ 2              tetrahedron and add the corresponding 4 facets :
            //                \  |   /                f0=0,1,2      f2=0,3,1
            //                 \ |  /                 f1=0,2,3      f3=2,1,3
            //                  \|/
            //                  0
            unsigned int id[3];
            id[0] = c->getStructure(0)->getIndex();
            id[1] = c->getStructure(1)->getIndex();
            id[2] = c->getStructure(2)->getIndex();
            equivalent(3, id);

            id[0] = c->getStructure(0)->getIndex();
            id[1] = c->getStructure(2)->getIndex();
            id[2] = c->getStructure(3)->getIndex();
            equivalent(3, id);

            id[0] = c->getStructure(0)->getIndex();
            id[1] = c->getStructure(3)->getIndex();
            id[2] = c->getStructure(1)->getIndex();
            equivalent(3, id);

            id[0] = c->getStructure(2)->getIndex();
            id[1] = c->getStructure(1)->getIndex();
            id[2] = c->getStructure(3)->getIndex();
            equivalent(3, id);
            break;
        }

        case StructureProperties::HEXAHEDRON: {
            // hexahedron are defined as follow:
            //              2-------------6
            //             / \           . \      So to generate the facet list,
            //            /   \         /   \     we just have to loop on all the
            //           1- - -\ - - - 5     \    hexahedron and add the corresponding 6 facets :
            //           \     3-------------7    f0=0,3,2,1     f3=3,7,6,2
            //            \   /         \   /     f1=0,4,7,3     f4=1,2,6,5
            //             \ /           . /      f2=0,1,5,4     f5=4,5,6,7
            //              0-------------4

            unsigned int id[4];
            id[0] = c->getStructure(0)->getIndex();
            id[1] = c->getStructure(3)->getIndex();
            id[2] = c->getStructure(2)->getIndex();
            id[3] = c->getStructure(1)->getIndex();
            equivalent(4, id);

            id[0] = c->getStructure(0)->getIndex();
            id[1] = c->getStructure(4)->getIndex();
            id[2] = c->getStructure(7)->getIndex();
            id[3] = c->getStructure(3)->getIndex();
            equivalent(4, id);

            id[0] = c->getStructure(0)->getIndex();
            id[1] = c->getStructure(1)->getIndex();
            id[2] = c->getStructure(5)->getIndex();
            id[3] = c->getStructure(4)->getIndex();
            equivalent(4, id);

            id[0] = c->getStructure(3)->getIndex();
            id[1] = c->getStructure(7)->getIndex();
            id[2] = c->getStructure(6)->getIndex();
            id[3] = c->getStructure(2)->getIndex();
            equivalent(4, id);

            id[0] = c->getStructure(1)->getIndex();
            id[1] = c->getStructure(2)->getIndex();
            id[2] = c->getStructure(6)->getIndex();
            id[3] = c->getStructure(5)->getIndex();
            equivalent(4, id);

            id[0] = c->getStructure(4)->getIndex();
            id[1] = c->getStructure(5)->getIndex();
            id[2] = c->getStructure(6)->getIndex();
            id[3] = c->getStructure(7)->getIndex();
            equivalent(4, id);
            break;
        }
        default:
            std::cout << "Cannot generate sub facets for cell #" << c->getIndex() << ": it is not of known type (TRIANGLE, QUAD, WEDGE, HEXAHEDRON or TETRAHEDRON)." << std::endl;
            break;
    }
}

// -------------------- generateExternalSurface ------------------------
void SurfaceExtractor::generateExternalSurface(StructuralComponent& surface, StructuralComponent& surfacePoints) {
    std::set<int> sufSet;
    std::set<int>::iterator it;
    for (std::vector <Facet*>::iterator it = facets.begin(); it != facets.end(); ++it) {
        if ((*it)->getUsed() == 1) { // used only once
            Cell* c = (*it)->getCell(pm);
            surface.addStructure(c);
            for (unsigned int j = 0; j < c->getNumberOfStructures(); j++) {
                sufSet.insert(dynamic_cast<Atom*>(c->getStructure(j))->getIndex());
            }
        }
    }
    for (it = sufSet.begin(); it != sufSet.end(); ++it) {
        surfacePoints.addStructure(pm->getAtom(*it));
    }
}

// -------------------- equivalent ------------------------
void SurfaceExtractor::equivalent(int size, unsigned int id[]) {
    std::vector <Facet*>::iterator it;

    // look into allFacets for equivalence
    it = facets.begin();
    while (it != facets.end() && !(*it)->testEquivalence(size, id)) {
        ++it;
    }

    // not found => insert
    if (it == facets.end()) {
        facets.push_back(new Facet(size, id));
    }
}

