type ProfileJson =  {
  name: string;
  customimage?: string;
  mappedbuttons?: EntityJson[];
  mappedaxis?: EntityJson[];
  mappedrotary?: EntityJson[];
  macros?: Array<any[]>;
}

type EntityJson = {
  source: number;
  type: string;
  target?: number|string;
  axis?: number;
  value?: number;
  macro?: number;
}

type EntityProfileString = {
  bytes: number;
  count: number;
  string: string;
};

/**
 * Main application class
 */
class ProfileGenerator {
  private json : HTMLTextAreaElement;
  private jsonCounter : HTMLTextAreaElement;
  private generatejson : HTMLButtonElement;
  private generatestring : HTMLButtonElement;
  private error : HTMLParagraphElement;
  private profile : HTMLTextAreaElement;
  private lineCountCache: number;
  
  private profileEnum : Array<string> = [
    "MAPPING_NONE",
    "JOYSTICK_BUTTON",
    "JOYSTICK_AXIS",
    "MOUSE_BUTTON",
    "MOUSE_REL_X_AXIS",
    "MOUSE_REL_Y_AXIS",
    "MOUSE_REL_WHEEL",
    "NEXT_PROFILE",
    "PREV_PROFILE",
    "SWITCH_PROFILE",
    "MACRO_PRESS",
    "MACRO_RELEASE",
    "KEYBOARD_BUTTON",
  ];
  private macroEnum: Array<string> = [
    "MACRO_NULL",
    "MACRO_SPEED",
    "MACRO_DELAY",
    "MACRO_KEY_PRESS",
    "MACRO_KEY_RELEASE",
    "MACRO_JOY_PRESS",
    "MACRO_JOY_RELEASE",
    "MACRO_MOUSE_PRESS",
    "MACRO_MOUSE_RELEASE",
    "MACRO_MOUSE_WHEEL",
    "MACRO_TYPE",
    "MACRO_MOUSE_REL_X",
    "MACRO_MOUSE_REL_Y",
    "MACRO_MOUSE_REL_X_AXIS",
    "MACRO_MOUSE_REL_Y_AXIS"
  ];

