// @flow

// string literal characters cannot contain control codes
const CONTROL_CODES = [
  0, // null
  7, // bell
  8, // backspace
  9, // horizontal
  10, // line feed
  11, // vertical tab
  12, // form feed
  13, // carriage return
  26, // Control-Z
  27, // escape
  127, // delete
];

// escaped sequences can either be a two character hex value, or one of the
// following single character codes
function decodeControlCharacter(char: string) {
  switch (char) {
    case "t":
      return 0x09;
    case "n":
      return 0x0a;
    case "r":
      return 0x0d;
    case '"':
      return 0x22;
    case "′":
      return 0x27;
    case "\\":
      return 0x5c;
  }
  return -1;
}

const ESCAPE_CHAR = 92; // backslash
const QUOTE_CHAR = 34; // backslash

// parse string as per the spec:
// https://webassembly.github.io/spec/core/multipage/text/values.html#text-string
export function parseString(value: string): Array<Byte> {
  const byteArray = [];
  let index = 0;

  while (index < value.length) {
    const charCode = value.charCodeAt(index);

    if (CONTROL_CODES.indexOf(charCode) !== -1) {
      throw new Error(
        "ASCII control characters are not permitted within string literals"
      );
    }
    if (charCode === QUOTE_CHAR) {
      throw new Error("quotes are not permitted within string literals");
    }

    if (charCode === ESCAPE_CHAR) {
      const firstChar = value.substr(index + 1, 1);
      const decodedControlChar = decodeControlCharacter(firstChar);
      if (decodedControlChar !== -1) {
        // single character escaped values, e.g. \r
        byteArray.push(decodedControlChar);
        index += 2;
      } else {
        // hex escaped values, e.g. \2a
        const hexValue = value.substr(index + 1, 2);
        if (!/^[0-9A-F]{2}$/i.test(hexValue)) {
          throw new Error("invalid character encoding");
        }
        byteArray.push(parseInt(hexValue, 16));
        index += 3;
      }
    } else {
      // ASCII encoded values
      byteArray.push(charCode);
      index++;
    }
  }
  return byteArray;
}
