import * as Models from '../visualUI/ivprog_elements';
import { LocalizedStrings } from "./../services/localizedStringsService";
import * as VariableValueMenuManagement from '../visualUI/commands/variable_value_menu';
import * as CodeParser from '../visualUI/commands/generic_expression';

function parseBoolean (var_value, dimensions) {
  if (dimensions == 0)
    return var_value == LocalizedStrings.getUI("logic_value_true");
  if (dimensions == 1) {
    var final_array = [];
    var_value.forEach(function(el){
      final_array.push(el == LocalizedStrings.getUI("logic_value_true"));
    });
    return final_array;
  }
  if (dimensions == 2) {
    var final_array = [];
    var_value.forEach(function(row){
      var temp = [];
      row.forEach(function(el){
        temp.push(el == LocalizedStrings.getUI("logic_value_true"));
      });
      final_array.push(temp);
    });
    return final_array;
  }
}

function parseGlobal (global_obj) {

  var new_global = new Models.Variable(
    global_obj.type, 
    global_obj.name, 
    global_obj.value);
  
  new_global.is_constant = global_obj.is_const;
  new_global.columns = global_obj.columns;
  new_global.dimensions = global_obj.dimension;
  new_global.rows = global_obj.rows;

  if (global_obj.type == "boolean")
    new_global.value = parseBoolean(global_obj.value, global_obj.dimension);
	
	window.program_obj.addGlobal(new_global);
}

function parseParameter (parameter_obj) {

  const new_parameter = new Models.Variable(
    parameter_obj.type,
    parameter_obj.name,
    null,
    parameter_obj.dimension,
    parameter_obj.is_const,
    parameter_obj.rows,
    parameter_obj.columns,
    parameter_obj.reference
  );

  new_parameter.value = parameter_obj.value;

  if (parameter_obj.type == "boolean" && parameter_obj.value)
    new_parameter.value = parseBoolean(parameter_obj.value, parameter_obj.dimension);

  return new_parameter;
}

function parseFunction (function_obj) {

  const new_function = new Models.Function(
    function_obj.name, 
    function_obj.return_type, 
    function_obj.return_dimensions, 
    [], 
    false, 
    false, 
    []);
  
  if (!new_function.name) {
    new_function.name = LocalizedStrings.getUI("start");
    new_function.is_main = true;
  }

  if (function_obj.parameters_list) {
    function_obj.parameters_list.forEach(function(el){
      new_function.parameters_list.push(parseParameter(el));
    });
  }

  if (function_obj.variables_list) {
    function_obj.variables_list.forEach(function(el){
      new_function.variables_list.push(parseParameter(el));
    });
  }

  window.program_obj.addFunction(new_function);
  
}

export function parserCodeVisual (code_obj = null) {

  window.conteudo = code_obj

  // Globals:
  window.program_obj.globals = [];
  code_obj.globals.forEach(parseGlobal);

  // Functions:
  window.program_obj.functions = [];
  code_obj.functions.forEach(parseFunction);

  // Commands: 
  window.program_obj.functions.forEach(function(preparedFunction) {
    code_obj.functions.forEach(function(rawFunction) {
      if ((preparedFunction.name == rawFunction.name) 
          || 
          (!rawFunction.name && preparedFunction.name == LocalizedStrings.getUI("start"))) {
            preparedFunction.commands = parseCommands(rawFunction.commands, preparedFunction);
          }
    })
  });

}

function parseCommands (commands_block, function_obj) {

  if (Array.isArray(commands_block)) {
    var temp = [];
    commands_block.forEach(function(command) {
      temp.push(parseCommands(command, function_obj));
    });
    return temp;
  }
  else {
    switch (commands_block.type) {
      case Models.COMMAND_TYPES.reader:
        return parseReader(commands_block, function_obj);

      case Models.COMMAND_TYPES.return:
        return parseReturn(commands_block, function_obj);

      case Models.COMMAND_TYPES.writer:
        return parseWriter(commands_block, function_obj);

      case Models.COMMAND_TYPES.break:
        return parseBreak(commands_block, function_obj);

      case Models.COMMAND_TYPES.comment:
        return parseComment(commands_block, function_obj);

      case Models.COMMAND_TYPES.attribution:
        return parseAttribution(commands_block, function_obj);

      case Models.COMMAND_TYPES.functioncall:
        return parseFunctionCall(commands_block, function_obj);

      case Models.COMMAND_TYPES.iftrue:
        return parseIfTrue(commands_block, function_obj);

      case Models.COMMAND_TYPES.repeatNtimes:
        return parseRepeatNTimes(commands_block, function_obj);

      case Models.COMMAND_TYPES.whiletrue:
        return parseWhileTrue(commands_block, function_obj);

      case Models.COMMAND_TYPES.dowhiletrue:
        return parseDoWhileTrue(commands_block, function_obj);

      case Models.COMMAND_TYPES.switch:
        return parseSwitch(commands_block, function_obj);
    }
    return null;
  }
  
}

