/***
 *      Receives GCode, processes it and moves the Data to the Roboter-Class
 */
const fs = require('fs');


class GCode{

    static fileName = "GCodeFiles/log.gcode";

    static containsMCode(s){
        return s.indexOf('M1') == 0
    }

    static receiveMCode(robot, m){

        m  = m.split(" ");
        if(m[0] == "M1"){
            m.forEach((s) => { 
                if(s.includes("X")){ robot.xMotor += Number(s.substring(1, s.length));}
                if(s.includes("x")){ robot.xMotor += Number(s.substring(1, s.length));}

                
                if(s.includes("Y")){ robot.alpha += Number(s.substring(1, s.length));}
                if(s.includes("y")){ robot.alpha += Number(s.substring(1, s.length));}

                
                if(s.includes("Z")){ robot.beta += Number(s.substring(1, s.length));}
                if(s.includes("z")){ robot.beta += Number(s.substring(1, s.length));}
            })
        }

        console.log("Empfangene Motor-Position (" + robot.xMotor.toPrecision(3)+ ", "+ robot.alpha.toPrecision(3)+ ", "+ robot.beta.toPrecision(3)+ ", "+ robot.a.toPrecision(5) + ", "+ robot.b.toPrecision(5)+ ", "+ robot.c.toPrecision(5)+ ") ");
        robot.sendCommand();
    }

    static containsCommand(s){
        if(s.indexOf('M1 ') !== -1){return true;} // M1-Commands = G1-Command only for Motor-Coordinates
        if(s.indexOf('G') !== 0){return false;}
        if(s.indexOf('G90') ==  0){return true;}
        if(s.indexOf('G91') ==  0){return true;}
        if(s.indexOf('G28') !== -1){return true;}
        if(s.indexOf('G1 ') !== -1){return true;}
        return false;
    }


    static getM114(robot){
        let text = '{"position":{    "x":'+robot.x+
                                  ', "y":'+robot.y+
                                  ', "z":'+robot.z+
                                  ', "a":' +robot.phi + 
                                  ', "b":' +robot.theta + 
                                  ', "c":' +robot.psi + '},' +
                    '"motorCounts":{ "x":'+ robot.xMotor +
                                  ', "y":'+ robot.alpha + 
                                  ', "z":'+ robot.beta +
                                  ', "a":'+ robot.a + 
                                  ', "b":'+ robot.b + 
                                  ', "c":'+ robot.c +
                                  ', "e":'+ 0.0 +
                                  '}}'; 
        return text;
    }


    static toPiMultiple(gCode){
        if(gCode == undefined){return gCode;}

        var multipleParameters = gCode.split(" ");
        var newGString = ""; 
        multipleParameters.forEach((paramet) => { 
            if(paramet == undefined || !paramet || paramet.length === 0 || paramet == " " || paramet ==""){
            
            }
            else if(['A', 'a', 'B', 'b', 'C', 'c','E','e'].includes(paramet.charAt(0)) )
            {
                var numberParameter = Number(paramet.substring(1, paramet.length))
                newGString += " " + paramet[0] + String(numberParameter*Math.PI/180)
            }
            else{
                newGString += " " + paramet;
            }
        });
        return newGString.trim();
    };

