import { assert } from '@awayjs/graphics';

import { release } from '@awayfl/swf-loader';

export const enum Bytecode {
	BKPT               = 0x01,
	NOP                = 0x02,
	THROW              = 0x03,
	GETSUPER           = 0x04,
	SETSUPER           = 0x05,
	DXNS               = 0x06,
	DXNSLATE           = 0x07,
	KILL               = 0x08,
	LABEL              = 0x09,
	LF32X4             = 0x0A,
	SF32X4             = 0x0B,
	IFNLT              = 0x0C,
	IFNLE              = 0x0D,
	IFNGT              = 0x0E,
	IFNGE              = 0x0F,
	JUMP               = 0x10,
	IFTRUE             = 0x11,
	IFFALSE            = 0x12,
	IFEQ               = 0x13,
	IFNE               = 0x14,
	IFLT               = 0x15,
	IFLE               = 0x16,
	IFGT               = 0x17,
	IFGE               = 0x18,
	IFSTRICTEQ         = 0x19,
	IFSTRICTNE         = 0x1A,
	LOOKUPSWITCH       = 0x1B,
	PUSHWITH           = 0x1C,
	POPSCOPE           = 0x1D,
	NEXTNAME           = 0x1E,
	HASNEXT            = 0x1F,
	PUSHNULL           = 0x20,
	PUSHUNDEFINED      = 0x21,
	PUSHFLOAT          = 0x22,
	NEXTVALUE          = 0x23,
	PUSHBYTE           = 0x24,
	PUSHSHORT          = 0x25,
	PUSHTRUE           = 0x26,
	PUSHFALSE          = 0x27,
	PUSHNAN            = 0x28,
	POP                = 0x29,
	DUP                = 0x2A,
	SWAP               = 0x2B,
	PUSHSTRING         = 0x2C,
	PUSHINT            = 0x2D,
	PUSHUINT           = 0x2E,
	PUSHDOUBLE         = 0x2F,
	PUSHSCOPE          = 0x30,
	PUSHNAMESPACE      = 0x31,
	HASNEXT2           = 0x32,
	UNUSED_33          = 0x33,
	UNUSED_34          = 0x34,
	LI8                = 0x35,
	LI16               = 0x36,
	LI32               = 0x37,
	LF32               = 0x38,
	LF64               = 0x39,
	SI8                = 0x3A,
	SI16               = 0x3B,
	SI32               = 0x3C,
	SF32               = 0x3D,
	SF64               = 0x3E,
	UNUSED_3F          = 0x3F,
	NEWFUNCTION        = 0x40,
	CALL               = 0x41,
	CONSTRUCT          = 0x42,
	CALLMETHOD         = 0x43,
	CALLSTATIC         = 0x44,
	CALLSUPER          = 0x45,
	CALLPROPERTY       = 0x46,
	RETURNVOID         = 0x47,
	RETURNVALUE        = 0x48,
	CONSTRUCTSUPER     = 0x49,
	CONSTRUCTPROP      = 0x4A,
	CALLSUPERID        = 0x4B,
	CALLPROPLEX        = 0x4C,
	CALLINTERFACE      = 0x4D,
	CALLSUPERVOID      = 0x4E,
	CALLPROPVOID       = 0x4F,
	SXI1               = 0x50,
	SXI8               = 0x51,
	SXI16              = 0x52,
	APPLYTYPE          = 0x53,
	PUSHFLOAT4         = 0x54,
	NEWOBJECT          = 0x55,
	NEWARRAY           = 0x56,
	NEWACTIVATION      = 0x57,
	NEWCLASS           = 0x58,
	GETDESCENDANTS     = 0x59,
	NEWCATCH           = 0x5A,
	UNUSED_5B          = 0x5B,
	UNUSED_5C          = 0x5C,
	FINDPROPSTRICT     = 0x5D,
	FINDPROPERTY       = 0x5E,
	FINDDEF            = 0x5F,
	GETLEX             = 0x60,
	SETPROPERTY        = 0x61,
	GETLOCAL           = 0x62,
	SETLOCAL           = 0x63,
	GETGLOBALSCOPE     = 0x64,
	GETSCOPEOBJECT     = 0x65,
	GETPROPERTY        = 0x66,
	GETOUTERSCOPE      = 0x67,
	INITPROPERTY       = 0x68,
	UNUSED_69          = 0x69,
	DELETEPROPERTY     = 0x6A,
	UNUSED_6B          = 0x6B,
	GETSLOT            = 0x6C,
	SETSLOT            = 0x6D,
	GETGLOBALSLOT      = 0x6E,
	SETGLOBALSLOT      = 0x6F,
	CONVERT_S          = 0x70,
	ESC_XELEM          = 0x71,
	ESC_XATTR          = 0x72,
	CONVERT_I          = 0x73,
	CONVERT_U          = 0x74,
	CONVERT_D          = 0x75,
	CONVERT_B          = 0x76,
	CONVERT_O          = 0x77,
	CHECKFILTER        = 0x78,
	CONVERT_F          = 0x79,
	UNPLUS             = 0x7a,
	CONVERT_F4         = 0x7b,
	BC_7C              = 0x7c,
	BC_7D              = 0x7d,
	BC_7E              = 0x7e,
	BC_7F              = 0x7f,
	COERCE             = 0x80,
	COERCE_B           = 0x81,
	COERCE_A           = 0x82,
	COERCE_I           = 0x83,
	COERCE_D           = 0x84,
	COERCE_S           = 0x85,
	ASTYPE             = 0x86,
	ASTYPELATE         = 0x87,
	COERCE_U           = 0x88,
	COERCE_O           = 0x89,
	UNUSED_8A          = 0x8A,
	UNUSED_8B          = 0x8B,
	UNUSED_8C          = 0x8C,
	UNUSED_8D          = 0x8D,
	UNUSED_8E          = 0x8E,
	UNUSED_8F          = 0x8F,
	NEGATE             = 0x90,
	INCREMENT          = 0x91,
	INCLOCAL           = 0x92,
	DECREMENT          = 0x93,
	DECLOCAL           = 0x94,
	TYPEOF             = 0x95,
	NOT                = 0x96,
	BITNOT             = 0x97,
	UNUSED_98          = 0x98,
	UNUSED_99          = 0x99,
	UNUSED_9A          = 0x9A,
	UNUSED_9B          = 0x9B,
	UNUSED_9C          = 0x9C,
	UNUSED_9D          = 0x9D,
	UNUSED_9E          = 0x9E,
	UNUSED_9F          = 0x9F,
	ADD                = 0xA0,
	SUBTRACT           = 0xA1,
	MULTIPLY           = 0xA2,
	DIVIDE             = 0xA3,
	MODULO             = 0xA4,
	LSHIFT             = 0xA5,
	RSHIFT             = 0xA6,
	URSHIFT            = 0xA7,
	BITAND             = 0xA8,
	BITOR              = 0xA9,
	BITXOR             = 0xAA,
	EQUALS             = 0xAB,
	STRICTEQUALS       = 0xAC,
	LESSTHAN           = 0xAD,
	LESSEQUALS         = 0xAE,
	GREATERTHAN        = 0xAF,
	GREATEREQUALS      = 0xB0,
	INSTANCEOF         = 0xB1,
	ISTYPE             = 0xB2,
	ISTYPELATE         = 0xB3,
	IN                 = 0xB4,
	UNUSED_B5          = 0xB5,
	UNUSED_B6          = 0xB6,
	UNUSED_B7          = 0xB7,
	UNUSED_B8          = 0xB8,
	UNUSED_B9          = 0xB9,
	UNUSED_BA          = 0xBA,
	UNUSED_BB          = 0xBB,
	UNUSED_BC          = 0xBC,
	UNUSED_BD          = 0xBD,
	UNUSED_BE          = 0xBE,
	UNUSED_BF          = 0xBF,
	INCREMENT_I        = 0xC0,
	DECREMENT_I        = 0xC1,
	INCLOCAL_I         = 0xC2,
	DECLOCAL_I         = 0xC3,
	NEGATE_I           = 0xC4,
	ADD_I              = 0xC5,
	SUBTRACT_I         = 0xC6,
	MULTIPLY_I         = 0xC7,
	UNUSED_C8          = 0xC8,
	UNUSED_C9          = 0xC9,
	UNUSED_CA          = 0xCA,
	UNUSED_CB          = 0xCB,
	UNUSED_CC          = 0xCC,
	UNUSED_CD          = 0xCD,
	UNUSED_CE          = 0xCE,
	UNUSED_CF          = 0xCF,
	GETLOCAL0          = 0xD0,
	GETLOCAL1          = 0xD1,
	GETLOCAL2          = 0xD2,
	GETLOCAL3          = 0xD3,
	SETLOCAL0          = 0xD4,
	SETLOCAL1          = 0xD5,
	SETLOCAL2          = 0xD6,
	SETLOCAL3          = 0xD7,
	UNUSED_D8          = 0xD8,
	UNUSED_D9          = 0xD9,
	UNUSED_DA          = 0xDA,
	UNUSED_DB          = 0xDB,
	UNUSED_DC          = 0xDC,
	UNUSED_DD          = 0xDD,
	UNUSED_DE          = 0xDE,
	UNUSED_DF          = 0xDF,
	UNUSED_E0          = 0xE0,
	UNUSED_E1          = 0xE1,
	UNUSED_E2          = 0xE2,
	UNUSED_E3          = 0xE3,
	UNUSED_E4          = 0xE4,
	UNUSED_E5          = 0xE5,
	UNUSED_E6          = 0xE6,
	UNUSED_E7          = 0xE7,
	UNUSED_E8          = 0xE8,
	UNUSED_E9          = 0xE9,
	UNUSED_EA          = 0xEA,
	UNUSED_EB          = 0xEB,
	UNUSED_EC          = 0xEC,
	UNUSED_ED          = 0xED,
	UNUSED_EE          = 0xEE,
	INVALID            = 0xED,
	DEBUG              = 0xEF,

	DEBUGLINE          = 0xF0,
	DEBUGFILE          = 0xF1,
	BKPTLINE           = 0xF2,
	TIMESTAMP          = 0xF3,

	RESTARGC           = 0xF4,
	RESTARG            = 0xF5,

	UNUSED_F6          = 0xF6,
	UNUSED_F7          = 0xF7,
	UNUSED_F8          = 0xF8,
	UNUSED_F9          = 0xF9,
	UNUSED_FA          = 0xFA,
	UNUSED_FB          = 0xFB,
	UNUSED_FC          = 0xFC,
	UNUSED_FD          = 0xFD,
	UNUSED_FE          = 0xFE,

	END                = 0xFF
}

