/*
* Copyright 2006 Sony Computer Entertainment Inc.
*
* Licensed under the MIT Open Source License, for details please see license.txt or the website
* http://www.opensource.org/licenses/mit-license.php
*
*/

#include "ColladaConditioner.h"
unsigned int ColladaConditioner::getMaxOffset( domInputLocalOffset_Array &input_array ) {

    unsigned int maxOffset = 0;
    for ( unsigned int i = 0; i < input_array.getCount(); i++ ) {
        if ( input_array[i]->getOffset() > maxOffset ) {
            maxOffset = (unsigned int)input_array[i]->getOffset();
        }
    }
    return maxOffset;
}

void ColladaConditioner::createTrianglesFromPolylist( domMesh *thisMesh, domPolylist *thisPolylist ) {

    // Create a new <triangles> inside the mesh that has the same material as the <polylist>
    domTriangles *thisTriangles = (domTriangles *)thisMesh->createAndPlace("triangles");
    //thisTriangles->setCount( 0 );
    unsigned int triangles = 0;
    thisTriangles->setMaterial(thisPolylist->getMaterial());
    domP* p_triangles = (domP*)thisTriangles->createAndPlace("p");

    // Give the new <triangles> the same <_dae> and <parameters> as the old <polylist>
    for(int i=0; i<(int)(thisPolylist->getInput_array().getCount()); i++) {

        thisTriangles->placeElement( thisPolylist->getInput_array()[i]->clone() );
    }

    // Get the number of inputs and primitives for the polygons array
    int numberOfInputs = (int)getMaxOffset(thisPolylist->getInput_array()) + 1;
    int numberOfPrimitives = (int)(thisPolylist->getVcount()->getValue().getCount());

    unsigned int offset = 0;

    // Triangulate all the primitives, this generates all the triangles in a single <p> element
    for(int j = 0; j < numberOfPrimitives; j++) {

        int triangleCount = (int)thisPolylist->getVcount()->getValue()[j] -2;
        // Write out the primitives as triangles, just fan using the first element as the base
        int idx = numberOfInputs;
        for(int k = 0; k < triangleCount; k++) {
            // First vertex
            for(int l = 0; l < numberOfInputs; l++) {

                p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + l]);
            }
            // Second vertex
            for(int l = 0; l < numberOfInputs; l++) {

                p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + idx + l]);
            }
            // Third vertex
            idx += numberOfInputs;
            for(int l = 0; l < numberOfInputs; l++) {

                p_triangles->getValue().append(thisPolylist->getP()->getValue()[offset + idx + l]);
            }
            triangles++;
        }
        offset += (unsigned int)thisPolylist->getVcount()->getValue()[j] * numberOfInputs;
    }
    thisTriangles->setCount( triangles );

}

void ColladaConditioner::createTrianglesFromPolygons( domMesh *thisMesh, domPolygons *thisPolygons ) {

    // Create a new <triangles> inside the mesh that has the same material as the <polygons>
    domTriangles *thisTriangles = (domTriangles *)thisMesh->createAndPlace("triangles");
    thisTriangles->setCount( 0 );
    thisTriangles->setMaterial(thisPolygons->getMaterial());
    domP* p_triangles = (domP*)thisTriangles->createAndPlace("p");

    // Give the new <triangles> the same <_dae> and <parameters> as the old <polygons>
    for(int i=0; i<(int)(thisPolygons->getInput_array().getCount()); i++) {

        thisTriangles->placeElement( thisPolygons->getInput_array()[i]->clone() );
    }

    // Get the number of inputs and primitives for the polygons array
    int numberOfInputs = (int)getMaxOffset(thisPolygons->getInput_array()) +1;
    int numberOfPrimitives = (int)(thisPolygons->getP_array().getCount());

    // Triangulate all the primitives, this generates all the triangles in a single <p> element
    for(int j = 0; j < numberOfPrimitives; j++) {

        // Check the polygons for consistancy (some exported files have had the wrong number of indices)
        domP * thisPrimitive = thisPolygons->getP_array()[j];
        int elementCount = (int)(thisPrimitive->getValue().getCount());
        // Skip the invalid primitive
        if((elementCount % numberOfInputs) != 0) {
            continue;
        } else {
            int triangleCount = (elementCount/numberOfInputs)-2;
            // Write out the primitives as triangles, just fan using the first element as the base
            int idx = numberOfInputs;
            for(int k = 0; k < triangleCount; k++) {
                // First vertex
                for(int l = 0; l < numberOfInputs; l++) {

                    p_triangles->getValue().append(thisPrimitive->getValue()[l]);
                }
                // Second vertex
                for(int l = 0; l < numberOfInputs; l++) {

                    p_triangles->getValue().append(thisPrimitive->getValue()[idx + l]);
                }
                // Third vertex
                idx += numberOfInputs;
                for(int l = 0; l < numberOfInputs; l++) {

                    p_triangles->getValue().append(thisPrimitive->getValue()[idx + l]);
                }
                thisTriangles->setCount(thisTriangles->getCount()+1);
            }
        }
    }

}