  private keyLookup = {
    "KEY_RESERVED":0,
    "KEY_ERROR_ROLLOVER":1,
    "KEY_POST_FAIL":2,
    "KEY_ERROR_UNDEFINED":3,
    "KEY_A":4,
    "KEY_B":5,
    "KEY_C":6,
    "KEY_D":7,
    "KEY_E":8,
    "KEY_F":9,
    "KEY_G":10,
    "KEY_H":11,
    "KEY_I":12,
    "KEY_J":13,
    "KEY_K":14,
    "KEY_L":15,
    "KEY_M":16,
    "KEY_N":17,
    "KEY_O":18,
    "KEY_P":19,
    "KEY_Q":20,
    "KEY_R":21,
    "KEY_S":22,
    "KEY_T":23,
    "KEY_U":24,
    "KEY_V":25,
    "KEY_W":26,
    "KEY_X":27,
    "KEY_Y":28,
    "KEY_Z":29,
    "KEY_1":30,
    "KEY_2":31,
    "KEY_3":32,
    "KEY_4":33,
    "KEY_5":34,
    "KEY_6":35,
    "KEY_7":36,
    "KEY_8":37,
    "KEY_9":38,
    "KEY_0":39,
    "KEY_ENTER":40,
    "KEY_RETURN":40,
    "KEY_ESC":41,
    "KEY_BACKSPACE":42,
    "KEY_TAB":43,
    "KEY_SPACE":44,
    "KEY_MINUS":45,
    "KEY_EQUAL":46,
    "KEY_LEFT_BRACE":47,
    "KEY_RIGHT_BRACE":48,
    "KEY_BACKSLASH":49,
    "KEY_NON_US_NUM":50,
    "KEY_SEMICOLON":51,
    "KEY_QUOTE":52,
    "KEY_TILDE":53,
    "KEY_COMMA":54,
    "KEY_PERIOD":55,
    "KEY_SLASH":56,
    "KEY_CAPS_LOCK":0x39,
    "KEY_F1":0x3A,
    "KEY_F2":0x3B,
    "KEY_F3":0x3C,
    "KEY_F4":0x3D,
    "KEY_F5":0x3E,
    "KEY_F6":0x3F,
    "KEY_F7":0x40,
    "KEY_F8":0x41,
    "KEY_F9":0x42,
    "KEY_F10":0x43,
    "KEY_F11":0x44,
    "KEY_F12":0x45,
    "KEY_PRINT":0x46,
    "KEY_PRINTSCREEN":0x46,
    "KEY_SCROLL_LOCK":0x47,
    "KEY_PAUSE":0x48,
    "KEY_INSERT":0x49,
    "KEY_HOME":0x4A,
    "KEY_PAGE_UP":0x4B,
    "KEY_DELETE":0x4C,
    "KEY_END":0x4D,
    "KEY_PAGE_DOWN":0x4E,
    "KEY_RIGHT_ARROW":0x4F,
    "KEY_LEFT_ARROW":0x50,
    "KEY_DOWN_ARROW":0x51,
    "KEY_UP_ARROW":0x52,
    "KEY_RIGHT":0x4F,
    "KEY_LEFT":0x50,
    "KEY_DOWN":0x51,
    "KEY_UP":0x52,
    "KEY_NUM_LOCK":0x53,
    "KEYPAD_DIVIDE":0x54,
    "KEYPAD_MULTIPLY":0x55,
    "KEYPAD_SUBTRACT":0x56,
    "KEYPAD_ADD":0x57,
    "KEYPAD_ENTER":0x58,
    "KEYPAD_1":0x59,
    "KEYPAD_2":0x5A,
    "KEYPAD_3":0x5B,
    "KEYPAD_4":0x5C,
    "KEYPAD_5":0x5D,
    "KEYPAD_6":0x5E,
    "KEYPAD_7":0x5F,
    "KEYPAD_8":0x60,
    "KEYPAD_9":0x61,
    "KEYPAD_0":0x62,
    "KEYPAD_DOT":0x63,
    "KEY_NON_US":0x64,
    "KEY_APPLICATION":0x65,
    "KEY_MENU":0x65,
    "KEY_POWER":0x66,
    "KEY_PAD_EQUALS":0x67,
    "KEY_F13":0x68,
    "KEY_F14":0x69,
    "KEY_F15":0x6A,
    "KEY_F16":0x6B,
    "KEY_F17":0x6C,
    "KEY_F18":0x6D,
    "KEY_F19":0x6E,
    "KEY_F20":0x6F,
    "KEY_F21":0x70,
    "KEY_F22":0x71,
    "KEY_F23":0x72,
    "KEY_F24":0x73,
    "KEY_EXECUTE":0x74,
    "KEY_HELP":0x75,
    "KEY_MENU2":0x76,
    "KEY_SELECT":0x77,
    "KEY_STOP":0x78,
    "KEY_AGAIN":0x79,
    "KEY_UNDO":0x7A,
    "KEY_CUT":0x7B,
    "KEY_COPY":0x7C,
    "KEY_PASTE":0x7D,
    "KEY_FIND":0x7E,
    "KEY_MUTE":0x7F,
    "KEY_VOLUME_MUTE":0x7F,
    "KEY_VOLUME_UP":0x80,
    "KEY_VOLUME_DOWN":0x81,
    "KEY_LOCKING_CAPS_LOCK":0x82,
    "KEY_LOCKING_NUM_LOCK":0x83,
    "KEY_LOCKING_SCROLL_LOCK":0x84,
    "KEYPAD_COMMA":0x85,
    "KEYPAD_EQUAL_SIGN":0x86,
    "KEY_INTERNATIONAL1":0x87,
    "KEY_INTERNATIONAL2":0x88,
    "KEY_INTERNATIONAL3":0x89,
    "KEY_INTERNATIONAL4":0x8A,
    "KEY_INTERNATIONAL5":0x8B,
    "KEY_INTERNATIONAL6":0x8C,
    "KEY_INTERNATIONAL7":0x8D,
    "KEY_INTERNATIONAL8":0x8E,
    "KEY_INTERNATIONAL9":0x8F,
    "KEY_LANG1":0x90,
    "KEY_LANG2":0x91,
    "KEY_LANG3":0x92,
    "KEY_LANG4":0x93,
    "KEY_LANG5":0x94,
    "KEY_LANG6":0x95,
    "KEY_LANG7":0x96,
    "KEY_LANG8":0x97,
    "KEY_LANG9":0x98,
    "KEY_ALTERNATE_ERASE":0x99,
    "KEY_SYSREQ_ATTENTION":0x9A,
    "KEY_CANCEL":0x9B,
    "KEY_CLEAR":0x9C,
    "KEY_PRIOR":0x9D,
    "KEY_RETURN2":0x9E,
    "KEY_SEPARATOR":0x9F,
    "KEY_OUT":0xA0,
    "KEY_OPER":0xA1,
    "KEY_CLEAR_AGAIN":0xA2,
    "KEY_CRSEL_PROPS":0xA3,
    "KEY_EXSEL":0xA4,
    "KEY_PAD_00":0xB0,
    "KEY_PAD_000":0xB1,
    "KEY_THOUSANDS_SEPARATOR":0xB2,
    "KEY_DECIMAL_SEPARATOR":0xB3,
    "KEY_CURRENCY_UNIT":0xB4,
    "KEY_CURRENCY_SUB_UNIT":0xB5,
    "KEYPAD_LEFT_BRACE":0xB6,
    "KEYPAD_RIGHT_BRACE":0xB7,
    "KEYPAD_LEFT_CURLY_BRACE":0xB8,
    "KEYPAD_RIGHT_CURLY_BRACE":0xB9,
    "KEYPAD_TAB":0xBA,
    "KEYPAD_BACKSPACE":0xBB,
    "KEYPAD_A":0xBC,
    "KEYPAD_B":0xBD,
    "KEYPAD_C":0xBE,
    "KEYPAD_D":0xBF,
    "KEYPAD_E":0xC0,
    "KEYPAD_F":0xC1,
    "KEYPAD_XOR":0xC2,
    "KEYPAD_CARET":0xC3,
    "KEYPAD_PERCENT":0xC4,
    "KEYPAD_LESS_THAN":0xC5,
    "KEYPAD_GREATER_THAN":0xC6,
    "KEYPAD_AMPERSAND":0xC7,
    "KEYPAD_DOUBLEAMPERSAND":0xC8,
    "KEYPAD_PIPE":0xC9,
    "KEYPAD_DOUBLEPIPE":0xCA,
    "KEYPAD_COLON":0xCB,
    "KEYPAD_POUND_SIGN":0xCC,
    "KEYPAD_SPACE":0xCD,
    "KEYPAD_AT_SIGN":0xCE,
    "KEYPAD_EXCLAMATION_POINT":0xCF,
    "KEYPAD_MEMORY_STORE":0xD0,
    "KEYPAD_MEMORY_RECALL":0xD1,
    "KEYPAD_MEMORY_CLEAR":0xD2,
    "KEYPAD_MEMORY_ADD":0xD3,
    "KEYPAD_MEMORY_SUBTRACT":0xD4,
    "KEYPAD_MEMORY_MULTIPLY":0xD5,
    "KEYPAD_MEMORY_DIVIDE":0xD6,
    "KEYPAD_PLUS_MINUS":0xD7,
    "KEYPAD_CLEAR":0xD8,
    "KEYPAD_CLEAR_ENTRY":0xD9,
    "KEYPAD_BINARY":0xDA,
    "KEYPAD_OCTAL":0xDB,
    "KEYPAD_DECIMAL":0xDC,
    "KEYPAD_HEXADECIMAL":0xDD,
    "KEY_LEFT_CTRL":0xE0,
    "KEY_LEFT_SHIFT":0xE1,
    "KEY_LEFT_ALT":0xE2,
    "KEY_LEFT_GUI":0xE3,
    "KEY_LEFT_WINDOWS":0xE3,
    "KEY_RIGHT_CTRL":0xE4,
    "KEY_RIGHT_SHIFT":0xE5,
    "KEY_RIGHT_ALT":0xE6,
    "KEY_RIGHT_GUI":0xE7,
    "KEY_RIGHT_WINDOWS":0xE7,
  };
  constructor() {
    this.json = <HTMLTextAreaElement>document.getElementById("json");
    this.jsonCounter = <HTMLTextAreaElement>document.getElementById("jsoncounter");
    this.profile = <HTMLTextAreaElement>document.getElementById("string");
    this.generatejson = <HTMLButtonElement>document.getElementById("generatejson");
    this.generatestring = <HTMLButtonElement>document.getElementById("generatestring");
    this.error = <HTMLParagraphElement>document.getElementById("error");
    this.generatestring.addEventListener("click", this.createProfile.bind(this));
    this.generatejson.addEventListener("click", this.createJson.bind(this));
    this.lineCountCache = 0;
    this.json.addEventListener('scroll', () => {
      this.jsonCounter.scrollTop = this.json.scrollTop;
      this.jsonCounter.scrollLeft = this.json.scrollLeft;
    });
    this.json.addEventListener('input', () => {
      this.line_counter();
    });

    this.line_counter();
  }
  createProfile(event: Event) {
    event.preventDefault();
    var input = this.json.value;
    var json = {};
    try {
      json = JSON.parse(input);
    } catch (e) {
        this.error.innerHTML = e.message;
        return false;
    }
    var profiles = [];
    var lengths = [];
    var profileCount = 0;
    for (var pNum in json) {
      var p = this.parseProfile(json[pNum])
      profiles.push(p[1]);
      lengths.push(p[0]);
      ++profileCount;
    }
    profiles.unshift(this.hexString(profileCount));
    var length = 3;
    for (var i = 0; i < lengths.length; ++i) {
      length += lengths[i];
    }
    profiles.unshift(this.hexString(length >> 8)  + this.hexString(length & 255));
    this.profile.value = "cimdit:F" + profiles.join("");
    return false;
  }

