import Controller from './Controller';
import HasSettings from './HasSettings';
import NumberSetting from './Settings/NumberSetting';

type MatrixAxis = {
  [key: string]: number;
};

type MatrixInputs = { [key: string]: Controller<number, any> | NumberSetting };

type MatrixOutputs = {
  [key: string]: NumberSetting;
}

type MatrixSettings = {[key: string]: {[key: string]: NumberSetting}};

export class MatrixControlledNumberSetting extends NumberSetting {
  constructor() {
    super(0);
  }
}

export default class Matrix extends HasSettings<MatrixSettings> {
  protected inputValues: MatrixAxis;
  protected inputs: MatrixInputs;
  protected outputs: MatrixOutputs;
  protected outputMappers: {[key: string]: (input: number) => number}
  getDefaultSettings() {
    return {};
  }

  constructor(givenSettings?: MatrixSettings) {
    super(givenSettings);
    this.inputs = {};
    this.inputValues = {};
    this.outputs = {};
    this.outputMappers = {};
    if (givenSettings) {
      this.settings = givenSettings;
    }
  }

  registerInput(name: string, input: Controller<number, any> | NumberSetting) {
    this.inputs[name] = input;
    this.inputValues[name] = 0;
    Object.keys(this.settings).forEach((outputName) => {
      if (!this.settings[outputName][name]) {
        this.settings[outputName][name] = new NumberSetting(0);
      }
    });
  }

  registerOutput(name: string, setting: NumberSetting, mapper?: (input: number) => number) {
    this.outputs[name] = setting;
    if (mapper) {
      this.outputMappers[name] = mapper;
    }
    if (!this.settings[name]) {
      this.settings[name] = {};
    }
    Object.keys(this.inputs).forEach((inputName) => {
      if (!this.settings[name][inputName]) {
        this.settings[name][inputName] = new NumberSetting(0);
      }
    });
  }

  process() {
    Object.keys(this.inputs).forEach((k) => {
      const input = this.inputs[k];
      if (input instanceof Controller) {
        this.inputValues[k] = input.process();
      } else {
        this.inputValues[k] = input.value;
      }
    });

    Object.keys(this.outputs).forEach((outputKey) => {
      let sum = 0;
      Object.keys(this.settings[outputKey]).forEach((inputKey) => {
        sum += this.inputValues[inputKey] * this.settings[outputKey][inputKey].value;
      });
      if (this.outputMappers[outputKey]) {
        this.outputs[outputKey].value = this.outputMappers[outputKey](sum);
      } else {
        this.outputs[outputKey].value = sum;
      }
    });
  }
}