const bytecodeNames = [
	'',
	'BKPT',
	'NOP',
	'THROW',
	'GETSUPER',
	'SETSUPER',
	'DXNS',
	'DXNSLATE',
	'KILL',
	'LABEL',
	'LF32X4',
	'SF32X4',
	'IFNLT',
	'IFNLE',
	'IFNGT',
	'IFNGE',
	'JUMP',
	'IFTRUE',
	'IFFALSE',
	'IFEQ',
	'IFNE',
	'IFLT',
	'IFLE',
	'IFGT',
	'IFGE',
	'IFSTRICTEQ',
	'IFSTRICTNE',
	'LOOKUPSWITCH',
	'PUSHWITH',
	'POPSCOPE',
	'NEXTNAME',
	'HASNEXT',
	'PUSHNULL',
	'PUSHUNDEFINED',
	'PUSHFLOAT',
	'NEXTVALUE',
	'PUSHBYTE',
	'PUSHSHORT',
	'PUSHTRUE',
	'PUSHFALSE',
	'PUSHNAN',
	'POP',
	'DUP',
	'SWAP',
	'PUSHSTRING',
	'PUSHINT',
	'PUSHUINT',
	'PUSHDOUBLE',
	'PUSHSCOPE',
	'PUSHNAMESPACE',
	'HASNEXT2',
	'UNUSED_33',
	'UNUSED_34',
	'LI8',
	'LI16',
	'LI32',
	'LF32',
	'LF64',
	'SI8',
	'SI16',
	'SI32',
	'SF32',
	'SF64',
	'UNUSED_3F',
	'NEWFUNCTION',
	'CALL',
	'CONSTRUCT',
	'CALLMETHOD',
	'CALLSTATIC',
	'CALLSUPER',
	'CALLPROPERTY',
	'RETURNVOID',
	'RETURNVALUE',
	'CONSTRUCTSUPER',
	'CONSTRUCTPROP',
	'CALLSUPERID',
	'CALLPROPLEX',
	'CALLINTERFACE',
	'CALLSUPERVOID',
	'CALLPROPVOID',
	'SXI1',
	'SXI8',
	'SXI16',
	'APPLYTYPE',
	'PUSHFLOAT4',
	'NEWOBJECT',
	'NEWARRAY',
	'NEWACTIVATION',
	'NEWCLASS',
	'GETDESCENDANTS',
	'NEWCATCH',
	'UNUSED_5B',
	'UNUSED_5C',
	'FINDPROPSTRICT',
	'FINDPROPERTY',
	'FINDDEF',
	'GETLEX',
	'SETPROPERTY',
	'GETLOCAL',
	'SETLOCAL',
	'GETGLOBALSCOPE',
	'GETSCOPEOBJECT',
	'GETPROPERTY',
	'GETOUTERSCOPE',
	'INITPROPERTY',
	'UNUSED_69',
	'DELETEPROPERTY',
	'UNUSED_6B',
	'GETSLOT',
	'SETSLOT',
	'GETGLOBALSLOT',
	'SETGLOBALSLOT',
	'CONVERT_S',
	'ESC_XELEM',
	'ESC_XATTR',
	'CONVERT_I',
	'CONVERT_U',
	'CONVERT_D',
	'CONVERT_B',
	'CONVERT_O',
	'CHECKFILTER',
	'CONVERT_F',
	'UNPLUS',
	'CONVERT_F4',
	'BC_7C',
	'BC_7D',
	'BC_7E',
	'BC_7F',
	'COERCE',
	'COERCE_B',
	'COERCE_A',
	'COERCE_I',
	'COERCE_D',
	'COERCE_S',
	'ASTYPE',
	'ASTYPELATE',
	'COERCE_U',
	'COERCE_O',
	'UNUSED_8A',
	'UNUSED_8B',
	'UNUSED_8C',
	'UNUSED_8D',
	'UNUSED_8E',
	'UNUSED_8F',
	'NEGATE',
	'INCREMENT',
	'INCLOCAL',
	'DECREMENT',
	'DECLOCAL',
	'TYPEOF',
	'NOT',
	'BITNOT',
	'UNUSED_98',
	'UNUSED_99',
	'UNUSED_9A',
	'UNUSED_9B',
	'UNUSED_9C',
	'UNUSED_9D',
	'UNUSED_9E',
	'UNUSED_9F',
	'ADD',
	'SUBTRACT',
	'MULTIPLY',
	'DIVIDE',
	'MODULO',
	'LSHIFT',
	'RSHIFT',
	'URSHIFT',
	'BITAND',
	'BITOR',
	'BITXOR',
	'EQUALS',
	'STRICTEQUALS',
	'LESSTHAN',
	'LESSEQUALS',
	'GREATERTHAN',
	'GREATEREQUALS',
	'INSTANCEOF',
	'ISTYPE',
	'ISTYPELATE',
	'IN',
	'UNUSED_B5',
	'UNUSED_B6',
	'UNUSED_B7',
	'UNUSED_B8',
	'UNUSED_B9',
	'UNUSED_BA',
	'UNUSED_BB',
	'UNUSED_BC',
	'UNUSED_BD',
	'UNUSED_BE',
	'UNUSED_BF',
	'INCREMENT_I',
	'DECREMENT_I',
	'INCLOCAL_I',
	'DECLOCAL_I',
	'NEGATE_I',
	'ADD_I',
	'SUBTRACT_I',
	'MULTIPLY_I',
	'UNUSED_C8',
	'UNUSED_C9',
	'UNUSED_CA',
	'UNUSED_CB',
	'UNUSED_CC',
	'UNUSED_CD',
	'UNUSED_CE',
	'UNUSED_CF',
	'GETLOCAL0',
	'GETLOCAL1',
	'GETLOCAL2',
	'GETLOCAL3',
	'SETLOCAL0',
	'SETLOCAL1',
	'SETLOCAL2',
	'SETLOCAL3',
	'UNUSED_D8',
	'UNUSED_D9',
	'UNUSED_DA',
	'UNUSED_DB',
	'UNUSED_DC',
	'UNUSED_DD',
	'UNUSED_DE',
	'UNUSED_DF',
	'UNUSED_E0',
	'UNUSED_E1',
	'UNUSED_E2',
	'UNUSED_E3',
	'UNUSED_E4',
	'UNUSED_E5',
	'UNUSED_E6',
	'UNUSED_E7',
	'UNUSED_E8',
	'UNUSED_E9',
	'UNUSED_EA',
	'UNUSED_EB',
	'UNUSED_EC',
	'INVALID',
	'UNUSED_EE',
	'DEBUG',
	'DEBUGLINE',
	'DEBUGFILE',
	'BKPTLINE',
	'TIMESTAMP',
	'RESTARGC',
	'RESTARG',
	'UNUSED_F6',
	'UNUSED_F7',
	'UNUSED_F8',
	'UNUSED_F9',
	'UNUSED_FA',
	'UNUSED_FB',
	'UNUSED_FC',
	'UNUSED_FD',
	'UNUSED_FE',
	'END'];

