"""
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
"""

"""
USAGE:
inside gdb, use:
> source devtools/gdb_pretty_printers.py
"""

class StringPrinter:
    "Prints Common::String and Common::U32String"

    def __init__(self, val):
        self.val = val

    def to_string(self):
        return self.val["_str"].string()

    def display_hint(self):
        return "string"


class ArrayPrinter:
    "Prints Common::Array"

    class _iterator:
        def __init__(self, start, item_count):
            self.current_item = start
            self.item_count = item_count
            self.current_item_count = 0
            self.item_size = 8 * start.dereference().type.sizeof

        def __iter__(self):
            return self

        def __next__(self):
            if self.current_item_count == self.item_count:
                raise StopIteration
            value = self.current_item.dereference()
            index = self.current_item_count
            self.current_item_count += 1
            self.current_item += 1
            return f"[{index}]", value

    def __init__(self, val):
        self.val = val

    def to_string(self):
        size = self.val["_size"]
        capacity = self.val["_capacity"]
        return f"{self.val.type.name} of length {size}, capacity {capacity}"

    def children(self):
        return self._iterator(self.val["_storage"], self.val["_size"])

    def display_hint(self):
        return "array"

class RBTreeIterator:
    "Utility iterator for Common::RBTree"

    def __init__(self, leftmost, item_count):
        self.current_node = leftmost
        self.item_count = item_count
        self.current_item_count = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current_item_count == self.item_count:
            raise StopIteration
        value = self.current_node.dereference()["value"]
        self.current_item_count += 1
        if self.current_node.dereference()["right"]:
            while self.current_node.dereference()["left"]:
                self.current_node = self.current_node.dereference()["left"]
        else:
            parent = self.current_node.dereference()["parent"]
            while parent and parent.dereference()["right"] == self.current_node.dereference()["right"]:
                self.current_node = parent
                parent = self.current_node.dereference()["parent"]
        return value

class RBTreeMapPrinter:
    "Prints Common::StableMap and Common::MultiMap"

    class _iterator:
        def __init__(self, rb_tree):
            self.iter = RBTreeIterator(rb_tree["_leftmost"], rb_tree["_size"])
            self.count = 0

        def __iter__(self):
            return self

        def __next__(self):
            if self.count % 2 == 0:
                value = next(self.iter)
                self.current_pair = value
                item = value["first"]
            else:
                item = self.current_pair["second"]
            self.count += 1
            return f"{self.count}", item

    def __init__(self, val):
        self.val = val

    def to_string(self):
        size = self.val["_items"]["_size"]
        return f"{self.val.type.name} of length {size}"

    def children(self):
        return self._iterator(self.val["_items"])

    def display_hint(self):
        return "map"


class ListPrinter:
    "Prints Common::List"

    class _iterator:
        def __init__(self, anchor, typename):
            self.anchor = anchor
            self.current_node = anchor["_next"].dereference()
            self.current_item_count = 0
            self.node_type = gdb.lookup_type(f"{typename}::Node")

        def __iter__(self):
            return self

        def __next__(self):
            if self.current_node.address == self.anchor.address:
                raise StopIteration
            value = self.current_node.cast(self.node_type)["_data"]
            index = self.current_item_count
            self.current_item_count += 1
            self.current_node = self.current_node["_next"].dereference()
            return f"[{index}]", value

    def __init__(self, val):
        self.val = val

    def to_string(self):
        if self.val["_anchor"]["_next"] == self.val["_anchor"].address:
            return f"empty {self.val.type.name}"
        return self.val.type.name

    def children(self):
        return self._iterator(self.val["_anchor"], self.val.type.name)

    def display_hint(self):
        return "array"

import gdb.printing

def build_pretty_printer():
    pp = gdb.printing.RegexpCollectionPrettyPrinter(
        "Common")
    pp.add_printer('String', '^Common::String$', StringPrinter)
    pp.add_printer('U32String', '^Common::U32String$', StringPrinter)
    pp.add_printer('Array', '^Common::Array<.*>$', ArrayPrinter)
    pp.add_printer('StableMap', '^Common::StableMap<.*>$', RBTreeMapPrinter)
    pp.add_printer('MultiMap', '^Common::MultiMap<.*>$', RBTreeMapPrinter)
    pp.add_printer('List', '^Common::List<.*>$',  ListPrinter)
    return pp

gdb.printing.register_pretty_printer(
    gdb.current_objfile(),
    build_pretty_printer()
)