import * as Commands from './../../ast/commands';
import { Types } from './../../typeSystem/types';
import { toInt } from "./../../typeSystem/parsers";
import { Modes } from '../modes';
import { StoreValue } from '../store/value/store_value';
import { ProcessorErrorFactory } from '../error/processorErrorFactory';

/*
*  substring
*  length
*  uppercase
*  lowercase
*  charAt
**/

export function createSubstringFun () {
  const substringFun = async (sto, _) => {
    const str = sto.applyStore("str");
    const start = sto.applyStore("start");
    const end = sto.applyStore("end");
    const result = str.get().substring(start.get(), end.get());
    const temp = new StoreValue(Types.STRING, result);
    sto.insertStore("$", temp);
    sto.mode = Modes.RETURN;
    return sto;
  };

  const block = new Commands.CommandBlock([],  [new Commands.SysCall(substringFun)]);
  const func = new Commands.Function('$substring', Types.STRING,
    [new Commands.FormalParameter(Types.STRING, 'str', false),
    new Commands.FormalParameter(Types.INTEGER, 'start', false),
    new Commands.FormalParameter(Types.INTEGER, 'end', false)],
    block);
  return func;
}

export function createLengthFun () {
  const lengthFun = async (sto, _) => {
    const str = sto.applyStore("str");
    const temp = new StoreValue(Types.INTEGER, toInt(str.value.length));
    sto.insertStore("$", temp);
    sto.mode = Modes.RETURN;
    return sto;
  }
  const block = new Commands.CommandBlock([],  [new Commands.SysCall(lengthFun)]);
  const func = new Commands.Function('$length', Types.INTEGER,
    [new Commands.FormalParameter(Types.STRING, 'str', false)],
    block);
  return func;
}

export function createUppercaseFun () {
  const uppercaseFun = async (sto, _) => {
    const str = sto.applyStore("str");
    const temp = new StoreValue(Types.STRING, str.get().toUpperCase());
    sto.insertStore("$", temp);
    sto.mode = Modes.RETURN;
    return sto;
  }
  const block = new Commands.CommandBlock([],  [new Commands.SysCall(uppercaseFun)]);
  const func = new Commands.Function('$uppercase', Types.STRING,
    [new Commands.FormalParameter(Types.STRING, 'str', false)],
    block);
  return func;
}

export function createLowercaseFun () {
  const lowercaseFun = async (sto, _) => {
    const str = sto.applyStore("str");
    const temp = new StoreValue(Types.STRING, str.get().toLowerCase());
    sto.insertStore("$", temp);
    sto.mode = Modes.RETURN;
    return sto;
  }
  const block = new Commands.CommandBlock([],  [new Commands.SysCall(lowercaseFun)]);
  const func = new Commands.Function('$lowercase', Types.STRING,
    [new Commands.FormalParameter(Types.STRING, 'str', false)],
    block);
  return func;
}

export function createrCharAtFun () {
  const charAtFun = async function (sto, _) {
    const str = sto.applyStore("str");
    const idx = sto.applyStore("index");
    if (idx.get().toNumber() < 0 || idx.get().toNumber() >= str.get().length) {
      throw ProcessorErrorFactory.invalid_string_index(idx.get().toNumber(), str.get(),
        this.function_call_stack.pop());
    }
    const temp = new StoreValue(Types.CHAR, str.get().charAt(idx.get().toNumber()));
    sto.insertStore("$", temp);
    sto.mode = Modes.RETURN;
    return sto;
  }
  const block = new Commands.CommandBlock([],  [new Commands.SysCall(charAtFun)]);
  const func = new Commands.Function('$charAt', Types.CHAR,
    [new Commands.FormalParameter(Types.STRING, 'str', false),
    new Commands.FormalParameter(Types.INTEGER, 'index', false)],
    block);
  return func;
}
