import { IStoreValue } from "./istore_value";
import { ArrayType } from "../../../typeSystem/array_type";
import { IType } from "../../../typeSystem/itype";
import { Type } from "../../../typeSystem/type";
import { StoreValueAddress } from "./store_value_address";

export class ArrayStoreValue implements IStoreValue {

  public type: IType; 
  public id?: String;
  public isConst: boolean;
  public lines: number;
  public columns?: number;
  public values: StoreValueAddress[];

  constructor(type: ArrayType, values: StoreValueAddress[], lines: number, columns?: number, id?: String, isConst = false) {
    this.type = type;
    this.id = id;
    this.isConst = isConst
    this.values = values;
    this.lines = lines;
    this.columns = columns;
  }

  get (): StoreValueAddress[] {
    return this.values;
  }

  /**
   * @deprecated
   * Potential not necessary since array access is occuring directly in the StoreObject
   * @param line 
   * @param column 
   */
  getAt (line: number, column?: number): IStoreValue {
    console.log("Column ", column);
    if(this.isVector()) {
      if(column != null) {
        throw new Error(this.id + " is not a matrix!");
      }
      column = line;
      line = 0;
    } else if (column == null) {
      const values: StoreValueAddress[] = [];
      for(let col = 0; col < this.columns!; col += 1) {
        const index = line * this.lines + col;
        values.push(this.values[index]);
      }
      const array_type = this.type as ArrayType
      const vector_type = new ArrayType(array_type.innerType as Type, array_type.dimensions - 1);
      return new ArrayStoreValue(vector_type, values, this.columns!, 0, this.id, this.isConst);
    }
    const index = line * this.lines + column;
    console.log("Get at: ",index);
    return this.values[index];
  }

  setAt (value: IStoreValue, line:number, column?: number): void {
    let used_dims = 1;
    if(column != null) {
      used_dims += 1;
    }
    let actual_line = line;
    let actual_column = column;
    if(!(this.type as ArrayType).canAccept(value.type, used_dims)) {
      throw new Error("!!!Internal Error: Attempting to insert an invalid value inside array "+this.id);
    }
    if(this.isVector()) {
      if(column != null) {
        throw new Error(this.id + " is not a matrix!");
      }
      actual_column = actual_line;
      actual_line = 0;
    } else if (column == null) {
      if(!(value instanceof ArrayStoreValue)) {
        throw new Error("Attempting to insert a single value as a line of matrix " + this.id);
      }
      const actual_values = value.get()
      for(let i = 0; i < this.columns!; i += 1) {
        const pos = actual_line * this.columns! + i;
        const val = actual_values[i]
        this.values[pos] = new StoreValueAddress(value.type, val.get(), actual_line, i, this.id, this.isConst);
      }
    }
    const columns =  this.columns == null ? 0 : this.columns;
    const pos = actual_line * columns + actual_column!;
    this.values[pos] = new StoreValueAddress(value.type, value.get(), line, column, this.id, this.isConst);
  }

  inStore () {
    return this.id != null;
  }

  isVector (): boolean {
    return (this.type as ArrayType).dimensions == 1;
  }
}
