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

export function createOutputFun () {
  const writeFunction = async function (store, _) {
    const totalSV = store.applyStore('p1.0');
    const total = totalSV.get();
    for (let i = 1; i < total; i += 1) {
      const val = store.applyStore(`p1.${i}`);
      this.output.sendOutput(convertToString(val.get(), val.type));
    }
    store.mode = Modes.RETURN;
    return store;
  }
  const block = new Commands.CommandBlock([], [new Commands.SysCall(writeFunction)]);
  const func = new Commands.Function('$write', Types.VOID,
    [new Commands.FormalParameter(Types.ALL, 'p1', false, true)],
    block);
  return func;
}

export function createInputFun () {
  const readFunction = async function (store, _) {
    const text = await this.input.requestInput();
    const typeToConvert = store.applyStore('p1').type;
    let type = null
    let result = null;
    try {
      if (typeToConvert.isCompatible(Types.INTEGER)) {
        result = toInt(text.trim()).trunc();
        type = Types.INTEGER;
      } else if (typeToConvert.isCompatible(Types.REAL)) {
        result = toReal(text.trim())
        type = Types.REAL;
      } else if (typeToConvert.isCompatible(Types.BOOLEAN)) {
        result = toBool(text.trim())
        type = Types.BOOLEAN;
      } else if (typeToConvert.isCompatible(Types.STRING)) {
        result = toString(text)
        type  = Types.STRING;
      } else {
        throw new Error("!!!!Critical error: Unknown type in readFunction!!!!");
      }
    } catch (_) {
      if(this.mode == Modes.ABORT) {
        store.mode = Modes.RETURN;
        return store;
      }
      const stringInfo = typeToConvert.stringInfo()[0]
      const realObject = store.getStoreObject("p1");
      if (realObject.getReferenceDimension() > 0) {
        const arrayInfo = realObject.type.stringInfo()[0];
        const dim = realObject.getReferenceDimension();
        throw ProcessorErrorFactory.invalid_read_type_array(text, stringInfo.type, stringInfo.dim,
          realObject.getRefObj(), arrayInfo.type, dim, this.function_call_stack.pop());
      }
      throw ProcessorErrorFactory.invalid_read_type(text, stringInfo.type, stringInfo.dim,
        realObject.getRefObj(), this.function_call_stack.pop());
    }
    const stoValue = new StoreValue(type, result);
    store.updateStore('p1', stoValue);
    store.mode = Modes.RETURN;
    return store;
  }
  const block = new Commands.CommandBlock([],  [new Commands.SysCall(readFunction)]);
  const func = new Commands.Function('$read', Types.VOID,
    [new Commands.FormalParameter(Types.ALL, 'p1', true)],
    block);
  return func;
}