    static receiveGCode(robot, g){

        if(g == undefined) return;

        var multipleCommands = g.split(" G");
        var doProcessRest = false;
        if(multipleCommands.length > 1){
            doProcessRest = true;
            g = multipleCommands[0];
        }
        //console.log("Command: " + g);

        g = g.split(" ");

        var calculateNew = true;
        var calculateFromMotorCoordinates = false;

        if(g[0] == "G90") {robot.moveRelative = false; calculateNew = false;}
        else if(g[0] == "G91") {robot.moveRelative = true; calculateNew = false;}
        else if(g[0] == "G28") {
                robot.x = 0;
                robot.y = robot.l1 + robot.l2 + robot.l3;
                robot.z = 0;
                robot.phi = -Math.PI/2;
                robot.theta = Math.PI/2;
                robot.psi = 0;
                robot.e = 0;
            }
        else if(g[0] == "G1" && robot.moveRelative){
            g.forEach((s) => { 
                if(s.includes("X")){ robot.x += Number(s.substring(1, s.length));}
                if(s.includes("x")){ robot.x += Number(s.substring(1, s.length));}
                if(s.includes("Y")){ robot.y += Number(s.substring(1, s.length));}
                if(s.includes("y")){ robot.y += Number(s.substring(1, s.length));}
                if(s.includes("Z")){ robot.z += Number(s.substring(1, s.length));}
                if(s.includes("z")){ robot.z += Number(s.substring(1, s.length));}

                
                // abc in 2Pi-Angles 
                if(s.includes("A")){ robot.phi += Number(s.substring(1, s.length));}
                if(s.includes("a")){ robot.phi += Number(s.substring(1, s.length));}
                if(s.includes("B")){ robot.theta += Number(s.substring(1, s.length));}
                if(s.includes("b")){ robot.theta += Number(s.substring(1, s.length));}
                if(s.includes("C")){ robot.psi += Number(s.substring(1, s.length));}
                if(s.includes("c")){ robot.psi += Number(s.substring(1, s.length));}

                if(s.includes("E")){ robot.e += Number(s.substring(1, s.length));}
                if(s.includes("e")){ robot.e += Number(s.substring(1, s.length));}
            });
        }
        else if(g[0] == "M1" && robot.moveRelative){
            calculateFromMotorCoordinates = true;
            g.forEach((s) => { 
                if(s.includes("X")){ robot.xMotor += Number(s.substring(1, s.length));}
                if(s.includes("x")){ robot.xMotor += Number(s.substring(1, s.length));}
                if(s.includes("Y")){ robot.alpha += Number(s.substring(1, s.length));}
                if(s.includes("y")){ robot.alpha += Number(s.substring(1, s.length));}
                if(s.includes("Z")){ robot.beta += Number(s.substring(1, s.length));}
                if(s.includes("z")){ robot.beta += Number(s.substring(1, s.length));}

                
                if(s.includes("A")){ robot.a += Number(s.substring(1, s.length));}
                if(s.includes("a")){ robot.a += Number(s.substring(1, s.length));}
                if(s.includes("B")){ robot.b += Number(s.substring(1, s.length));}
                if(s.includes("b")){ robot.b += Number(s.substring(1, s.length));}
                if(s.includes("C")){ robot.c += Number(s.substring(1, s.length));}
                if(s.includes("c")){ robot.c += Number(s.substring(1, s.length));}

                if(s.includes("E")){ robot.e += Number(s.substring(1, s.length));}
                if(s.includes("e")){ robot.e += Number(s.substring(1, s.length));}
            });
        }
        // Absolute-Positioning
        else if(g[0] == "G1" && !robot.moveRelative){
            g.forEach((s) => { 
                if(s.includes("X")){ robot.x = Number(s.substring(1, s.length));}
                if(s.includes("x")){ robot.x = Number(s.substring(1, s.length));}
                if(s.includes("Y")){ robot.y = Number(s.substring(1, s.length));}
                if(s.includes("y")){ robot.y = Number(s.substring(1, s.length));}
                if(s.includes("Z")){ robot.z = Number(s.substring(1, s.length));}
                if(s.includes("z")){ robot.z = Number(s.substring(1, s.length));}

                if(s.includes("A")){ robot.phi = Number(s.substring(1, s.length));}
                if(s.includes("a")){ robot.phi = Number(s.substring(1, s.length));}
                if(s.includes("B")){ robot.theta = Number(s.substring(1, s.length));}
                if(s.includes("b")){ robot.theta = Number(s.substring(1, s.length));}
                if(s.includes("C")){ robot.psi = Number(s.substring(1, s.length));}
                if(s.includes("c")){ robot.psi = Number(s.substring(1, s.length));}

                if(s.includes("E")){ robot.e = Number(s.substring(1, s.length));}
                if(s.includes("e")){ robot.e = Number(s.substring(1, s.length));}
            });
        }
        else if(g[0] == "M1" && !robot.moveRelative){
            calculateFromMotorCoordinates = true;
            g.forEach((s) => { 
                if(s.includes("X")){ robot.xMotor = Number(s.substring(1, s.length));}
                if(s.includes("x")){ robot.xMotor = Number(s.substring(1, s.length));}
                if(s.includes("Y")){ robot.alpha = Number(s.substring(1, s.length));}
                if(s.includes("y")){ robot.alpha = Number(s.substring(1, s.length));}
                if(s.includes("Z")){ robot.beta = Number(s.substring(1, s.length));}
                if(s.includes("z")){ robot.beta = Number(s.substring(1, s.length));}

                if(s.includes("A")){ robot.a = Number(s.substring(1, s.length));}
                if(s.includes("a")){ robot.a = Number(s.substring(1, s.length));}
                if(s.includes("B")){ robot.b = Number(s.substring(1, s.length));}
                if(s.includes("b")){ robot.b = Number(s.substring(1, s.length));}
                if(s.includes("C")){ robot.c = Number(s.substring(1, s.length));}
                if(s.includes("c")){ robot.c = Number(s.substring(1, s.length));}
                
                if(s.includes("E")){ robot.e = Number(s.substring(1, s.length));}
                if(s.includes("e")){ robot.e = Number(s.substring(1, s.length));}
            });
        }


        if(calculateNew && !calculateFromMotorCoordinates){  
            robot.calculateAngles3D();  
            robot.sendCommand();
        }

        if(calculateFromMotorCoordinates){
            robot.calculatePositionFromMotorAngles();
            robot.sendCommand();
        }
        

        // verarbeitet alle weiteren Commands in dieser Zeile.
        if(doProcessRest){
            multipleCommands.forEach((cmd) => {
                if(cmd[0] != "G"){
                    this.receiveGCode(robot, "G"+cmd);   
                }
            })
        }
        
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////77
    // Commands for Files

    static removeStringFromFile(fileName, stringToRemove) {
        try {
            const data = fs.readFileSync(fileName, 'utf8');
            const modifiedData = data.replace(new RegExp(stringToRemove, 'g'), '');
            fs.writeFileSync(fileName, modifiedData, 'utf8');
        } catch (err) {
            console.error('Error:', err);
        }
    }

    static ContainsFilesCommand(message){
        if(message.indexOf('FPoint') !== -1){return true;}  // Writes new Point (at end of file)
        if(message.indexOf('FPlus') !== -1){return true;}   // go to Next Position in Log File
        if(message.indexOf('FMinus') !== -1){return true;}  // go to Previous Position
        if(message.indexOf('FFirst') !== -1){return true;}  // set Cursour to First Position of Log File
        if(message.indexOf('FLast') !== -1){return true;}   // set Cursour to Last Position of Log File


        if(message.indexOf('FShow') !== -1){return true;}   // Shows/Sends GCode-File
        if(message.indexOf('FList') !== -1){return true;}   // Lists GCode-Files
        if(message.indexOf('FLoad ') !== -1){return true;}  // Loads File into Log
        if(message.indexOf('FSave ') !== -1){return true;}  // Saves Log to GCode-File
        if(message.indexOf('FClear') !== -1){return true;}  // Clears default Log File

        if(message.indexOf('M20') !== -1){return true;} // M20 - List SD Card-Contents // https://marlinfw.org/docs/gcode/M020.html
        if(message.indexOf('M23') !== -1){return true;} // M23 - Select SD file
        if(message.indexOf('M28') !== -1){return true;} // M28 - Start SD write // M28 [B1] filename
        if(message.indexOf('M29') !== -1){return true;} // M29 - Stop SD write


        return false;
    }

    static receiveFC(robot, message){

        if(message.indexOf('FShow') !== -1){
            try {
                const data = fs.readFileSync(this.fileName, 'utf8');
                const reply = "XYZ__FShow__XYZ" + data; // prepend header
                return reply.replaceAll("G91 ","").replaceAll("G90 ","").replace(/\s?\bt\d+\b\s?/g, '').trim();

            } catch (err) {
                console.error('Error:', err);
            }
        }

        if(message.indexOf('FPoint') !== -1){
            
            const secondsSinceEpoch = 10*Math.floor(Date.now() / 100);

            var strGCode = String(`G90 G1 x${robot.x} y${robot.y} z${robot.z} a${(robot.phi*180/Math.PI).toFixed(2)} b${(robot.theta*180/Math.PI).toFixed(2)} c${(robot.psi*180/Math.PI).toFixed(2)} e${(robot.e*180/Math.PI).toFixed(2)} t${secondsSinceEpoch}  f1000` )
            this.removeStringFromFile(this.fileName, ';!')
            fs.appendFileSync(this.fileName, strGCode+ ';!'+ '\r\n', 'utf8');
        }

        if(message.indexOf('FMinus') !== -1 || message.indexOf('FPlus') !== -1){
            let lines = fs.readFileSync(this.fileName, 'utf8').split('\r\n');

            let lineChange = -1;

            // Process lines
            for (let i = 0; i < lines.length; i++) {
                if (lines[i].indexOf(';!') !== -1) {
                    if(message.indexOf('FMinus') !== -1 && i > 0){
                        lines[i - 1] += ';!';
                        lines[i] = lines[i].split(';!')[0];
                        var gCodePi = this.toPiMultiple(lines[i-1]);
                        this.receiveGCode(robot, gCodePi);
                    }
                    if(message.indexOf('FPlus') !== -1 && i < (lines.length-2) && lineChange == -1){
                        lines[i + 1] += ';!';                        
                        lines[i] = lines[i].split(';!')[0];
                        lineChange = i+1;
                        var gCodePi = this.toPiMultiple(lines[i+1]);
                        this.receiveGCode(robot, gCodePi);
                    }
                }
            }

            // Write the updated content back
            fs.writeFileSync(this.fileName, lines.join('\r\n'), 'utf8');

        }
        
        return GCode.getM114(robot);
    }
}

module.exports = GCode 