  hexString(input: number) : string {
    if (input < 0) {
      input += 256;
    }
    var hex = input.toString(16);
    if (hex.length < 2) {
      hex = "0" + hex;
    }
    return hex;
  }

  parseProfile(json: Object) {
    var out = "";
    var length = 2;
    // get name
    var tmp = json["name"];
    for (var pos in tmp) {
      out += this.hexString(tmp.charCodeAt(pos));
    }
    out += "00";
    length += out.length/2;
    // custom image
    if (json["customimage"]) {
      out += "01";
      out += json["customimage"];
      length += 513;
    } else {
      out += "00";
      ++length;
    }
    // add buttons
    if (json["mappedbuttons"] instanceof Array) {
      var buttons = this.map(json["mappedbuttons"]);
      out += this.hexString(buttons.count);
      out += buttons.string;
      length += buttons.bytes + 1;
    } else {
      out += "00";
      ++length;
    }
    // add axis
    if (json["mappedaxis"] instanceof Array) {
      var axis = this.map(json["mappedaxis"]);
      out += this.hexString(axis.count);
      out += axis.string;
      length += axis.bytes + 1;
    } else {
      out += "00";
      ++length;
    }
    // add rotary
    if (json["mappedrotary"] instanceof Array) {
      var rotary = this.map(json["mappedrotary"]);
      out += this.hexString(rotary.count);
      out += rotary.string;
      length += rotary.bytes + 1;
    } else {
      out += "00";
      ++length;
    }
    // add macro sequences
    if (json["macros"] instanceof Array) {
      var macro = this.createMacroString(json["macros"]);
      out += this.hexString(macro.count);
      out += macro.string;
      length += macro.bytes + 1;
    } else {
      out += "00";
      ++length;
    }
    out = this.hexString(length >> 8) + this.hexString(length & 255) + out;
    return [length, out];
  }

