class ScriptBuffer(object):
    __slots__ = ('_value')
    name = 'tmwa::map::ScriptBuffer'
    enabled = True

    def __init__(self, value):
        self._value = value

    def to_string(self):
        return self._value['debug_name']

    def get_com(self, b, i, r, labels_dict):
        # see script-parse-internal.hpp:ByteCode and script-call.cpp:get_com
        ops = '''
    NOP, POS, INT, PARAM, FUNC, STR, ARG,
    VARIABLE, EOL,
    LOR, LAND, LE, LT, GE, GT, EQ, NE,
    XOR, OR, AND, ADD, SUB, MUL, DIV, MOD,
    NEG, LNOT, NOT, R_SHIFT, L_SHIFT,
    FUNC_REF,
        '''.replace(',', '').split()
        ci = int(b[i])
        if ci < 0:
            ci += 256
        if ci >= 0x80:
            rv = 0
            sh = 0
            # Because of how the python iterator up a frame consumed the i already,
            # have to manually unroll the first iteration of the C loop.
            # TODO itertools.chain([i], r) or something?
            if True:
                if True:
                    rv += (ci & 0x7f) << sh
                    sh += 6
            if ci >= 0xc0:
                while True:
                    i = next(r)
                    ci = int(b[i])

                    if ci < 0:
                        ci += 256
                    rv += (ci & 0x7f) << sh
                    sh += 6
                    if not (ci >= 0xc0):
                        break
            return 'INT %d' % rv

        cs = ops[ci]
        if cs in {'POS', 'VARIABLE', 'FUNC_REF', 'PARAM'}:
            ai = 0
            ai |= (int(b[next(r)]) << 0)
            ai |= (int(b[next(r)]) << 8)
            ai |= (int(b[next(r)]) << 16)
            if cs == 'POS':
                ln = labels_dict.get(ai)
                if ln is not None:
                    return 'POS %d %s' % (ai, ln[0])
            elif cs == 'VARIABLE':
                global rstring_disable_children
                rstring_disable_children = True
                try:
                    rv = 'VARIABLE %s' % gdb.parse_and_eval('tmwa::map::variable_names.names._M_impl._M_start[{ai}]'.format(ai=ai))
                finally:
                    rstring_disable_children = False
                return rv
            elif cs == 'FUNC_REF':
                return 'FUNC_REF %s' % gdb.parse_and_eval('tmwa::map::builtin_functions[{ai}].name'.format(ai=ai))
            elif cs == 'PARAM':
                # https://sourceware.org/bugzilla/show_bug.cgi?id=17568
                try:
                    # this should work
                    SP = gdb.lookup_type('tmwa::SP')
                except gdb.error:
                    # this should not work
                    SP = gdb.parse_and_eval('tmwa::SP').type
                return 'PARAM %s' % gdb.Value(ai).cast(SP)
            else:
                assert False
            return '%s %d' % (cs, ai)
        elif cs == 'STR':
            buf = bytearray()
            while True:
                i = next(r)
                ci = int(b[i])
                if ci == 0:
                    break
                buf.append(ci)
            return 'STR "%s"' % bytes(buf).decode("utf-8").replace('\\', '\\\\').replace('"', '\\"')
        elif cs == 'EOL':
            return cs + '\n'
        return cs

    def children(self):
        labels = self._value['debug_labels']
        labels_begin = labels['_M_impl']['_M_start']
        labels_end = labels['_M_impl']['_M_finish']
        labels_size = int(labels_end - labels_begin)
        labels_list = [labels_begin[i] for i in range(labels_size)]
        labels_dict = {}
        char_ptr = gdb.lookup_type('char').pointer()
        for e in labels_list:
            offset = int(e['second'])
            label_name = str(e['first'].address.cast(char_ptr))
            labels_dict.setdefault(offset, []).append(label_name)
        code = self._value['script_buf']
        code_begin = code['_M_impl']['_M_start']
        code_end = code['_M_impl']['_M_finish']
        code_size = int(code_end - code_begin)
        r = iter(range(code_size))
        for i in r:
            buf = []
            for label in labels_dict.get(i, []):
                if label.startswith('On'):
                    yield 'blah', 'event %s:' % label
                else:
                    yield 'blah', 'label %s:' % label
            c = self.get_com(code_begin, i, r, labels_dict)
            yield 'blah', '%6d: %s' % (i, c)


    def display_hint(self):
        return 'array'

    src = '''
    {
        end;
    S_Sub:
        return;
    OnFoo:
        callsub S_Sub;
        setarray @a,
            -1, 0, 1,
            0x0, 0x1, 0x2,
            0x1, 0x2, 0x3,
            0x3, 0x4, 0x5,
            0x7, 0x8, 0x9,
            0xf, 0x10, 0x11,
            0x1f, 0x20, 0x21,
            0x3f, 0x40, 0x41,
            0x7f, 0x80, 0x81,
            0xff, 0x100, 0x101,
            0x1ff, 0x200, 0x201,
            0x3ff, 0x400, 0x401,
            0x7ff, 0x800, 0x801,
            0xfff, 0x1000, 0x1001,
            0x1fff, 0x2000, 0x2001,
            0x3fff, 0x4000, 0x4001,
            0x7fff, 0x8000, 0x8001,
            0xffff, 0x10000, 0x10001,
            0x1ffff, 0x20000, 0x20001,
            0x3ffff, 0x40000, 0x40001,
            0x7ffff, 0x80000, 0x80001,
            0xfffff, 0x100000, 0x100001,
            0x1fffff, 0x200000, 0x200001,
            0x3fffff, 0x400000, 0x400001,
            0x7fffff, 0x800000, 0x800001,
            0xffffff, 0x1000000, 0x1000001,
            0x1ffffff, 0x2000000, 0x2000001,
            0x3ffffff, 0x4000000, 0x4000001,
            0x7ffffff, 0x8000000, 0x8000001,
            0xfffffff, 0x10000000, 0x10000001,
            0x1fffffff, 0x20000000, 0x20000001,
            0x3fffffff, 0x40000000, 0x40000001,
            0x7fffffff, 0x80000000, 0x80000001,
            0xffffffff;
        set TEST_FAKE_PARAM_BASELEVEL, TEST_FAKE_CONSTANT;
        set @s$, "hello";
        set @i, a || b;
        set @i, a && b;
        set @i, a <= b;
        set @i, a < b;
        set @i, a >= b;
        set @i, a > b;
        set @i, a == b;
        set @i, a != b;
        set @i, a ^ b;
        set @i, a | b;
        set @i, a & b;
        set @i, a + b;
        set @i, a - b;
        set @i, a * b;
        set @i, a / b;
        set @i, a % b;
        set @i, - b;
        set @i, ! b;
        set @i, ~ b;
        set @i, a >> b;
        set @i, a << b;
    }
    '''.replace('\n', ' ')

    asm = ''\
    +'''     0: FUNC_REF "end",
             4: ARG,
             5: FUNC,
             6: EOL
        ,
        label "S_Sub":,
             7: FUNC_REF "return",
            11: ARG,
            12: FUNC,
            13: EOL
        ,
        label "OnFoo":,
            14: FUNC_REF "callsub",
            18: ARG,
            19: POS 7 "S_Sub",
            23: FUNC,
            24: EOL
        ,
            25: FUNC_REF "setarray",
            29: ARG,
            30: VARIABLE "@a",
            34: INT 1,
            35: NEG,
            36: INT 0,
            37: INT 1,
            38: INT 0,
            39: INT 1,
            40: INT 2,
            41: INT 1,
            42: INT 2,
            43: INT 3,
            44: INT 3,
            45: INT 4,
            46: INT 5,
            47: INT 7,
            48: INT 8,
            49: INT 9,
            50: INT 15,
            51: INT 16,
            52: INT 17,
            53: INT 31,
            54: INT 32,
            55: INT 33,
            56: INT 63,
            57: INT 64,
            59: INT 65,
            61: INT 127,
            63: INT 128,
            65: INT 129,
            67: INT 255,
            69: INT 256,
            71: INT 257,
            73: INT 511,
            75: INT 512,
            77: INT 513,
            79: INT 1023,
            81: INT 1024,
            83: INT 1025,
            85: INT 2047,
            87: INT 2048,
            89: INT 2049,
            91: INT 4095,
            93: INT 4096,
            95: INT 4097,
            97: INT 8191,
           100: INT 8192,
           103: INT 8193,
           106: INT 16383,
           109: INT 16384,
           112: INT 16385,
           115: INT 32767,
           118: INT 32768,
           121: INT 32769,
           124: INT 65535,
           127: INT 65536,
           130: INT 65537,
           133: INT 131071,
           136: INT 131072,
           139: INT 131073,
           142: INT 262143,
           145: INT 262144,
           148: INT 262145,
           151: INT 524287,
           155: INT 524288,
           159: INT 524289,
           163: INT 1048575,
           167: INT 1048576,
           171: INT 1048577,
           175: INT 2097151,
           179: INT 2097152,
           183: INT 2097153,
           187: INT 4194303,
           191: INT 4194304,
           195: INT 4194305,
           199: INT 8388607,
           203: INT 8388608,
           207: INT 8388609,
           211: INT 16777215,
           215: INT 16777216,
           219: INT 16777217,
           223: INT 33554431,
           228: INT 33554432,
           233: INT 33554433,
           238: INT 67108863,
           243: INT 67108864,
           248: INT 67108865,
           253: INT 134217727,
           258: INT 134217728,
           263: INT 134217729,
           268: INT 268435455,
           273: INT 268435456,
           278: INT 268435457,
           283: INT 536870911,
           288: INT 536870912,
           293: INT 536870913,
           298: INT 1073741823,
           303: INT 1073741824,
           308: INT 1073741825,
           313: INT 2147483647,
           319: INT 2147483648,
           325: INT 2147483649,
           331: INT 4294967295,
           337: FUNC,
           338: EOL
        ,
           339: FUNC_REF "set",
           343: ARG,
           344: PARAM tmwa::SP::BASELEVEL,
           348: INT 42,
           349: FUNC,
           350: EOL
        ,
           351: FUNC_REF "set",
           355: ARG,
           356: VARIABLE "@s$",
           360: STR "hello",
           367: FUNC,
           368: EOL
        ,
           369: FUNC_REF "set",
           373: ARG,
           374: VARIABLE "@i",
           378: VARIABLE "a",
           382: VARIABLE "b",
           386: LOR,
           387: FUNC,
           388: EOL
        ,
           389: FUNC_REF "set",
           393: ARG,
           394: VARIABLE "@i",
           398: VARIABLE "a",
           402: VARIABLE "b",
           406: LAND,
           407: FUNC,
           408: EOL
        ,
           409: FUNC_REF "set",
           413: ARG,
           414: VARIABLE "@i",
           418: VARIABLE "a",
           422: VARIABLE "b",
           426: LE,
           427: FUNC,
           428: EOL
        ,
           429: FUNC_REF "set",
           433: ARG,
           434: VARIABLE "@i",
           438: VARIABLE "a",
           442: VARIABLE "b",
           446: LT,
           447: FUNC,
           448: EOL
        ,
           449: FUNC_REF "set",
           453: ARG,
           454: VARIABLE "@i",
           458: VARIABLE "a",
           462: VARIABLE "b",
           466: GE,
           467: FUNC,
           468: EOL
        ,
           469: FUNC_REF "set",
           473: ARG,
           474: VARIABLE "@i",
           478: VARIABLE "a",
           482: VARIABLE "b",
           486: GT,
           487: FUNC,
           488: EOL
        ,
           489: FUNC_REF "set",
           493: ARG,
           494: VARIABLE "@i",
           498: VARIABLE "a",
           502: VARIABLE "b",
           506: EQ,
           507: FUNC,
           508: EOL
        ,
           509: FUNC_REF "set",
           513: ARG,
           514: VARIABLE "@i",
           518: VARIABLE "a",
           522: VARIABLE "b",
           526: NE,
           527: FUNC,
           528: EOL
        ,
           529: FUNC_REF "set",
           533: ARG,
           534: VARIABLE "@i",
           538: VARIABLE "a",
           542: VARIABLE "b",
           546: XOR,
           547: FUNC,
           548: EOL
        ,
           549: FUNC_REF "set",
           553: ARG,
           554: VARIABLE "@i",
           558: VARIABLE "a",
           562: VARIABLE "b",
           566: OR,
           567: FUNC,
           568: EOL
        ,
           569: FUNC_REF "set",
           573: ARG,
           574: VARIABLE "@i",
           578: VARIABLE "a",
           582: VARIABLE "b",
           586: AND,
           587: FUNC,
           588: EOL
        ,
           589: FUNC_REF "set",
           593: ARG,
           594: VARIABLE "@i",
           598: VARIABLE "a",
           602: VARIABLE "b",
           606: ADD,
           607: FUNC,
           608: EOL
        ,
           609: FUNC_REF "set",
           613: ARG,
           614: VARIABLE "@i",
           618: VARIABLE "a",
           622: VARIABLE "b",
           626: SUB,
           627: FUNC,
           628: EOL
        ,
           629: FUNC_REF "set",
           633: ARG,
           634: VARIABLE "@i",
           638: VARIABLE "a",
           642: VARIABLE "b",
           646: MUL,
           647: FUNC,
           648: EOL
        ,
           649: FUNC_REF "set",
           653: ARG,
           654: VARIABLE "@i",
           658: VARIABLE "a",
           662: VARIABLE "b",
           666: DIV,
           667: FUNC,
           668: EOL
        ,
           669: FUNC_REF "set",
           673: ARG,
           674: VARIABLE "@i",
           678: VARIABLE "a",
           682: VARIABLE "b",
           686: MOD,
           687: FUNC,
           688: EOL
        ,
           689: FUNC_REF "set",
           693: ARG,
           694: VARIABLE "@i",
           698: VARIABLE "b",
           702: NEG,
           703: FUNC,
           704: EOL
        ,
           705: FUNC_REF "set",
           709: ARG,
           710: VARIABLE "@i",
           714: VARIABLE "b",
           718: LNOT,
           719: FUNC,
           720: EOL
        ,
           721: FUNC_REF "set",
           725: ARG,
           726: VARIABLE "@i",
           730: VARIABLE "b",
           734: NOT,
           735: FUNC,
           736: EOL
        ,
           737: FUNC_REF "set",
           741: ARG,
           742: VARIABLE "@i",
           746: VARIABLE "a",
           750: VARIABLE "b",
           754: R_SHIFT,
           755: FUNC,
           756: EOL
        ,
           757: FUNC_REF "set",
           761: ARG,
           762: VARIABLE "@i",
           766: VARIABLE "a",
           770: VARIABLE "b",
           774: L_SHIFT,
           775: FUNC,
           776: EOL
        ,
           777: NOP'''.replace('''
        ''', '\n')

    test_extra = ('''
    #include "../compat/borrow.hpp"
    #include "../io/line.hpp"
    #include "../mmo/clif.t.hpp"
    #include "../ast/script.hpp"
    #include "../map/script-parse-internal.hpp"

    using tmwa::operator "" _s;

    static
    const tmwa::map::ScriptBuffer& test_script_buffer(tmwa::LString source)
    {
        auto p = tmwa::map::add_strp("TEST_FAKE_PARAM_BASELEVEL"_s);
        p->type = tmwa::map::StringCode::PARAM;
        p->val = static_cast<uint16_t>(tmwa::SP::BASELEVEL);
        p = tmwa::map::add_strp("TEST_FAKE_CONSTANT"_s);
        p->type = tmwa::map::StringCode::INT;
        p->val = 42;

        tmwa::io::LineCharReader lr(tmwa::io::from_string, "<script debug print test>"_s, source);
        tmwa::ast::script::ScriptOptions opt;
        opt.implicit_start = true;
        opt.implicit_end = true;
        auto code_res = tmwa::ast::script::parse_script_body(lr, opt);
        auto code = TRY_UNWRAP(code_res.get_success(), abort());
        auto ups = tmwa::map::compile_script("script debug print test"_s, code, opt.implicit_end);
        assert(ups);
        return *ups.release();
    }
    ''')

    # the pretty-printer is designed to be run with 'set print array on'
    # but the testsuite runs with all settings default, in this case off.
    tests = [
            ('test_script_buffer("' + src.replace('\\', '\\\\').replace('"', '\\"') + '"_s)',
                '"script debug print test" = {' + asm.replace('\n', ' ').replace('EOL ,', 'EOL\n,') + '}'),
    ]