import { IMap } from "./types";
import { Terminal } from "./Terminal";

export let Aliases: IMap<string> = {};
export let GlobalAliases: IMap<string> = {};

export function loadAliases(saveString: string): void {
  if (saveString === "") {
    Aliases = {};
  } else {
    Aliases = JSON.parse(saveString);
  }
}

export function loadGlobalAliases(saveString: string): void {
  if (saveString === "") {
    GlobalAliases = {};
  } else {
    GlobalAliases = JSON.parse(saveString);
  }
}

// Prints all aliases to terminal
export function printAliases(): void {
  for (const name of Object.keys(Aliases)) {
    if (Aliases.hasOwnProperty(name)) {
      Terminal.print("alias " + name + "=" + Aliases[name]);
    }
  }
  for (const name of Object.keys(GlobalAliases)) {
    if (GlobalAliases.hasOwnProperty(name)) {
      Terminal.print("global alias " + name + "=" + GlobalAliases[name]);
    }
  }
}

// Returns true if successful, false otherwise
export function parseAliasDeclaration(dec: string, global = false): boolean {
  const re = /^([\w|!%,@-]+)=(("(.+)")|('(.+)'))$/;
  const matches = dec.match(re);
  if (matches == null || matches.length != 7) {
    return false;
  }

  if (global) {
    addGlobalAlias(matches[1], matches[4] || matches[6]);
  } else {
    addAlias(matches[1], matches[4] || matches[6]);
  }
  return true;
}

function addAlias(name: string, value: string): void {
  if (name in GlobalAliases) {
    delete GlobalAliases[name];
  }
  Aliases[name] = value.trim();
}

function addGlobalAlias(name: string, value: string): void {
  if (name in Aliases) {
    delete Aliases[name];
  }
  GlobalAliases[name] = value.trim();
}

function getAlias(name: string): string | null {
  if (Aliases.hasOwnProperty(name)) {
    return Aliases[name];
  }

  return null;
}

function getGlobalAlias(name: string): string | null {
  if (GlobalAliases.hasOwnProperty(name)) {
    return GlobalAliases[name];
  }
  return null;
}

export function removeAlias(name: string): boolean {
  if (Aliases.hasOwnProperty(name)) {
    delete Aliases[name];
    return true;
  }

  if (GlobalAliases.hasOwnProperty(name)) {
    delete GlobalAliases[name];
    return true;
  }

  return false;
}

/**
 * Returns the original string with any aliases substituted in.
 * Aliases are only applied to "whole words", one level deep
 */
export function substituteAliases(origCommand: string): string {
  const commandArray = origCommand.split(" ");
  if (commandArray.length > 0) {
    // For the alias and unalias commands, dont substite
    if (commandArray[0] === "unalias" || commandArray[0] === "alias") {
      return commandArray.join(" ");
    }

    let somethingSubstituted = true;
    let depth = 0;

    while (somethingSubstituted && depth < 10) {
      depth++;
      somethingSubstituted = false;
      const alias = getAlias(commandArray[0])?.split(" ");
      if (alias != null) {
        somethingSubstituted = true;
        commandArray.splice(0, 1, ...alias);
        //commandArray[0] = alias;
      }
      for (let i = 0; i < commandArray.length; ++i) {
        const alias = getGlobalAlias(commandArray[i])?.split(" ");
        if (alias != null) {
          somethingSubstituted = true;
          commandArray.splice(i, 1, ...alias);
          i += alias.length - 1;
          //commandArray[i] = alias;
        }
      }
    }
  }
  return commandArray.join(" ");
}