import { Address } from "./address";

class LocationHolder {

  public data: Address[];
  private address_id: number

  constructor () {
    this.data = [];
    this.address_id = 0;
  }

  /**
   * 
   * @param {never} value the value to be allocated
   * @returns {number} - the address id
   */
  allocate (value: unknown): number {
    const id = this.address_id;
    // console.log("Allocation address "+ id);
    const address = new Address(id, value);
    this.data.push(address);
    this.address_id += 1;
    return id;
  }

  /**
   * 
   * @param {number} id 
   */
  deallocate (id: number): boolean {
    const index = this.findIndex(id);
    // console.log("Deallocation address "+ id);
    if(index !== -1) {
      this.data.splice(index, 1);
      return true;
    }
    return false;
  }

  /**
   * 
   * @param {number} id 
   * @returns {Address} the address identified by id
   */
  find (id: number): Address | undefined {
    let beg = 0
    let end = this.data.length;
    // console.log("Finding address "+id);
    while (beg < end) {
      const med = Math.floor((beg + end)/2);
      const address = this.getAddressAt(med);
      if(address.id === id) {
        return address;
      } else if (id > address.id) {
        beg = med;
      } else {
        end = med
      }
    }
    return undefined;
  }

  getAddressAt (pos: number): Address {
    return this.data[pos];
  }

  /**
   * 
   * @param {number} id address id
   * @returns {number} the index of the address identified by id
   */
  findIndex (id: number): number {
    let beg = 0
    let end = this.data.length;
    while (beg < end) {
      const med = Math.floor((beg + end)/2);
      const address = this.getAddressAt(med);
      if(address.id === id) {
        return med;
      } else if (id > address.id) {
        beg = med;
      } else {
        end = med
      }
    }
    return -1;
  }

  updateAddress (id: number, value: unknown): void {
    const index = this.findIndex(id);
    if(index === -1) {
      throw new Error("Invalid address..." + id);
    }
    this.data[index].value = value;
  }

  clear (): void {
    for (let i = 0; i < this.data.length; i += 1) {
      delete this.data[i];
    }
    this.data = [];
    this.address_id = 0;
  }
}

const inner_ref = new LocationHolder();

export const Location = Object.freeze({
  allocate: inner_ref.allocate.bind(inner_ref),
  deallocate: inner_ref.deallocate.bind(inner_ref),
  find: inner_ref.find.bind(inner_ref),
  updateAddress: inner_ref.updateAddress.bind(inner_ref),
  clear: inner_ref.clear.bind(inner_ref),
  size: () => inner_ref.data.length
});