  map(entities: Array<EntityJson>) : EntityProfileString {
    var out ="";
    var bytes = 0
    var count = 0;
    for (var i = 0; i < entities.length; ++i) {
      var entity = entities[i];
      try {
        var tmp = this.hexString(entity.source);
        tmp += this.hexString(this.profileEnum.indexOf(entity.type));
        if (entity.target !== undefined) {
          if (typeof(entity.target) === "number") {
            tmp += this.hexString(<number>entity.target);
          } else {
            tmp += this.hexString(this.keyLookup[<string>entity.target]);
          }
        } else if (entity.axis !== undefined) {
          tmp += this.hexString(entity.axis);
        } else if (entity.value !== undefined) {
          tmp += this.hexString(entity.value);
        } else if (entity.macro !== undefined) {
          tmp += this.hexString(entity.macro);
        } else {
          tmp += "00";
        }
        out += tmp;
        bytes += 3;
        ++count;
      } catch(e) {
        // print error
        console.log(e);
      }
    }
    return {bytes: bytes, count: count, string: out};
  }

  createMacroString(macros: any[][]) : EntityProfileString {
    var bytes = 0;
    var out = "";
    for (var i = 0; i < macros.length; ++i) {
      var macro = macros[i];
      var append = false;
      for (var j = 0; j < macro.length; ++j) {
        var token = macro[j];
        if (token === "MACRO_SPEED" || token === "MACRO_MOUSE_WHEEL" || token.toString().match(/^MACRO_(JOY|MOUSE)_(PRESS|RELEASE)$/)) {  // expect an unsigned integer variable
          if (append) {
            append = false;
            out += "00";
            ++bytes;
          }
          out += this.hexString(this.macroEnum.indexOf(token)) + this.hexString(macro[++j]);
          bytes += 2;
        } else if (token === "MACRO_DELAY") { // expect an 16 bit integer variable
          if (append) {
            append = false;
            out += "00";
            ++bytes;
          }
          var value = macro[++j];
          out += this.hexString(this.macroEnum.indexOf(token)) + this.hexString((value >> 8)) + this.hexString(value & 255);
          bytes += 3;
        } else if (token.toString().match(/^MACRO_KEY_(PRESS|RELEASE)$/) || token === "MACRO_TYPE") {  // followed by a list of keys/buttons
          if (append) {
            append = false;
            out += "00";
            ++bytes;
          }
          out += this.hexString(this.macroEnum.indexOf(token));
          append = true;
          ++bytes;
        } else if (token === "MACRO_MOUSE_REL_X" || token === "MACRO_MOUSE_REL_Y") { // expect one integer values
          if (append) {
            append = false;
            out += "00";
            ++bytes;
          }
          out += this.hexString(this.macroEnum.indexOf(token)) + this.hexString(macro[++j]);
          bytes +=2;
        } else if (token === "MACRO_MOUSE_REL_X_AXIS" || token === "MACRO_MOUSE_REL_Y_AXIS") { // expect one two integer values, joystick axis and speed if axis != -1
          if (append) {
            append = false;
            out += "00";
            ++bytes;
          }
          if (macro[j + 1] == -1) {
            out += this.hexString(this.macroEnum.indexOf(token)) + this.hexString(macro[++j]);
            bytes +=2;
          } else {
            out += this.hexString(this.macroEnum.indexOf(token)) + this.hexString(macro[++j]) + this.hexString(macro[++j]);
            bytes +=3;
          }
        } else {
          if (typeof(token) === "number") {
            out += this.hexString(token);
            ++bytes;
          } else {
            if (token.startsWith("KEY_")) {
              out += this.hexString(this.keyLookup[token]);
              ++bytes;
            } else {
              for (var k = 0; k < token.length; ++k) {
                out += this.hexString(this.keyLookup["KEY_" + token[k].toUpperCase()]);
                ++bytes;
              }
            }
          }
        }
      }
      if (append) {
        append = false;
        out += "00";
        ++bytes;
      }
      out += "00";
      ++bytes;
    }
    return {bytes: bytes, count: macros.length, string: out};
  }