function parseReader (command, function_obj) {
  var temp = CodeParser.expressionParserCodeVisual(command.variable, function_obj);

  if (Array.isArray(temp) && temp[0])
    temp = temp[0];

  return new Models.Reader(
    temp
  );
}

function parseReturn (command, function_obj) {

  var temp = CodeParser.expressionParserCodeVisual(command.expression, function_obj);

  return new Models.Return(
    temp
  );

}

function parseWriter (command, function_obj) {

  var temp = CodeParser.expressionParserCodeVisual(command.content[0], function_obj);

  return new Models.Writer(
    temp,
    command.newLine
  );
}

function parseBreak (command, function_obj) {
    return new Models.Break();
}

function parseAttribution (command, function_obj) {

  var variable = CodeParser.expressionParserCodeVisual(command.variable, function_obj);
  var expression = CodeParser.expressionParserCodeVisual(command.expression, function_obj);

  if (Array.isArray(variable)) 
    variable = variable[0];

  return new Models.Attribution(
    variable,
    expression
  );
}

function parseComment (command, function_obj) {
  // TODO
  return new Models.Comment(
    null
  );
}

function parseFunctionCall (command, function_obj) {

  var parameters = [];
  if (command.parameters_list) {
    command.parameters_list.forEach(function(el) {
      var temp = CodeParser.expressionParserCodeVisual(el, function_obj);
      if (temp.content === 0) temp.content = "0";
      parameters.push(temp[0]);
    });
  }

  var function_called = CodeParser.searchFunction(command.name);

  var temp = new Models.VariableValueMenu(VariableValueMenuManagement.VAR_OR_VALUE_TYPES.only_function, null, null, null, false);
  temp.function_called = function_called;
  temp.parameters_list = parameters;
  
  return new Models.FunctionCall(
    temp, 
    null
  );
}

function parseIfTrue(command, function_obj) {

  var expression = CodeParser.expressionParserCodeVisual(command.expression, function_obj);
  var ifTrueBlock = parseCommands(command.ifTrue, function_obj);
  var ifFalseBlock = parseCommands(command.ifFalse, function_obj);

  return new Models.IfTrue(expression, ifTrueBlock, ifFalseBlock);

}

function parseRepeatNTimes(command, function_obj) {

  var var_attribution = CodeParser.expressionParserCodeVisual(command.var_attribution, function_obj);
  var_attribution = var_attribution[0];

  var expression1 = CodeParser.expressionParserCodeVisual(command.var_initial, function_obj);
  expression1 = expression1[0];

  var expression2 = CodeParser.expressionParserCodeVisual(command.condition, function_obj);

  if (expression2[0].item == 'minus') {
    expression2 = expression2[1];
    expression2.content *= -1;
  } else
    expression2 = expression2[0];

  var var_step = CodeParser.expressionParserCodeVisual(command.step_expression, function_obj);

  var commands_block = parseCommands(command.commands, function_obj);

  var operator = command.step_expression[0].value == '+' 
                  ? Models.ARITHMETIC_TYPES.plus
                  : Models.ARITHMETIC_TYPES.minus;

  var expression3 = new Models.ExpressionElement(
    Models.EXPRESSION_ELEMENTS.exp_op_exp, 
    [ 
      null,
      operator, 
      var_step[1]
    ]);

  return new Models.RepeatNTimes(
    var_attribution,
    new Models.VariableValueMenu(VariableValueMenuManagement.VAR_OR_VALUE_TYPES.only_variable, null, null, null, false),
    expression1,
    expression2,
    expression3, 
    commands_block);

}

function parseWhileTrue (command, function_obj) {

  var expression = CodeParser.expressionParserCodeVisual(command.expression, function_obj);
  var commands = parseCommands(command.commands, function_obj);

  return new Models.WhileTrue(
    expression, 
    commands
  );
}

function parseDoWhileTrue (command, function_obj) {
  
  var expression = CodeParser.expressionParserCodeVisual(command.expression, function_obj);
  var commands = parseCommands(command.commands, function_obj);

  return new Models.DoWhileTrue(
    expression, 
    commands
  );
}

function parseSwitch (command, function_obj) {

  var expression = CodeParser.expressionParserCodeVisual(command.expression, function_obj);

  var sc = [];
  if (command.cases) {
    command.cases.forEach(function(case_el) {

      var temp_exp = CodeParser.expressionParserCodeVisual(case_el.expression, function_obj);
      var temp_commands = parseCommands(case_el.commands, function_obj);
      var temp_case = new Models.SwitchCase(temp_exp[0], temp_commands);

      sc.push(temp_case);
    })
  }

	return new Models.Switch(
    expression[0], 
    sc
  );
}