export function getBytecodeName(bytecode: Bytecode): string {
	return release ? 'Bytecode: ' + bytecode : bytecodeNames[bytecode];
}

const enum Flags {
	NONE            = 0x000,
	STOP            = 0x001,
	FALL_THROUGH    = 0x002,
	BRANCH          = 0x004,
	CAN_THROW       = 0x008,
	RETURN          = 0x010
}

/**
 * A array that maps from a bytecode value to the set of {@link OPFlags} for the corresponding instruction.
 */
export const BytecodeFlags = new Uint32Array(256);
export const BytecodeFormat = new Array(256);

function define(bytecode: Bytecode, format: string, flags: Flags = 0) {
	BytecodeFlags[bytecode] = flags;
	BytecodeFormat[bytecode] = format;
}

/**
 * Only call this before the compiler is used.
 */
export function defineBytecodes() {
	define(Bytecode.BKPT, '');
	define(Bytecode.NOP, '');
	define(Bytecode.THROW, '');
	define(Bytecode.GETSUPER, 'e');
	define(Bytecode.SETSUPER, 'e');
	define(Bytecode.DXNS, 'e');
	define(Bytecode.DXNSLATE, '');
	define(Bytecode.KILL, 'e');
	define(Bytecode.LABEL, '');
	define(Bytecode.LF32X4, '');
	define(Bytecode.SF32X4, '');
	define(Bytecode.IFNLT, 'd');
	define(Bytecode.IFNLE, 'd');
	define(Bytecode.IFNGT, 'd');
	define(Bytecode.IFNGE, 'd');
	define(Bytecode.JUMP, 'd');
	define(Bytecode.IFTRUE, 'd');
	define(Bytecode.IFFALSE, 'd');
	define(Bytecode.IFEQ, 'd');
	define(Bytecode.IFNE, 'd');
	define(Bytecode.IFLT, 'd');
	define(Bytecode.IFLE, 'd');
	define(Bytecode.IFGT, 'd');
	define(Bytecode.IFGE, 'd');
	define(Bytecode.IFSTRICTEQ, 'd');
	define(Bytecode.IFSTRICTNE, 'd');
	define(Bytecode.LOOKUPSWITCH, '');
	define(Bytecode.PUSHWITH, '');
	define(Bytecode.POPSCOPE, '');
	define(Bytecode.NEXTNAME, '');
	define(Bytecode.HASNEXT, '');
	define(Bytecode.PUSHNULL, '');
	define(Bytecode.PUSHUNDEFINED, '');
	define(Bytecode.NEXTVALUE, '');
	define(Bytecode.PUSHBYTE, 'b');
	define(Bytecode.PUSHSHORT, 'c');
	define(Bytecode.PUSHTRUE, '');
	define(Bytecode.PUSHFALSE, '');
	define(Bytecode.PUSHNAN, '');
	define(Bytecode.POP, '');
	define(Bytecode.DUP, '');
	define(Bytecode.SWAP, '');
	define(Bytecode.PUSHSTRING, 'e');
	define(Bytecode.PUSHINT, 'e');
	define(Bytecode.PUSHUINT, 'e');
	define(Bytecode.PUSHDOUBLE, 'e');
	define(Bytecode.PUSHSCOPE, '');
	define(Bytecode.PUSHNAMESPACE, 'e');
	define(Bytecode.HASNEXT2, 'ee');
	// define(Bytecode.UNDEFINED, "");
	// define(Bytecode.UNDEFINED, "");
	define(Bytecode.LI8, '');
	define(Bytecode.LI16, '');
	define(Bytecode.LI32, '');
	define(Bytecode.LF32, '');
	define(Bytecode.LF64, '');
	define(Bytecode.SI8, '');
	define(Bytecode.SI16, '');
	define(Bytecode.SI32, '');
	define(Bytecode.SF32, '');
	define(Bytecode.SF64, '');
	define(Bytecode.NEWFUNCTION, 'e');
	define(Bytecode.CALL, 'e');
	define(Bytecode.CONSTRUCT, 'e');
	define(Bytecode.CALLMETHOD, 'ee');
	define(Bytecode.CALLSTATIC, 'ee');
	define(Bytecode.CALLSUPER, 'ee');
	define(Bytecode.CALLPROPERTY, 'ee');
	define(Bytecode.RETURNVOID, '');
	define(Bytecode.RETURNVALUE, '');
	define(Bytecode.CONSTRUCTSUPER, 'e');
	define(Bytecode.CONSTRUCTPROP, 'ee');
	define(Bytecode.CALLSUPERID, '');
	define(Bytecode.CALLPROPLEX, 'ee');
	define(Bytecode.CALLINTERFACE, '');
	define(Bytecode.CALLSUPERVOID, 'ee');
	define(Bytecode.CALLPROPVOID, 'ee');
	define(Bytecode.SXI1, '');
	define(Bytecode.SXI8, '');
	define(Bytecode.SXI16, '');
	define(Bytecode.APPLYTYPE, 'e');
	define(Bytecode.PUSHFLOAT4, '');
	define(Bytecode.NEWOBJECT, 'e');
	define(Bytecode.NEWARRAY, 'e');
	define(Bytecode.NEWACTIVATION, '');
	define(Bytecode.NEWCLASS, 'e');
	define(Bytecode.GETDESCENDANTS, 'e');
	define(Bytecode.NEWCATCH, 'e');
	// define(Bytecode.UNDEFINED, "");
	// define(Bytecode.UNDEFINED, "");
	define(Bytecode.FINDPROPSTRICT, 'e');
	define(Bytecode.FINDPROPERTY, 'e');
	define(Bytecode.FINDDEF, '');
	define(Bytecode.GETLEX, 'e');
	define(Bytecode.SETPROPERTY, 'e');
	define(Bytecode.GETLOCAL, 'e');
	define(Bytecode.SETLOCAL, 'e');
	define(Bytecode.GETGLOBALSCOPE, '');
	define(Bytecode.GETSCOPEOBJECT, 'e');
	define(Bytecode.GETPROPERTY, 'e');
	define(Bytecode.GETOUTERSCOPE, '');
	define(Bytecode.INITPROPERTY, 'e');
	define(Bytecode.DELETEPROPERTY, 'e');
	define(Bytecode.GETSLOT, 'e');
	define(Bytecode.SETSLOT, 'e');
	define(Bytecode.GETGLOBALSLOT, 'e');
	define(Bytecode.SETGLOBALSLOT, 'e');
	define(Bytecode.CONVERT_S, '');
	define(Bytecode.ESC_XELEM, '');
	define(Bytecode.ESC_XATTR, '');
	define(Bytecode.CONVERT_I, '');
	define(Bytecode.CONVERT_U, '');
	define(Bytecode.CONVERT_D, '');
	define(Bytecode.CONVERT_B, '');
	define(Bytecode.CONVERT_O, '');
	define(Bytecode.CHECKFILTER, '');
	define(Bytecode.CONVERT_F, '');
	define(Bytecode.UNPLUS, '');
	define(Bytecode.CONVERT_F4, '');
	define(Bytecode.COERCE, 'e');
	define(Bytecode.COERCE_B, '');
	define(Bytecode.COERCE_A, '');
	define(Bytecode.COERCE_I, '');
	define(Bytecode.COERCE_D, '');
	define(Bytecode.COERCE_S, '');
	define(Bytecode.ASTYPE, 'e');
	define(Bytecode.ASTYPELATE, '');
	define(Bytecode.COERCE_U, '');
	define(Bytecode.COERCE_O, '');
	define(Bytecode.NEGATE, '');
	define(Bytecode.INCREMENT, '');
	define(Bytecode.INCLOCAL, 'e');
	define(Bytecode.DECREMENT, '');
	define(Bytecode.DECLOCAL, 'e');
	define(Bytecode.TYPEOF, '');
	define(Bytecode.NOT, '');
	define(Bytecode.BITNOT, '');
	define(Bytecode.ADD, '');
	define(Bytecode.SUBTRACT, '');
	define(Bytecode.MULTIPLY, '');
	define(Bytecode.DIVIDE, '');
	define(Bytecode.MODULO, '');
	define(Bytecode.LSHIFT, '');
	define(Bytecode.RSHIFT, '');
	define(Bytecode.URSHIFT, '');
	define(Bytecode.BITAND, '');
	define(Bytecode.BITOR, '');
	define(Bytecode.BITXOR, '');
	define(Bytecode.EQUALS, '');
	define(Bytecode.STRICTEQUALS, '');
	define(Bytecode.LESSTHAN, '');
	define(Bytecode.LESSEQUALS, '');
	define(Bytecode.GREATERTHAN, '');
	define(Bytecode.GREATEREQUALS, '');
	define(Bytecode.INSTANCEOF, '');
	define(Bytecode.ISTYPE, 'e');
	define(Bytecode.ISTYPELATE, '');
	define(Bytecode.IN, '');
	define(Bytecode.INCREMENT_I, '');
	define(Bytecode.DECREMENT_I, '');
	define(Bytecode.INCLOCAL_I, 'e');
	define(Bytecode.DECLOCAL_I, 'e');
	define(Bytecode.NEGATE_I, '');
	define(Bytecode.ADD_I, '');
	define(Bytecode.SUBTRACT_I, '');
	define(Bytecode.MULTIPLY_I, '');
	define(Bytecode.GETLOCAL0, '');
	define(Bytecode.GETLOCAL1, '');
	define(Bytecode.GETLOCAL2, '');
	define(Bytecode.GETLOCAL3, '');
	define(Bytecode.SETLOCAL0, '');
	define(Bytecode.SETLOCAL1, '');
	define(Bytecode.SETLOCAL2, '');
	define(Bytecode.SETLOCAL3, '');
	define(Bytecode.INVALID, '');
	define(Bytecode.DEBUG, 'aeae');
	define(Bytecode.DEBUGLINE, 'e');
	define(Bytecode.DEBUGFILE, 'e');
	define(Bytecode.BKPTLINE, 'e');
	define(Bytecode.TIMESTAMP, '');

	// define(Bytecode.UNUSED_6B, "", Flags.NONE);
	// define(Bytecode.UNUSED_DE, "", Flags.NONE);
	// define(Bytecode.UNUSED_BB, "", Flags.NONE);
}