  fromHexS(input: string) : number {
    var ret = parseInt(input, 16);
    if (ret>127) ret -= 256;
    return ret;
  }
  fromHexU(input: string) : number {
    return parseInt(input, 16);
  }
  createJson(event: Event) {
    event.preventDefault();
    var string = this.profile.value;
    // strip cimdit command
    string = string.replace("cimdit:F","");
    var tokens = string.match(/.{1,2}/g);
    // parse token list
    // ignore first 2 entries (length)
    tokens.shift();
    tokens.shift();
    var json : ProfileJson[];
    json =  [];
    var numProfile = this.fromHexU(tokens.shift());
    for (var i = 0; i < numProfile; ++i) {
      json.push(this.createProfileFromTokens(tokens));
    }
    this.json.value = JSON.stringify(json,null,"  ");
    return false;
  }
  createProfileFromTokens(tokens) : ProfileJson {
    // var bytes = tokens.shift() * 255 + tokens.shift() - 2;
    tokens.shift(); tokens.shift();
    var name = "";
    var char: number;
    do {
      char = this.fromHexU(tokens.shift());
      if (char)
        name += String.fromCharCode(char);
    } while (char !== 0 && tokens.length > 0);
    var ret:ProfileJson = {name: name};
    var customImage = tokens.shift();
    if (customImage != "00") {
      var cu = "";
      for (var i = 0; i < 512; ++i) {
        cu += tokens.shift();
      }
      ret.customimage = cu;
    }
    var mappedButtons:EntityJson[] = [];
    var numButtons = this.fromHexU(tokens.shift());
    for (var i = 0; i < numButtons; ++i) {
      mappedButtons.push(this.createEntityFromTokens(tokens));
    }
    ret.mappedbuttons = mappedButtons;
    var mappedAxis:EntityJson[] = [];
    var numAxis = this.fromHexU(tokens.shift());
    for (var i = 0; i < numAxis; ++i) {
      mappedAxis.push(this.createEntityFromTokens(tokens));
    }
    ret.mappedaxis = mappedAxis;
    var mappedRotary:EntityJson[] = [];
    var numRotary = this.fromHexU(tokens.shift());
    for (var i = 0; i < numRotary; ++i) {
      mappedRotary.push(this.createEntityFromTokens(tokens));
    }
    ret.mappedrotary = mappedRotary;
    var macros : Array<any[]> = [];
    var numMacro = this.fromHexU(tokens.shift());
    for (var i = 0; i < numMacro; ++i) {
      macros.push(this.createMacrosFromTokens(tokens));
    }
    ret.macros = macros;
    return ret;
  }
  createEntityFromTokens(tokens) :EntityJson{
    var entity: EntityJson = {source: this.fromHexU(tokens.shift()), type: this.profileEnum[this.fromHexU(tokens.shift())]};
    if (entity.type === 'JOYSTICK_BUTTON' || entity.type === 'MOUSE_BUTTON') {
      entity.target = this.fromHexU(tokens.shift());
    } else if (entity.type === 'KEYBOARD_BUTTON') {
      entity.target = this.getKey(this.fromHexU(tokens.shift()));
    } else if (entity.type === 'JOYSTICK_AXIS') {
      entity.axis = this.fromHexU(tokens.shift());
      } else if (entity.type === 'MOUSE_REL_X_AXIS' || entity.type === 'MOUSE_REL_Y_AXIS' || entity.type === 'MOUSE_REL_WHEEL') {
      entity.value = this.fromHexS(tokens.shift());
    } else if (entity.type === 'MACRO_PRESS' || entity.type === 'MACRO_RELEASE') {
      entity.macro = this.fromHexU(tokens.shift());
    } else {
      tokens.shift();
    }
    return entity;
  }

