####################################################
# Code generator (transpiler) to LIE++ Language
# TEC - FOD
# Authors:
#   Michael Gonzalez Rivera
#   Luis Saborio Hernandez
# Edited:
#   Ellioth Ramirez
####################################################

import sys
import platform
import subprocess
import re
import math

from antlr4 import *
from gen.fodcatListener import fodcatListener
from gen.fodcatLexer import fodcatLexer
from gen.fodcatParser import fodcatParser

from multi.multiFun import multiFun
GTI = multiFun()

# Documentation for CodeGenerator
#
# Compiler code generator that works over the AST generated by ANTLR4.
# It translates the LIE++ code to Python, and also executes, if it can, the translated
# code.


class CodeGenerator(fodcatListener):

    # CodeGenerator constructor. Initialize all needed global variables for the
    # succesfully execution of the code generator.
    # @param output Python output file name.
    def __init__(self, output, canGPIOFlag, input_mode, inputs=[]):
        self.input_mode = input_mode
        self.inputs = inputs
        # Store the Python output file name.
        self.output = output
        # String to store the translated code.
        self.program = ""

        # Identation level counter.
        self.countIndent = 0
        # Used to set a diferent name to each "while" translated form a "repetir"
        # instrucction.
        self.cicleCount = 0

        # Flag to store if it is translating an "if" command.
        self.ifFlag = False
        # Flag to store if it is translating an "else if" command.
        self.elseifFlag = False
        # Flag to store if it is translating a "while" command.
        self.whileFlag = False
        # Flag to store if the function call is part of an expression.
        self.expressionFlag = False
        # Flag to store if the variable/array declaration is global or local.
        self.localsFlag = False  # Set the local definitions flag to false
        # Stores if it can use the GPIOs
        self.canGPIOFlag = canGPIOFlag
        # Stores if buttons have been used in source code
        self.hasButtonFlag = False
        # Stores if LEDs have been used in source code
        self.hasLEDFlag = False
        # Stores if variable brightness LEDs have been used in source code
        self.hasVLEDFlag = False
        # Stores if servos have been used in source code
        self.hasServoFlag = False
        # Stores if angular servos have been used in source code
        self.hasAngularServoFlag = False
        # Stores if sleep instruccion has been used in source code
        self.hasSleepFlag = False
        # Stores if GPIOs have been used in source code
        self.hasGPIOFlag = False
        # Flag to store if the variable/array declarations are using dynamic declarations/assignations.
        self.canExecuteFlag = True

        # Dictionary that stores local variables for the exec() parameters.
        self.locals = {}
        # Dictionary that stores global variables for the exec() parameters.
        self.globals = {}
        # Defines that __builtins__ are not used for the exec() command.
        # self.globals["__builtins__"] = None
        # Stores the definition of the main function.
        self.globals["main"] = 'def main()'
        # ## Specifies that the str function will be used for the exec() command.
        # self.globals["str"] = str
        # ## Specifies that the print function will be used for the exec() command.
        # self.globals["print"] = print

    # Sets the indentation level in program code. It works with the value of the countIndent variable.
    # @param indentLevel Receives the indentation level to be set at program code.
    def indent(self, indentLevel):
        while indentLevel > 0:
            # Add indentation at program code.
            self.program += "\t"
            indentLevel -= 1

    # Transforms keywords and symbols to Python using regex substitution or string replacement.
    # @param String of expression.
    # @return Expression modified to fit Python keywords.
    def processKeyword(self, exp):
        # sub is a subtitution in "alone" spaces, not substrings.
        exp = re.sub(r"\bpi\b", str(math.pi), exp)
        exp = re.sub(r"\beuler\b", str(math.e), exp)
        exp = re.sub(r"\bverdadero\b", "True", exp)
        exp = re.sub(r"\bfalso\b", "False", exp)
        exp = re.sub(r"\by\b", " and ", exp)
        exp = exp.replace("&", " and ")  # replace is in all the string.
        exp = re.sub(r"\s+o\s+", " or ", exp)
        exp = exp.replace("|", " or ")
        exp = re.sub(r"\b(no[(]|no\s+[(])\b", " not (", exp)
        exp = re.sub(r"([!][(]|[!]\s+[(])", " not (", exp)
        exp = re.sub(r"\bno\b", "not", exp)
        exp = re.sub(r"\s+=\s+", " == ", exp)
        exp = re.sub(r"\bdiferente\b", " != ", exp)
        exp = re.sub(r"\bigual\b", " == ", exp)
        exp = re.sub(r"\bmod\b", " % ", exp)

        exp = re.sub(r"\b.encendido\b", ".on", exp)
        exp = re.sub(r"\b.apagado\b", ".off", exp)
        exp = re.sub(r"\b.parpadear\b", ".blink", exp)
        exp = exp.replace(".brillo", ".value")
        exp = re.sub(r"\b.minimo\b", ".min", exp)
        exp = re.sub(r"\b.medio\b", ".mid", exp)
        exp = re.sub(r"\b.maximo\b", ".max", exp)
        exp = exp.replace(".angulo", ".angle")
        return exp

    # Checks if is an expression, searching if there
    # is a logic or arithmetic operator.
    # @param varValue String of code to be checked.
    # @return Boolean items list.
    def checkIfExpression(self, varValue):
        items = list()
        items.append(varValue.__contains__("+"))
        items.append(varValue.__contains__("-"))
        if items[1]:
            if not((varValue.find("-") > 0) | (varValue.count("-") > 1)):  # If is not a negative number
                items[1] = False
        items.append(varValue.__contains__("%"))
        items.append(varValue.__contains__("("))
        items.append(varValue.__contains__("*"))
        items.append(varValue.__contains__("/"))
        items.append(varValue.__contains__(">"))
        items.append(varValue.__contains__("<"))
        items.append(varValue.__contains__(" and "))
        items.append(varValue.__contains__(" or "))
        items.append(varValue.__contains__(" not "))
        items.append(varValue.__contains__("=="))
        return items

    # Checks if is a logic or relational expression, searching if there is a logical or
    # relational operator. Is used for the print translation.
    # @param varValue String of code to be checked.
    # @return Boolean items list.

    def checkIfLogicalRelationalExpression(self, varValue):
        items = list()
        items.append(varValue.__contains__(">"))
        items.append(varValue.__contains__("<"))
        items.append(varValue.__contains__(" and "))
        items.append(varValue.__contains__(" or "))
        items.append(varValue.__contains__(" not "))
        items.append(varValue.__contains__("=="))
        items.append(varValue.__contains__("True"))
        items.append(varValue.__contains__("False"))
        return items

    # ************************************************************************************
    # Initial Rule
    # ************************************************************************************

    # Enter a parse tree produced by fodcatParser#program.
    # @param ctx:fodcatParser.CommandContext
    def enterProgram(self, ctx: fodcatParser.ProgramContext):
        self.program = ""
        self.program += "input_mode = '" + self.input_mode + "'"
        inputs = ''
        if self.inputs != []:
            inputs = '['
            first = True
            for input in self.inputs:
                if first:
                    inputs += input
                    first = False
                else:
                    inputs += ', ' + input
            inputs += ']'
        self.program += "\nentradas = " + inputs

    # Exit a parse tree produced by fodcatParser#program. Writes in a file the Python
    # translated program, and, if there are no problems, it executes program with the
    # specific variables.
    # @param ctx:fodcatParser.CommandContext
    def exitProgram(self, ctx: fodcatParser.ProgramContext):
        self.program += "\ndef leer():\n"
        self.indent(1)
        self.program += "if input_mode == 'automatico':\n"
        self.indent(2)
        self.program += "return entradas.pop(0)\n"
        self.indent(1)
        self.program += "elif input_mode == 'manual':\n"
        self.indent(2)
        self.program += "return input()\n\n"

        self.program += "\nmain()"  # Add the main function call
        self.program += "\nfrom types import FunctionType"
        self.program += "\n\nnombre_variables = " + \
            str(list(self.globals.keys()))
        self.program += "\nvar_globales = {}"
        self.program += "\nfor nombre_variable in nombre_variables:\n"
        self.countIndent += 1  # Add 1 to the indentation level
        self.indent(self.countIndent)
        self.program += "if (type(globals()[nombre_variable]) is not FunctionType and nombre_variable != 'main'):\n"
        self.indent(2)
        self.program += "var_globales[nombre_variable] = globals()[nombre_variable]"
        self.program += "\nprint('dict_globales = ', var_globales)"

        if self.output == None:
            # does nothing
            return

        if self.output != None:
            # Writes in a file the Python translated program
            self.output.write(self.program)
        # If is the correct device to be executed
        if (self.canGPIOFlag and self.hasGPIOFlag) or (not self.hasGPIOFlag):
            # Executes program with the specific variables
            mmFun = multiFun()
            mmFun.printPreatty(mmFun.execCode(self.program))

        # If is using the GPIOs, but is not running over a Raspberry Pi
        elif (not self.canGPIOFlag) and self.hasGPIOFlag:
            print("El código no se ejecutará, ya que se están utilizando los GPIOs,"
                  " pero no se está ejecutando en una Raspberry Pi.\nPuede transferir el archivo \""
                  + self.output.name + "\" a una Raspberry Pi para realizar la ejecución manual.")
        # if self.canExecuteFlag: # If there are no problems
        #     # If is the correct device to be executed
        #     if (self.canGPIOFlag and self.hasGPIOFlag) or (not self.hasGPIOFlag):
        #         #Execute program with the specific variables
        #         exec(self.program, self.globals, self.locals)
        #     # If is using the GPIOs, but is not running over a Raspberry Pi
        #     elif (not self.canGPIOFlag) and self.hasGPIOFlag:
        #         print("El código no se ejecutará, ya que se están utilizando los GPIOs,"
        #               " pero no se está ejecutando en una Raspberry Pi.\nPuede transferir el archivo \""
        #               + self.output.name + "\" a una Raspberry Pi para realizar la ejecución manual.")
        # else: # If there is a dynamic assignation
        #     print("Hubo una asignación dinámica en el código, por lo que no se realizará "
        #           "la ejecución automática, ya que no garantiza un resultado correcto.\n"
        #           "Puede realizar la ejecución manual del archivo \"" + self.output.name + "\".")

    # Enter a parse tree produced by fodcatParser#main_function.
    # @param ctx:fodcatParser.CommandContext
    def enterMain_function(self, ctx: fodcatParser.Main_functionContext):
        self.countIndent += 1  # Add 1 to the indentation level
        self.program += "\ndef main():\n"  # Add the main function

        self.globals["main"] = "def main()"
        self.localsFlag = True  # Set the local definitions flag to true
        if ctx.getChild(4).getText() == "fin":  # If the main function is empty
            self.program += "\tpass"  # Do nothing

    # Exit a parse tree produced by fodcatParser#main_function.
    # @param ctx:fodcatParser.CommandContext
    def exitMain_function(self, ctx: fodcatParser.Main_functionContext):
        self.indent(self.countIndent)
        self.program += "print('dict_locales = ', locals())\n"
        self.countIndent -= 1  # Substract 1 from the indentation level
        self.program += "\n"
        self.localsFlag = False  # Set the local definitions flag to false

    # ************************************************************************************
    # Commands
    # ************************************************************************************

    # Enter a parse tree produced by fodcatParser#command.
    # @param ctx:fodcatParser.CommandContext
    def enterCommand(self, ctx: fodcatParser.CommandContext):
        # If the command is a "mientras" statement
        if ctx.getChild(0).getText() == "mientras":
            self.cicleCount += 1  # Add 1 to the cicleCount variable
            self.indent(self.countIndent)  # Set proper indentation level
            whileExpression = GTI.getTextInterval(ctx, ctx.getChild(2))
            whileExpression = self.processKeyword(
                whileExpression)  # Gets the "while expression"
            if (whileExpression.__contains__(".presionado")):
                # Adds while statement when a button is used
                self.program += "while (" + ctx.getChild(
                    2).getChild(0).getText() + ".is_pressed):\n"
                self.whileFlag = True
            else:
                self.program += "while (" + whileExpression + "):\n"
            self.countIndent += 1  # Add 1 to the indentation level

            if ctx.getChildCount() < 7:  # If the "mientras" body is empty
                self.indent(self.countIndent)  # Set proper indentation level
                self.program += "pass\n"  # Do nothing

        # If the command is a "repetir" statement
        elif ctx.getChild(0).getText() == "repetir":
            self.cicleCount += 1  # Add 1 to the cicleCount variable
            self.indent(self.countIndent)  # Set proper indentation level
            repetitions = GTI.getTextInterval(ctx, ctx.getChild(2))
            # Gets the times the commands inside the "repetir" statement should be repeated
            repetitions = self.processKeyword(repetitions)
            # Creates the counter to be used by the while statement
            self.program += "whileCounter" + \
                str(self.cicleCount) + "=" + repetitions + "\n"
            try:
                self.locals["whileCounter" +
                            str(self.cicleCount)] = int(repetitions)
            except:
                pass
            self.indent(self.countIndent)  # Set proper indentation level
            # Emulates the "repetir" statement with a "while"
            self.program += "while whileCounter" + \
                str(self.cicleCount) + " > 0:\n"
            self.countIndent += 1  # Add 1 to the indentation level
            self.indent(self.countIndent)  # Set proper indentation level
            self.program += "whileCounter" + \
                str(self.cicleCount) + " -= 1\n"  # Decreases by 1 the counter

            if ctx.getChildCount() < 7:  # If the "repetir" body is empty
                self.indent(self.countIndent)  # Set proper indentation level
                self.program += "pass\n"  # Do nothing

        # If the command is a "imprimir" statement
        elif ctx.getChild(0).getText() == "imprimir":
            self.indent(self.countIndent)  # Set proper indentation level
            childCounter = 2
            self.program += "print("
            expression = False  # Flag to check if is an expression
            relLogExpression = False  # Flag to check if is a relational or logical expression
            first = True  # First value to be printed flag
            while (childCounter < ctx.getChildCount()):  # Gets all the elements to be printed
                # Checks if the child is an expression
                if not(ctx.getChild(childCounter).getText().__contains__("\"")):
                    expression = True
                    # Checks if the child is a relational or logical expression
                    if (any(self.checkIfLogicalRelationalExpression(self.processKeyword(ctx.getChild(childCounter).getText())))):
                        relLogExpression = True
                if first:  # If is the first value to be printed
                    first = False
                    if expression:  # If is an expression
                        if relLogExpression:  # If is a relational or logical expression
                            # The "if" statement inside the print statement is to translate to spanish the boolean values
                            self.program += "str(\"verdadero\" if (" + self.processKeyword(
                                ctx.getChild(childCounter).getText()) + ") else \"falso\")"
                        else:
                            self.program += "str(" + self.processKeyword(
                                ctx.getChild(childCounter).getText()) + ")"
                    else:  # If is not an expression
                        self.program += ctx.getChild(childCounter).getText()
                else:  # If is not the first value to be printed
                    if expression:  # If is an expression
                        if relLogExpression:  # If is a relational or logical expression
                            # The "if" statement inside the print statement is to translate to spanish the boolean values
                            self.program += " + str(\"verdadero\" if (" + self.processKeyword(
                                ctx.getChild(childCounter).getText()) + ") else \"falso\")"
                        else:
                            self.program += " + str(" + self.processKeyword(
                                ctx.getChild(childCounter).getText()) + ")"
                    else:  # If is not an expression
                        self.program += " + " + \
                            ctx.getChild(childCounter).getText()
                expression = False  # Clear expression flag
                relLogExpression = False  # Clear relational or logical expression flag
                # Add 2 to the childCounter (to skip the print operator)
                childCounter += 2
            self.program += ")"

        # If the command is a "devolver" statement
        elif ctx.getChild(0).getText() == "devolver":
            self.indent(self.countIndent)  # Set proper indentation level
            self.program += "return "
            if ctx.getChildCount() > 1:  # If is not an empty return
                # Add return value
                self.program += self.processKeyword(
                    GTI.getTextInterval(ctx, ctx.getChild(1)))
        else:
            pass

    # Exit a parse tree produced by fodcatParser#command.
    # @param ctx:fodcatParser.CommandContext
    def exitCommand(self, ctx: fodcatParser.CommandContext):
        # If the command was a "mientras" or "repetir" statement
        if (ctx.getChild(0).getText() == "mientras") | (ctx.getChild(0).getText() == "repetir"):
            self.countIndent -= 1  # Substract 1 from the indentation level
            self.whileFlag = False
        # If the command was a "imprimir" or "devolver" statement
        elif (ctx.getChild(0).getText() == "imprimir") | (ctx.getChild(0).getText() == "devolver"):
            self.program += "\n"
        else:
            pass

    # Enter a parse tree produced by fodcatParser#ifstat.
    # @param ctx:fodcatParser.CommandContext
    def enterIfstat(self, ctx: fodcatParser.IfstatContext):
        if not self.elseifFlag:  # Checks if it is not translating an "else if" command
            self.indent(self.countIndent)  # Set proper indentation level
        else:
            self.elseifFlag = False
        # Get text with whitespace
        ifCommand = GTI.getTextInterval(ctx, ctx)
        # Get text between "si" and "entonces"
        logicExp = str(re.findall("(?<=si)(.*?)(?=entonces)", ifCommand)[0])
        if (logicExp.__contains__(".presionado")):
            # Adds if statement when a button is used
            self.program += "if (" + \
                ctx.getChild(2).getChild(0).getText() + ".is_pressed):\n"
            self.ifFlag = True
        else:
            # Adds if statement to the program after converting keywords
            self.program += "if (" + self.processKeyword(logicExp) + "):\n"
        if ctx.getChildCount() < 6:  # If is an empty "if"
            self.indent(self.countIndent)  # Set proper indentation level
            self.program += "\tpass"  # Do nothing
        self.countIndent += 1  # Add 1 to the indentation level

    # Exit a parse tree produced by fodcatParser#ifstat.
    # @param ctx:fodcatParser.CommandContext
    def exitIfstat(self, ctx: fodcatParser.IfstatContext):
        if (str(re.findall("(?<=si)(.*?)(?=entonces)",
                           GTI.getTextInterval(ctx, ctx))[0]).__contains__(".presionado")):
            self.ifFlag = False
        self.program += "\n"
        self.countIndent -= 1  # Substract 1 from the indentation level

    # Enter a parse tree produced by fodcatParser#elsestat.
    # @param ctx:fodcatParser.CommandContext
    def enterElsestat(self, ctx: fodcatParser.ElsestatContext):
        self.indent(self.countIndent)  # Set proper indentation level
        if ctx.getChildCount() >= 2:  # If the "else" statement has more than 2 children nodes
            if ctx.getChild(1).getChild(0).getText() == "si":  # Is an "else if" statement
                self.elseifFlag = True  # Set elsif flag to true
                self.program += "el"
            else:  # Is an "else" statement
                self.program += "else:\n"
                self.countIndent += 1  # Add 1 to the indentation level

        else:  # If is a regular "else" statement
            self.program += "else:\n"
            self.indent(self.countIndent)  # Set proper indentation level
            self.program += "\tpass\n"

    # Exit a parse tree produced by fodcatParser#elsestat.
    # @param ctx:fodcatParser.CommandContext
    def exitElsestat(self, ctx: fodcatParser.ElsestatContext):
        if ctx.getChildCount() == 2:
            if not(ctx.getChild(1).getChild(0).getText() == "si"):
                self.countIndent -= 1  # Substract 1 from the indentation level
        else:
            self.countIndent -= 1  # Substract 1 from the indentation level

    # Enter a parse tree produced by fodcatParser#func_call.
    # @param ctx:fodcatParser.CommandContext
    def enterFunc_call(self, ctx: fodcatParser.Func_callContext):
        if not self.expressionFlag:  # If the function call is not inside an expression
            self.indent(self.countIndent)  # Set proper indentation level
            self.program += ctx.getText()

    # Exit a parse tree produced by fodcatParser#func_call.
    # @param ctx:fodcatParser.CommandContext
    def exitFunc_call(self, ctx: fodcatParser.Func_callContext):
        if not self.expressionFlag:  # If the function call is not inside an expression
            self.program += "\n"

    # Enter a parse tree produced by fodcatParser#flow_exp.
    # @param ctx:fodcatParser.CommandContext
    def enterFlow_exp(self, ctx: fodcatParser.Flow_expContext):
        self.indent(self.countIndent)  # Set proper indentation level
        # If the command is a "finciclo" statement
        if ctx.getChild(0).getText() == "finciclo":
            self.program += "break"
        # If the command is a "continuar" statement
        elif ctx.getChild(0).getText() == "continuar":
            self.program += "continue"

    # Exit a parse tree produced by fodcatParser#flow_exp.
    # @param ctx:fodcatParser.CommandContext
    def exitFlow_exp(self, ctx: fodcatParser.Flow_expContext):
        self.program += "\n"

    # Enter a parse tree produced by fodcatParser#assignment.
    # @param ctx:fodcatParser.CommandContext
    def enterAssignment(self, ctx: fodcatParser.AssignmentContext):
        self.indent(self.countIndent)  # Set proper indentation level
        if ctx.getChild(0).getChildCount() > 1:  # Its an array assignation
            position = GTI.getTextInterval(ctx, ctx.getChild(0).getChild(2))
            position = self.processKeyword(position)  # Get translated position
            if position in self.locals:
                position = str(self.locals[position])
            elif position in self.locals:
                position = str(self.globals[position])
            expressionListPosition = self.checkIfExpression(
                position)  # Checks if the position is an expression
            value = GTI.getTextInterval(ctx, ctx.getChild(2))
            value = self.processKeyword(value)  # Get translated value
            expressionListValue = self.checkIfExpression(
                value)  # Checks if the value is an expression
            # If position or value is an expression
            if (any(expressionListPosition)) | (any(expressionListValue)):
                if not self.localsFlag:  # If is a global array assignation
                    self.canExecuteFlag = False  # Will not be executed
            else:
                if self.localsFlag & self.canExecuteFlag:  # If is a local array assignation
                    # If the array is local
                    if ctx.getChild(0).getChild(0).getText() in self.locals:
                        # If a value has not been assigned
                        if self.locals[ctx.getChild(0).getChild(0).getText()][int(position)] == None:
                            try:
                                self.locals[ctx.getChild(0).getChild(0).getText()][int(position)] = int(
                                    value)  # Checks if the value is an integer number
                            except:
                                try:
                                    self.locals[ctx.getChild(0).getChild(0).getText()][int(position)] = float(
                                        value)  # Checks if the value is a float number
                                except:
                                    try:
                                        # Checks if the value is a boolean
                                        if value == "True":  # Checks if the value is true
                                            self.locals[ctx.getChild(0).getChild(
                                                0).getText()][int(position)] = True
                                        elif value == "False":  # Checks if the value is false
                                            self.locals[ctx.getChild(0).getChild(
                                                0).getText()][int(position)] = False
                                        else:
                                            if "\"" in value:  # Checks if the value is a string
                                                self.locals[ctx.getChild(0).getChild(
                                                    0).getText()][int(position)] = str(value)
                                            else:  # Is an identifier
                                                self.canExecuteFlag = False
                                    except:
                                        pass
                    else:  # If the array is global
                        # If a value has not been assigned
                        if self.globals[ctx.getChild(0).getChild(0).getText()][int(position)] == None:
                            try:
                                self.globals[ctx.getChild(0).getChild(0).getText()][int(position)] = int(
                                    value)  # Checks if the value is an integer number
                            except:
                                try:
                                    self.globals[ctx.getChild(0).getChild(0).getText()][int(position)] = float(
                                        value)  # Checks if the value is a float number
                                except:
                                    try:
                                        # Checks if the value is a boolean
                                        if value == "True":  # Checks if the value is true
                                            self.globals[ctx.getChild(0).getChild(
                                                0).getText()][int(position)] = True
                                        elif value == "False":  # Checks if the value is false
                                            self.globals[ctx.getChild(0).getChild(
                                                0).getText()][int(position)] = False
                                        else:
                                            if "\"" in value:  # Checks if the value is a string
                                                self.globals[ctx.getChild(0).getChild(
                                                    0).getText()][int(position)] = str(value)
                                            else:  # Is an identifier
                                                self.canExecuteFlag = False
                                    except:
                                        pass
                elif(self.canExecuteFlag):  # If is a global array assignation
                    try:
                        self.globals[ctx.getChild(0).getChild(0).getText()][int(position)] = int(
                            value)  # Checks if the value is an integer number
                    except:
                        try:
                            self.globals[ctx.getChild(0).getChild(0).getText()][int(position)] = float(
                                value)  # Checks if the value is a float number
                        except:
                            try:
                                # Checks if the value is a boolean
                                if value == "True":  # Checks if the value is true
                                    self.globals[ctx.getChild(0).getChild(
                                        0).getText()][int(position)] = True
                                elif value == "False":  # Checks if the value is false
                                    self.globals[ctx.getChild(0).getChild(
                                        0).getText()][int(position)] = False
                                else:
                                    if "\"" in value:  # Checks if the value is a string
                                        self.globals[ctx.getChild(0).getChild(
                                            0).getText()][int(position)] = str(value)
                                    else:  # Is an identifier
                                        self.canExecuteFlag = False
                            except:
                                pass
            self.program += ctx.getChild(0).getChild(0).getText() + \
                " [" + ctx.getChild(0).getChild(2).getText() + "] = " + value
        else:  # Its a variable declaration or value assignation
            value = GTI.getTextInterval(ctx, ctx.getChild(2))
            value = self.processKeyword(value)  # Get translated value
            # Checks if the value is an expression
            expressionList = self.checkIfExpression(value)
            if (any(expressionList)):  # If value is an expression
                if not self.localsFlag:  # If is a global declaration or value assignation
                    self.canExecuteFlag = False  # Will not be executed
            else:
                if self.localsFlag:  # If is a local declaration or value assignation
                    # If is the first definition
                    if (not ctx.getChild(0).getText() in self.locals) & (not ctx.getChild(0).getText() in self.globals):
                        try:
                            # Checks if the value is an integer number
                            self.locals[ctx.getChild(0).getText()] = int(value)
                        except:
                            try:
                                self.locals[ctx.getChild(0).getText()] = float(
                                    value)  # Checks if the value is a float number
                            except:
                                try:
                                    # Checks if the value is a boolean
                                    if value == "True":  # Checks if the value is true
                                        self.locals[ctx.getChild(
                                            0).getText()] = True
                                    elif value == "False":  # Checks if the value is false
                                        self.locals[ctx.getChild(
                                            0).getText()] = False
                                    else:
                                        if "\"" in value:  # Checks if the value is a string
                                            self.locals[ctx.getChild(
                                                0).getText()] = str(value)
                                        else:  # Is an identifier
                                            self.canExecuteFlag = False
                                except:
                                    pass
                else:  # If is a global declaration or value assignation
                    try:
                        self.globals[ctx.getChild(0).getText()] = int(
                            value)  # Checks if the value is an integer number
                    except:
                        try:
                            self.globals[ctx.getChild(0).getText()] = float(
                                value)  # Checks if the value is a float number
                        except:
                            try:
                                # Checks if the value is a boolean
                                if value == "True":  # Checks if the value is true
                                    self.globals[ctx.getChild(
                                        0).getText()] = True
                                elif value == "False":  # Checks if the value is false
                                    self.globals[ctx.getChild(
                                        0).getText()] = False
                                else:
                                    if "\"" in value:  # Checks if the value is a string
                                        self.globals[ctx.getChild(
                                            0).getText()] = str(value)
                                    else:  # Is an identifier
                                        self.canExecuteFlag = False
                            except:
                                pass
            self.program += ctx.getChild(0).getText() + " = " + value

    # Exit a parse tree produced by fodcatParser#assignment.
    # @param ctx:fodcatParser.CommandContext
    def exitAssignment(self, ctx: fodcatParser.AssignmentContext):
        self.program += "\n"

    # Enter a parse tree produced by fodcatParser#gpio.
    # @param ctx:fodcatParser.CommandContext
    def enterGpio(self, ctx: fodcatParser.GpioContext):
        # if not self.ifFlag:
        if not (self.ifFlag | self.whileFlag):
            self.indent(self.countIndent)  # Set proper indentation level
            if ctx.getChild(3).getText() == ":=":
                value = GTI.getTextInterval(ctx, ctx.getChild(4))
                value = self.processKeyword(value)  # Get translated value
                self.program += ctx.getChild(0).getText() + \
                    self.processKeyword(
                        "." + ctx.getChild(2).getText()) + " = " + value
            else:
                self.program += self.processKeyword(ctx.getText())

    # Exit a parse tree produced by fodcatParser#gpio.
    # @param ctx:fodcatParser.CommandContext
    def exitGpio(self, ctx: fodcatParser.GpioContext):
        # if not self.ifFlag:
        if not (self.ifFlag | self.whileFlag):
            self.program += "\n"

    # Enter a parse tree produced by fodcatParser#sleep.
    # @param ctx:fodcatParser.CommandContext
    def enterSleep(self, ctx: fodcatParser.SleepContext):
        self.indent(self.countIndent)  # Set proper indentation level
        if not self.hasSleepFlag:
            self.program = "from time import sleep\n" + \
                self.program  # Import the GPIO class
            self.hasSleepFlag = True
        self.program += "sleep(" + ctx.getChild(2).getText() + ")"

    # Exit a parse tree produced by fodcatParser#sleep.
    # @param ctx:fodcatParser.CommandContext
    def exitSleep(self, ctx: fodcatParser.SleepContext):
        self.program += "\n"

    #
    # ************************************************************************************
    # Expressions
    # ************************************************************************************

    # Enter a parse tree produced by fodcatParser#expression.
    # @param ctx:fodcatParser.CommandContext
    def enterExpression(self, ctx: fodcatParser.ExpressionContext):
        self.expressionFlag = True  # Set the expression flag to true
        pass

    # Exit a parse tree produced by fodcatParser#expression.
    # @param ctx:fodcatParser.CommandContext
    def exitExpression(self, ctx: fodcatParser.ExpressionContext):
        self.expressionFlag = False  # Set the expression flag to false
        pass

    # ************************************************************************************
    # Declarations
    # ************************************************************************************

    # Enter a parse tree produced by fodcatParser#function_declaration.
    # @param ctx:fodcatParser.CommandContext
    def enterFunction_declaration(self, ctx: fodcatParser.Function_declarationContext):
        self.program += "global " + ctx.getChild(1).getText() + "\n"
        # Is function declaration without parameters
        if ctx.getChild(3).getChildCount() == 0:
            self.globals[ctx.getChild(1).getText(
            )] = "def " + ctx.getChild(1).getText() + "()"
            self.program += "def " + ctx.getChild(1).getText() + "():\n"
        else:  # Is function declaration with parameters
            self.globals[ctx.getChild(1).getText()] = "def " + ctx.getChild(1).getText() + \
                "(" + ctx.getChild(3).getText().replace('[]', '') + ")"
            self.program += "def " + \
                ctx.getChild(1).getText(
                ) + "(" + ctx.getChild(3).getText().replace('[]', '') + "):\n"
        self.localsFlag = True  # Set the local definitions flag to true
        self.countIndent += 1  # Add 1 to the indentation level
        if ctx.getChildCount() == 6:  # If the function body is empty
            self.indent(self.countIndent)  # Set proper indentation level
            self.program += "pass\n"  # Do nothing

    # Exit a parse tree produced by fodcatParser#function_declaration.
    # @param ctx:fodcatParser.CommandContext
    def exitFunction_declaration(self, ctx: fodcatParser.Function_declarationContext):
        self.countIndent = 0  # Set the indentation level to 0
        self.program += "\n"
        self.localsFlag = False  # Set the local definitions flag to false

    # Enter a parse tree produced by fodcatParser#array_declaration.
    # @param ctx:fodcatParser.CommandContext
    def enterArray_declaration(self, ctx: fodcatParser.Array_declarationContext):
        self.indent(self.countIndent)  # Set proper indentation level
        valueFlag = False  # Value is an expression
        if self.localsFlag:  # If is a local array declaration
            value = ctx.getChild(3).getText()
            if value in self.locals:
                value = int(self.locals[value])
            elif value in self.globals:
                value = int(self.globals[value])
            else:
                valueFlag = True  # Value is an expression
            try:
                # Checks if the value is an integer number
                self.locals[ctx.getChild(0).getText()] = [None] * int(value)
            except:
                if(not valueFlag):  # Is an identifier
                    self.locals[ctx.getChild(0).getText()] = [None] * value
                else:  # Is an expression
                    self.locals[ctx.getChild(0).getText()] = [None]
                self.canExecuteFlag = False
            # self.locals[ctx.getChild(0).getText()] = [None] * int(ctx.getChild(3).getText())
        else:  # If is a global array declaration
            value = ctx.getChild(3).getText()
            if value in self.globals:
                value = int(self.globals[value])
            else:
                valueFlag = True  # Value is an expression
            try:
                # Checks if the value is an integer number
                self.globals[ctx.getChild(0).getText()] = [None] * int(value)
            except:  # Is an identifier
                if (not valueFlag):  # Is an identifier
                    self.globals[ctx.getChild(0).getText()] = [None] * value
                else:  # Is an expression
                    self.globals[ctx.getChild(0).getText()] = [None]
                self.canExecuteFlag = False
            # self.globals[ctx.getChild(0).getText()] = [None] * int(ctx.getChild(3).getText())
        self.program += ctx.getChild(0).getText() + \
            " = [None] * " + ctx.getChild(3).getText()

    # Exit a parse tree produced by fodcatParser#array_declaration.
    # @param ctx:fodcatParser.CommandContext
    def exitArray_declaration(self, ctx: fodcatParser.Array_declarationContext):
        self.program += "\n"

    # Enter a parse tree produced by fodcatParser#gpio_declaration.
    # @param ctx:fodcatParser.CommandContext
    def enterGpio_declaration(self, ctx: fodcatParser.Gpio_declarationContext):
        self.hasGPIOFlag = True
        if ctx.getChild(3).getText() == ".":  # Is a variable brightness LED declaration
            if not self.hasVLEDFlag:
                self.program = "from gpiozero import PWMLED\n" + \
                    self.program  # Import the GPIO class
                self.hasVLEDFlag = True  # Has a variable LED definition
            value = GTI.getTextInterval(ctx, ctx.getChild(6))
            value = self.processKeyword(value)  # Get translated value
            # Checks if the value is an expression
            expressionList = self.checkIfExpression(value)
            if (any(expressionList)):  # If value is an expression
                if not self.localsFlag:  # If is a global declaration or value assignation
                    self.canExecuteFlag = False  # Will not be executed
            else:
                try:
                    self.globals[ctx.getChild(0).getText()] = int(
                        value)  # Checks if the value is an integer number
                except:
                    pass
            self.program += ctx.getChild(0).getText() + \
                " = PWMLED(" + value + ")"

        elif ctx.getChild(4).getChildCount() == 5:  # Is an angular servo declaration
            if not self.hasAngularServoFlag:
                self.program = "from gpiozero import AngularServo\n" + \
                    self.program  # Import the GPIO class
                self.hasAngularServoFlag = True
            value = GTI.getTextInterval(ctx, ctx.getChild(4))
            value = self.processKeyword(value)  # Get translated value
            # expressionList = self.checkIfExpression(value) # Checks if the value is an expression
            value = value.split(",")  # Separate the parameters
            expressionList = list()
            i = 0
            while (i < 3):  # Check if is a negative number or a expression
                # Checks if the value is an expression
                expressionList.append(self.checkIfExpression(value[i]))
                i += 1
            # If value is an expression
            if (any(expressionList[0]) & any(expressionList[1]) & any(expressionList[2])):
                if not self.localsFlag:  # If is a global declaration or value assignation
                    self.canExecuteFlag = False  # Will not be executed
            else:
                try:
                    self.globals[ctx.getChild(0).getText()] = [int(value[0]), int(
                        value[1]), int(value[2])]  # Checks if the values are an integer number
                except:
                    pass
            self.program += ctx.getChild(0).getText() + " = AngularServo(" + ctx.getChild(4).getChild(0).getText(
            ) + ", min_angle=" + ctx.getChild(4).getChild(2).getText() + ", max_angle=" + ctx.getChild(4).getChild(4).getText() + ")"

        elif ctx.getChild(2).getText() == "boton":  # Is a button declaration
            if not self.hasButtonFlag:
                self.program = "from gpiozero import Button\n" + \
                    self.program  # Import the GPIO class
                self.hasButtonFlag = True
            value = GTI.getTextInterval(ctx, ctx.getChild(4))
            value = self.processKeyword(value)  # Get translated value
            # Checks if the value is an expression
            expressionList = self.checkIfExpression(value)
            if (any(expressionList)):  # If value is an expression
                if not self.localsFlag:  # If is a global declaration or value assignation
                    self.canExecuteFlag = False  # Will not be executed
            else:
                try:
                    self.globals[ctx.getChild(0).getText()] = int(
                        value)  # Checks if the value is an integer number
                except:
                    pass
            self.program += ctx.getChild(0).getText() + \
                " = Button(" + ctx.getChild(4).getText() + ")"

        elif ctx.getChild(2).getText() == "led":  # Is a LED declaration
            if not self.hasLEDFlag:
                self.program = "from gpiozero import LED\n" + \
                    self.program  # Import the GPIO class
                self.hasLEDFlag = True
            value = GTI.getTextInterval(ctx, ctx.getChild(4))
            value = self.processKeyword(value)  # Get translated value
            # Checks if the value is an expression
            expressionList = self.checkIfExpression(value)
            if (any(expressionList)):  # If value is an expression
                if not self.localsFlag:  # If is a global declaration or value assignation
                    self.canExecuteFlag = False  # Will not be executed
            else:
                try:
                    self.globals[ctx.getChild(0).getText()] = int(
                        value)  # Checks if the value is an integer number
                except:
                    pass
            self.program += ctx.getChild(0).getText() + \
                " = LED(" + ctx.getChild(4).getText() + ")"

        elif ctx.getChild(2).getText() == "servo":  # Is a servo declaration
            if not self.hasServoFlag:
                self.program = "from gpiozero import Servo\n" + \
                    self.program  # Import the GPIO class
                self.hasServoFlag = True
            value = GTI.getTextInterval(ctx, ctx.getChild(4))
            value = self.processKeyword(value)  # Get translated value
            # Checks if the value is an expression
            expressionList = self.checkIfExpression(value)
            if (any(expressionList)):  # If value is an expression
                if not self.localsFlag:  # If is a global declaration or value assignation
                    self.canExecuteFlag = False  # Will not be executed
            else:
                try:
                    self.globals[ctx.getChild(0).getText()] = int(
                        value)  # Checks if the value is an integer number
                except:
                    pass
            self.program += ctx.getChild(0).getText() + \
                " = Servo(" + ctx.getChild(4).getText() + ")"

    # Exit a parse tree produced by fodcatParser#gpio_declaration.
    # @param ctx:fodcatParser.CommandContext
    def exitGpio_declaration(self, ctx: fodcatParser.Gpio_declarationContext):
        self.program += "\n"