defineBytecodes();

export class Bytes {
	static u8(code: Uint8Array, i: number): number {
		return code[i];
	}

	static s32(code: Uint8Array, i: number): number {
		let result = code[i];
		if (result & 0x80) {
			result = result & 0x7f | code[i + 1] << 7;
			if (result & 0x4000) {
				result = result & 0x3fff | code[i + 2] << 14;
				if (result & 0x200000) {
					result = result & 0x1fffff | code[i + 3] << 21;
					if (result & 0x10000000) {
						result = result & 0x0fffffff | code[i + 4] << 28;
						result = result & 0xffffffff;
					}
				}
			}
		}
		return result;
	}

	static u32(code: Uint8Array, i: number): number {
		return Bytes.s32(code, i) >>> 0;
	}

	static u30(code: Uint8Array, i: number): number {
		return Bytes.u32(code, i);
	}

	static s32Length(code: Uint8Array, i: number): number {
		let result = code[i];
		if (result & 0x80) {
			result = result & 0x7f | code[i + 1] << 7;
			if (result & 0x4000) {
				result = result & 0x3fff | code[i + 2] << 14;
				if (result & 0x200000) {
					result = result & 0x1fffff | code[i + 3] << 21;
					if (result & 0x10000000) {
						return 5;
					}
					return 4;
				}
				return 3;
			}
			return 2;
		}
		return 1;
	}
}