bool ColladaConditioner::triangulate(DAE *dae) {

    int error = 0;

    // How many geometry elements are there?
    int geometryElementCount = (int)(dae->getDatabase()->getElementCount(NULL, "geometry" ));

    for(int currentGeometry = 0; currentGeometry < geometryElementCount; currentGeometry++) {

        // Find the next geometry element
        domGeometry *thisGeometry;
        //      error = _dae->getDatabase()->getElement((daeElement**)&thisGeometry,currentGeometry, NULL, "geometry");
        daeElement * element = 0;
        error = dae->getDatabase()->getElement(&element,currentGeometry, NULL, "geometry");
        thisGeometry = (domGeometry *) element;

        // Get the mesh out of the geometry
        domMesh *thisMesh = thisGeometry->getMesh();

        if (thisMesh == NULL){
            continue;
        }

        // Loop over all the polygon elements
        for(int currentPolygons = 0; currentPolygons < (int)(thisMesh->getPolygons_array().getCount()); currentPolygons++) {

            // Get the polygons out of the mesh
            // Always get index 0 because every pass through this loop deletes the <polygons> element as it finishes with it
            domPolygons *thisPolygons = thisMesh->getPolygons_array()[currentPolygons];
            createTrianglesFromPolygons( thisMesh, thisPolygons );
        }
        while (thisMesh->getPolygons_array().getCount() > 0) {

            domPolygons *thisPolygons = thisMesh->getPolygons_array().get(0);
            // Remove the polygons from the mesh
            thisMesh->removeChildElement(thisPolygons);
        }
        int polylistElementCount = (int)(thisMesh->getPolylist_array().getCount());
        for(int currentPolylist = 0; currentPolylist < polylistElementCount; currentPolylist++) {

            // Get the polylist out of the mesh
            // Always get index 0 because every pass through this loop deletes the <polygons> element as it finishes with it
            domPolylist *thisPolylist = thisMesh->getPolylist_array()[currentPolylist];
            createTrianglesFromPolylist( thisMesh, thisPolylist );
        }
        while (thisMesh->getPolylist_array().getCount() > 0) {

            domPolylist *thisPolylist = thisMesh->getPolylist_array().get(0);
            // Remove the polylist from the mesh
            thisMesh->removeChildElement(thisPolylist);
        }
    }
    return (error == 0);
}

bool ColladaConditioner::triangulate(const char *inputFile) {

    DAE dae;
    bool convertSuceeded = true;
    domCOLLADA* root = dae.open(inputFile);

    if (!root) {
        printf("Failed to read file %s.\n", inputFile);
        return false;
    }

    convertSuceeded = triangulate(&dae);

    dae.writeAll();
    if(!convertSuceeded) {
        printf("Encountered errors\n");
    }

    return convertSuceeded;
}

bool ColladaConditioner::stripGeometry(DAE *dae) {
    bool convertSuceeded = true;
    int geometryElementCount = (int)(dae->getDatabase()->getElementCount(NULL,
                                                                         "library_geometries" ));

    for(int currentGeometry = 0; currentGeometry < geometryElementCount; currentGeometry++) {

        daeElement * element = 0;
        int error = dae->getDatabase()->getElement(&element, currentGeometry,
                                                   NULL, "library_geometries");
        daeBool removed = daeElement::removeFromParent(element);
        convertSuceeded = convertSuceeded && removed;
    }
    return convertSuceeded;
}

bool ColladaConditioner::stripGeometry(const char *inputFile) {
    DAE dae;
    bool convertSuceeded = true;
    domCOLLADA* root = dae.open(inputFile);

    if (!root) {
        printf("Failed to read file %s.\n", inputFile);
        return false;
    }

    stripGeometry(&dae);

    dae.writeAll();
    if(!convertSuceeded) {
        printf("Encountered errors\n");
    }

    return convertSuceeded;
}