  getKey(input: number) : string {
    return Object.keys(this.keyLookup).find(k=>this.keyLookup[k]===input);
  }

  createMacrosFromTokens(tokens: Array<string>) : Array<string | number> {
    var macro : Array<string | number> = [];
    do {
      var token = this.macroEnum[this.fromHexU(tokens.shift())];
      if (token !== "MACRO_NULL") {
        macro.push(token);
      }
      switch(token) {
        case "MACRO_NULL":
          break;
        case "MACRO_SPEED": // read in 1 extra numberic value 8 bit
        case "MACRO_JOY_PRESS":
        case "MACRO_JOY_RELEASE":
        case "MACRO_MOUSE_PRESS":
        case "MACRO_MOUSE_RELEASE":
        case "MACRO_MOUSE_WHEEL":
        macro.push(this.fromHexU(tokens.shift()));
        break;
        case "MACRO_DELAY": // read in 1 extra numberic value 16 bit
          macro.push((this.fromHexU(tokens.shift())<<8) + this.fromHexU(tokens.shift()));
        break;
        case "MACRO_KEY_PRESS": // one or more keys
        case "MACRO_KEY_RELEASE":
        case "MACRO_TYPE":
          var next: number;
          var tmp = "";
          do {
            next = this.fromHexU(tokens.shift());
            if (next != 0) {
              var k = this.getKey(next);
              if (k.match(/^KEY_\S$/)) {
                tmp += k[4].toLowerCase();
              } else {
                if (tmp !== "")
                macro.push(tmp);
                tmp = "";
                macro.push(k);
              }
            }
          } while (next != 0);
          if (tmp !== "") {
            macro.push(tmp);
          }
        break;
        case "MACRO_MOUSE_REL_X":
        case "MACRO_MOUSE_REL_Y": // one bytes
          var next :number = this.fromHexS(tokens.shift());
          macro.push(next);
          break;
        case "MACRO_MOUSE_REL_X_AXIS":
        case "MACRO_MOUSE_REL_Y_AXIS": // one or 2 bytes
          var next :number = this.fromHexS(tokens.shift());
          macro.push(next);
          if (next != -1) {
            macro.push(this.fromHexS(tokens.shift()));
          }
          break;
      }
    } while (token !== "MACRO_NULL" && tokens.length > 0);
    return macro;
  }

  line_counter() {
    var lineCount = this.json.value.split('\n').length;
    var outarr = new Array();
    if (this.lineCountCache != lineCount) {
      for (var x = 0; x < lineCount; x++) {
        outarr[x] = (x + 1);
      }
      this.jsonCounter.value = outarr.join('\n');
    }
    this.lineCountCache = lineCount;
  }
}