export const enum Sizes {
	u08 = 0,
	s08,
	s16,
	s24,
	u30,
	u32
}

function lengthAt(code: Uint8Array, i: number): number {
	let l = 1;
	const bytecode = code[i];
	if (bytecode === Bytecode.LOOKUPSWITCH) {
		l += 3; // Default offset.
		const n = Bytes.u30(code, i + l) + 1; // Offsets
		l += Bytes.s32Length(code, i + l);
		l += n * 3;
		return l;
	}
	const format = BytecodeFormat[bytecode];
	if (format === '') {
		return l;
	}
	assert(format, 'OP: ' + getBytecodeName(bytecode));
	for (let j = 0; j < format.length; j++) {
		const f = format[j].charCodeAt(0) - 97;
		switch (f) {
			case Sizes.u08:
			case Sizes.s08:
				l += 1;
				continue;
			case Sizes.s24:
				l += 3;
				continue;
			case Sizes.s16:
			case Sizes.u30:
			case Sizes.u32:
				l += Bytes.s32Length(code, i + l);
				continue;
		}
	}
	return l;
}

export class BytecodeStream {
	private _code: Uint8Array;
	private _bytecode: Bytecode;
	private _currentBCI: number;
	private _nextBCI: number;

	constructor(code: Uint8Array) {
		this._code = code;
		this.setBCI(0);
	}

	public next() {
		this.setBCI(this._nextBCI);
	}

	public endBCI(): number {
		return this._code.length;
	}

	public get nextBCI(): number {
		return this._nextBCI;
	}

	public get currentBCI(): number {
		return this._currentBCI;
	}

	public currentBytecode(): Bytecode {
		return this._bytecode;
	}

	public nextBC(): Bytecode {
		return Bytes.u8(this._code, this._nextBCI);
	}

	public setBCI(bci: number) {
		this._currentBCI = bci;
		if (this._currentBCI < this._code.length) {
			this._bytecode = Bytes.u8(this._code, bci);
			const l = lengthAt(this._code, bci);
			this._nextBCI = bci + l;
		} else {
			this._bytecode = Bytecode.END;
			this._nextBCI = this._currentBCI;
		}
	}
}