/**
 * Created by aghassaei on 3/10/15.
 */

Assembler = Backbone.Model.extend({

    defaults: {
        camStrategy: "raster",
        placementOrder: "XYZ",//used for raster strategy entry
        camProcess: "gcode",
        machineName: "handOfGod",
        machine: null,
        exporter: null,

        dataOut: "",
        needsPostProcessing: true,
        editsMadeToProgram: false,//warn the user that they will override changes

        rapidHeight: 6,//always store relative to origin
        rapidHeightRelative: true,
        safeHeight: 0.5,//inches above stock or assembly, when feed rate should slow

        origin: null,
        originPosition: new THREE.Vector3(20,0,0),//in abs coordinates
        stock: null,
        stockPosition: new THREE.Vector3(20,0,0),//in abs coordinates
        stockPositionRelative: true,
        stockFixed: false,//stock is fixed position from origin
        multipleStockPositions: false,
        stockArraySize: {x:4, y:4},
        stockSeparation: 2.78388,

        rapidSpeeds:{xy: 3, z: 2},//rapids at clearance height
        feedRate:{xy: 0.1, z: 0.1},//speed when heading towards assembly

        simLineNumber: 0,//used for stock simulation, reading through gcode
        simSpeed: 4//#X times real speed
    },

    initialize: function(options){

        _.bindAll(this, "postProcess");

        //bind events
        this.listenTo(options.appState, "change:currentTab", this._tabChanged);
        this.listenTo(this, "change:originPosition", this._moveOrigin);
        this.listenTo(this, "change:stockPosition", this._moveStock);
        this.listenTo(this,
                "change:originPosition " +
                "change:stockPosition " +
                "change:feedRate " +
                "change:rapidSpeeds " +
                "change:camProcess " +
                "change:camStrategy " +
                "change:placementOrder " +
                "change:safeHeight " +
                "change:stockArraySize " +
                "change:stockSeparation " +
                "change:multipleStockPositions " +
                "change:rapidHeight " +
                "change:machineName",
            this._setNeedsPostProcessing);
        this.listenTo(options.lattice,
                "change:numCells " +
                "change:units " +
                "change:scale " +
                "change:cellType " +
                "change:connectionType",
            this._setNeedsPostProcessing);
        this.listenTo(options.lattice, "change:scale", this._setCAMScale);
        this.listenTo(globals.appState, "change:stockSimulationPlaying", this._stockSimulation);

        this.listenTo(options.lattice, "change:partType", this._updatePartType);
        this.listenTo(options.lattice, "change:cellType change:connectionType", this._updateCellType);
        this.listenTo(options.appState, "change:cellMode", this._updateCellMode);
        this.listenTo(this, "change:machineName", this.selectMachine);

        this._initOriginAndStock();
    },

    selectMachine: function(){
        var machineName = this.get("machineName");
        if (this.get("machine")) this.get("machine").destroy();
        this.set("machine", null);
        if (machineName == "shopbot"){
            this.set("machine", new Shopbot());
        } else if (machineName == "handOfGod"){
            this.set("machine", new God());
        } else if (machineName == "oneBitBot"){
            this.set("machine", new OneBitBot());
        } else console.warn("selected machine not recognized");
    },

    makeProgramEdits: function(data){
        this.set("dataOut", data, {silent:true});
        this.set("editsMadeToProgram", true, {silent: true});
    },

///////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////VISUALIZATION//////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////

    isVisible: function(){
        var currentTab = globals.appState.get("currentTab");
        return (currentTab == "cam" || currentTab == "animate" || currentTab == "send");
    },

    _updateCellType: function(){
        if (this.get("machine")) this.get("machine").updateCellType();
        this.set("machineName", "handOfGod");//todo this should go away with dynamic allocation of this model

    },

    _updatePartType: function(){
        if (this.get("machine")) this.get("machine").updatePartType();
    },

    _updateCellMode: function(){
        if (this.get("machine")) this.get("machine").setVisibility(this.isVisible());
        globals.three.render();
    },

    _setCAMScale: function(){
        var scale = globals.lattice.get("scale");
        this.get("origin").scale.set(scale/8, scale/8, scale/8);
        this.get("stock").scale.set(scale/8, scale/8, scale/8);
        if (this.get("machine")) this.get("machine").setScale(scale);
    },

    _tabChanged: function(){
        this._setCAMVisibility();
        if (globals.appState.get("currentTab") != "animate") this.resetSimulation();
    },

    _setCAMVisibility: function(){
        var visible = this.isVisible();
        this.get("origin").visible = visible;
        this.get("stock").visible = visible;
        if (visible && !this.get("machine")) this.selectMachine();
        if (this.get("machine")) this.get("machine").setVisibility(visible);
        globals.three.render();
    },

    _initOriginAndStock: function(){//todo this is ugly
        var origin = new THREE.Mesh(new THREE.SphereGeometry(1),
            new THREE.MeshBasicMaterial({color:0xff0000}));
        globals.three.sceneAdd(origin);
        this.set("origin", origin);
        this._moveOrigin();
        //init stock mesh
        var stock = new THREE.Mesh(new THREE.SphereGeometry(1),
            new THREE.MeshBasicMaterial({color:0xff00ff}));
        globals.three.sceneAdd(stock);
        this.set("stock", stock);
        this._moveStock();
        this._setCAMScale();
        this._setCAMVisibility();
    },

    _moveOrigin: function(){
        var position = this.get("originPosition");
        this.get("origin").position.set(position.x, position.y, position.z);
        if (this.get("stockFixed")) this._updateStockPosToOrigin(position, this.previous("originPosition"));
        globals.three.render();
        if (this.get("machine") && this.get("machine").setMachinePosition) this.get("machine").setMachinePosition();
    },

    _updateStockPosToOrigin: function(newOrigin, lastOrigin){
        console.log(newOrigin);
        console.log(lastOrigin);
        var newStockPosition = _.clone(this.get("stockPosition"));
        _.each(_.keys(newStockPosition), function(key){
            newStockPosition[key] += newOrigin[key] - lastOrigin[key];
            newStockPosition[key] = parseFloat(newStockPosition[key].toFixed(4));
        });
        this.set("stockPosition", newStockPosition);
    },

    _moveStock: function(){
        var position = this.get("stockPosition");
        this.get("stock").position.set(position.x, position.y, position.z);
        globals.three.render();
    },

///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////SIMULATION//////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////

    resetSimulation: function(){
        this.set("simLineNumber", 0, {silent:true});
        globals.appState.set("stockSimulationPlaying", false);
        globals.three.stopAnimationLoop();
        globals.lattice.showCells();
    },

    _stockSimulation: function(){
        if (globals.appState.get("stockSimulationPlaying")){
            globals.three.startAnimationLoop();
            var currentLine = this.get("simLineNumber");
            if (currentLine == 0) globals.lattice.hideCells();
            var allLines = this.get("dataOut").split("\n");
            if(currentLine<allLines.length){
                var self = this;
                this.get("exporter").simulate(allLines[currentLine], this.get("machine"),
                    this.get("originPosition"), function(){
                    currentLine++;
                    self.set("simLineNumber", currentLine);
                    self._stockSimulation();
                });
            } else {
                //finished simulation
                this.resetSimulation();
            }
        } else {
            globals.three.stopAnimationLoop();
            this.get("machine").pause();
        }

    },

///////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////POST PROCESSING////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////

    _setNeedsPostProcessing: function(){
        this.set("needsPostProcessing", true);
    },

    postProcess: function(){
        this.set("needsPostProcessing", false);
        var exporter = this._getExporter();

        var data = "";
        data += exporter.makeHeader();
        data += "\n\n";
        data += exporter.addComment("begin program");
        data += "\n";

        data = this.get("machine").postProcess(data, exporter);

        data += "\n\n";
        data += exporter.addComment("end program");
        data += "\n";
        data += exporter.makeFooter();

        this.set("dataOut", data);
        this.set("editsMadeToProgram", false);
        this.set("exporter", exporter);
        if (!globals.appState.get("stockSimulationPlaying")) this.resetSimulation();
        return {data:data, exporter:exporter};
    },

    _getExporter: function(){
//        var currentExporter = this.get("exporter");
        var camProcess = this.get("camProcess");
        if (camProcess == "shopbot") {
            return new ShopbotExporter();
        } else if (camProcess == "gcode") {
            return new GCodeExporter();
        } else if (camProcess == "tinyG"){
            return new TinyGExporter();
        }
        console.warn("cam process not supported");
        return null;
    },

    _getOrder: function(strategy){
        if (strategy == "raster") return this.get("placementOrder");
        console.warn("strategy not recognized");
        return "";
    },

    save: function(){
        if (this.get("needsPostProcessing")){
            var output = this.postProcess();
            output.exporter.save(output.data);
            return;
        }
        this.get("exporter").save(this.get("dataOut"));
    }

});