/**
 * Based on https://github.com/mrdoob/three.js/blob/a72347515fa34e892f7a9bfa66a34fdc0df55954/examples/js/exporters/STLExporter.js
 * Tested on r68 and r70
 * @author jcarletto / https://github.com/jcarletto27
 * @author kjlubick / https://github.com/kjlubick
 * @author kovacsv / http://kovacsv.hu/
 * @author mrdoob / http://mrdoob.com/
 */
THREE.STLExporter = function () {
};

THREE.STLExporter.prototype = {

    constructor: THREE.STLExporter,

    parse: (function () {

        var vector = new THREE.Vector3();
        var normalMatrixWorld = new THREE.Matrix3();

        return function (scene) {

            var output = '';

            output += 'solid exported\n';

            scene.traverse(function (object) {
                if (object instanceof THREE.Mesh) {

                    var geometry = object.geometry;
                    var matrixWorld = object.matrixWorld;
                    var mesh = object;

                    if (geometry instanceof THREE.Geometry) {

                        var vertices = geometry.vertices;
                        var faces = geometry.faces;

                        normalMatrixWorld.getNormalMatrix(matrixWorld);
                        console.log(faces);
                        for (var i = 0, l = faces.length; i < l; i++) {
                            var face = faces[i];

                            vector.copy(face.normal).applyMatrix3(normalMatrixWorld).normalize();

                            output += '\tfacet normal ' + vector.x + ' ' + vector.y + ' ' + vector.z + '\n';
                            output += '\t\touter loop\n';

                            var indices = [face.a, face.b, face.c];

                            for (var j = 0; j < 3; j++) {
                                var vertexIndex = indices[j];
                                if (mesh.geometry.skinIndices.length == 0) {
                                    vector.copy(vertices[vertexIndex]).applyMatrix4(matrixWorld);
                                    output += '\t\t\tvertex ' + vector.x + ' ' + vector.y + ' ' + vector.z + '\n';
                                } else {
                                    vector.copy(vertices[vertexIndex]); //.applyMatrix4( matrixWorld );

                                    // see https://github.com/mrdoob/three.js/issues/3187
                                    boneIndices = [];
                                    boneIndices[0] = mesh.geometry.skinIndices[vertexIndex].x;
                                    boneIndices[1] = mesh.geometry.skinIndices[vertexIndex].y;
                                    boneIndices[2] = mesh.geometry.skinIndices[vertexIndex].z;
                                    boneIndices[3] = mesh.geometry.skinIndices[vertexIndex].w;

                                    weights = [];
                                    weights[0] = mesh.geometry.skinWeights[vertexIndex].x;
                                    weights[1] = mesh.geometry.skinWeights[vertexIndex].y;
                                    weights[2] = mesh.geometry.skinWeights[vertexIndex].z;
                                    weights[3] = mesh.geometry.skinWeights[vertexIndex].w;

                                    inverses = [];
                                    inverses[0] = mesh.skeleton.boneInverses[boneIndices[0]];
                                    inverses[1] = mesh.skeleton.boneInverses[boneIndices[1]];
                                    inverses[2] = mesh.skeleton.boneInverses[boneIndices[2]];
                                    inverses[3] = mesh.skeleton.boneInverses[boneIndices[3]];

                                    skinMatrices = [];
                                    skinMatrices[0] = mesh.skeleton.bones[boneIndices[0]].matrixWorld;
                                    skinMatrices[1] = mesh.skeleton.bones[boneIndices[1]].matrixWorld;
                                    skinMatrices[2] = mesh.skeleton.bones[boneIndices[2]].matrixWorld;
                                    skinMatrices[3] = mesh.skeleton.bones[boneIndices[3]].matrixWorld;


                                    //this checks to see if the mesh has any morphTargets - jc
                                    if (mesh.geometry.morphTargets !== 'undefined') {


                                        morphMatricesX = [];
                                        morphMatricesY = [];
                                        morphMatricesZ = [];
                                        morphMatricesInfluence = [];

                                        for (var mt = 0; mt < mesh.geometry.morphTargets.length; mt++) {
                                            //collect the needed vertex info - jc
                                            morphMatricesX[mt] = mesh.geometry.morphTargets[mt].vertices[vertexIndex].x;
                                            morphMatricesY[mt] = mesh.geometry.morphTargets[mt].vertices[vertexIndex].y;
                                            morphMatricesZ[mt] = mesh.geometry.morphTargets[mt].vertices[vertexIndex].z;
                                            morphMatricesInfluence[mt] = mesh.morphTargetInfluences[mt];
                                        }

                                    }
                                    var finalVector = new THREE.Vector4();

                                    if (mesh.geometry.morphTargets !== 'undefined') {

                                        var morphVector = new THREE.Vector4(vector.x, vector.y, vector.z);

                                        for (var mt = 0; mt < mesh.geometry.morphTargets.length; mt++) {
                                            //not pretty, but it gets the job done - jc
                                            morphVector.lerp(new THREE.Vector4(morphMatricesX[mt], morphMatricesY[mt], morphMatricesZ[mt], 1), morphMatricesInfluence[mt]);
                                        }

                                    }

                                    for (var k = 0; k < 4; k++) {
                                        if (mesh.geometry.morphTargets !== 'undefined') {
                                            var tempVector = new THREE.Vector4(morphVector.x, morphVector.y, morphVector.z);
                                        } else {
                                            var tempVector = new THREE.Vector4(vector.x, vector.y, vector.z);
                                        }
                                        tempVector.multiplyScalar(weights[k]);
                                        //the inverse takes the vector into local bone space
                                        //which is then transformed to the appropriate world space
                                        tempVector.applyMatrix4(inverses[k])
                                            .applyMatrix4(skinMatrices[k]);
                                        finalVector.add(tempVector);

                                    }

                                    output += '\t\t\tvertex ' + finalVector.x + ' ' + finalVector.y + ' ' + finalVector.z + '\n';
                                }
                            }
                            output += '\t\tendloop\n';
                            output += '\tendfacet\n';
                        }
                    }
                }

            });

            output += 'endsolid exported\n';

            return output;
        };
    }
    ())
};

function saveSTL(scene, name) {
    var exporter = new THREE.STLExporter();

    var stlString = exporter.parse(scene);
    var blob = new Blob([stlString], {
        type: 'text/plain'
    });
    saveAs(blob, name + '.stl');
}
var exporter = new THREE.STLExporter();
var exportString = function (output, filename) {

    var blob = new Blob([output], {
        type: 'text/plain'
    });
    var objectURL = URL.createObjectURL(blob);

    var link = document.createElement('a');
    link.href = objectURL;
    link.download = filename || 'data.json';
    link.target = '_blank';
    link.click();

};