# Verify the device where the compiler is been executed
# @return Boolean value if is running over a Raspberry Pi
def verifyDevice():
    if platform.system() == "Linux":  # If is a Linux OS
        command = "\n cat /proc/cpuinfo"
        all_info = subprocess.check_output(command, shell=True).strip()
        all_info = all_info.decode("utf-8")
        for line in all_info.split("\n"):
            if "model name" in line:
                # If is running over an ARM processor (Raspberry Pi)
                if "ARM" in re.sub(".*model name.*:", "", line, 1):
                    return True
    return False


def main(argv):
    # if (len(argv) == 4) & (argv[3] != "-f"):
    #     print("Error, la bandera para producir un archivo es -f.")
    #     quit()

    canGPIOFlag = verifyDevice()
    inp = FileStream(argv[1])
    lexer = fodcatLexer(inp)
    stream = CommonTokenStream(lexer)
    parser = fodcatParser(stream)
    tree = parser.program()

    if (len(argv) == 3):
        # if (len(argv) == 4):
        outfile = argv[2] + ".py"
        output = open(str(outfile), "w")
        fcat = CodeGenerator(output, canGPIOFlag)
    # elif (len(argv) == 1):
    #     output = None
    #     fcat = CodeGenerator(output, canGPIOFlag)
    else:
        print("Error, instruccion incorrecta.")
        quit()

    walker = ParseTreeWalker()
    walker.walk(fcat, tree)
    if (len(argv) == 4):
        output.close()


if __name__ == '__main__':
    main(sys.argv)
