# Copyright (c) 2020 Google LLC. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# The following Skia types can be natively visualized in LLDB:
# - AutoTArray, AutoSTArray
# - SkString
# - SkTArray, SkSTArray
# - sk_sp
#
# To enable LLDB debugging support, run the following command at the (lldb) prompt:
#
#      command script import (your-skia-local-path)/platform_tools/debugging/lldb/skia.py
#
# This can be automatically enabled at the start of every debugging session by creating a
# ~/.lldbinit file which contains this command.

import lldb

def SkString_SummaryProvider(valobj, dict):
    fRec = valobj.GetChildMemberWithName('fRec')
    # The fPtr inside fRec is automatically consumed by sk_sp_SynthProvider.
    fLength = fRec.GetChildMemberWithName('fLength')
    if fLength.GetValueAsUnsigned(0) <= 0:
        return '""'
    fBeginningOfData = fRec.GetChildMemberWithName('fBeginningOfData')

    # Fetch string contents into an SBData.
    string = fBeginningOfData.AddressOf().GetPointeeData(0, fLength.GetValueAsUnsigned(0))
    # Zero terminate the SBData. (This actually adds four zero bytes, but that's harmless.)
    string.Append(lldb.SBData.CreateDataFromInt(0))
    # Convert our SBData into a string.
    error = lldb.SBError()
    string = string.GetString(error, 0)
    if error.Fail():
        return '<error: ' + error.GetCString() + '>'
    else:
        return '"' + string + '"'


class SkTArray_SynthProvider:

    def __init__(self, valobj, dict):
        self.valobj = valobj

    def num_children(self):
        try:
            size = self.fSize.GetValueAsSigned(0)
            size = max(size, 0)
            size = min(size, 10000)
            return size
        except:
            return 0

    def get_child_index(self, name):
        try:
            return int(name.lstrip('[').rstrip(']'))
        except:
            return -1

    def get_child_at_index(self, index):
        if index < 0:
            return None
        if index >= self.num_children():
            return None

        try:
            offset = index * self.dataSize
            return self.fData.CreateChildAtOffset('[' + str(index) + ']', offset, self.dataType)
        except:
            return None

    def update(self):
        try:
            self.fData = self.valobj.GetChildMemberWithName('fData')
            self.fSize = self.valobj.GetChildMemberWithName('fSize')
            self.dataType = self.fData.GetType().GetPointeeType()
            self.dataSize = self.dataType.GetByteSize()
        except:
            pass

    def has_children(self):
        return True


class AutoTArray_SynthProvider:

    def __init__(self, valobj, dict):
        self.valobj = valobj

    def num_children(self):
        try:
            count = self.fCount.GetValueAsSigned(0)
            count = max(count, 0)
            count = min(count, 10000)
            return count
        except:
            return 0

    def get_child_index(self, name):
        try:
            return int(name.lstrip('[').rstrip(']'))
        except:
            return -1

    def get_child_at_index(self, index):
        if index < 0:
            return None
        if index >= self.num_children():
            return None

        try:
            offset = index * self.dataSize
            return self.fValue.CreateChildAtOffset('[' + str(index) + ']',
                                                   offset, self.dataType)
        except:
            return None

    def update(self):
        try:
            self.fCount = self.valobj.GetChildMemberWithName('fCount')
            fArray = self.valobj.GetChildMemberWithName('fArray')
            # These lookups rely on implementation details of unique_ptr and __compressed_pair.
            ptr = fArray.GetChildMemberWithName('__ptr_')
            self.fValue = ptr.GetChildMemberWithName('__value_')
            self.dataType = self.fValue.GetType().GetPointeeType()
            self.dataSize = self.dataType.GetByteSize()
        except:
            pass

    def has_children(self):
        return True


class SkSpan_SynthProvider:

    def __init__(self, valobj, dict):
        self.valobj = valobj

    def num_children(self):
        try:
            count = self.fSize.GetValueAsSigned(0)
            count = max(count, 0)
            count = min(count, 10000)
            return count
        except:
            return 0

    def get_child_index(self, name):
        try:
            return int(name.lstrip('[').rstrip(']'))
        except:
            return -1

    def get_child_at_index(self, index):
        if index < 0:
            return None
        if index >= self.num_children():
            return None

        try:
            offset = index * self.dataSize
            return self.fPtr.CreateChildAtOffset('[' + str(index) + ']',
                                                 offset, self.dataType)
        except:
            return None

    def update(self):
        try:
            self.fPtr = self.valobj.GetChildMemberWithName('fPtr')
            self.fSize = self.valobj.GetChildMemberWithName('fSize')
            self.dataType = self.fPtr.GetType().GetPointeeType()
            self.dataSize = self.dataType.GetByteSize()
        except:
            pass

    def has_children(self):
        return True



class sk_sp_SynthProvider:

    def __init__(self, valobj, dict):
        self.valobj = valobj

    def num_children(self):
        return self.fPtr.GetNumChildren()

    def get_child_at_index(self, index):
        try:
            return self.fPtr.GetChildAtIndex(index)
        except:
            return None

    def get_child_index(self, name):
        return self.fPtr.GetIndexOfChildWithName(name)

    def update(self):
        try:
            self.fPtr = self.valobj.GetChildMemberWithName('fPtr')
        except:
            pass


def __lldb_init_module(debugger, dict):
    debugger.HandleCommand(
        'type summary add -F skia.SkString_SummaryProvider "SkString" -w skia')
    debugger.HandleCommand(
        'type synthetic add -l skia.sk_sp_SynthProvider -x "^sk_sp<.+>$" -w skia')
    debugger.HandleCommand(
        'type summary add --summary-string "fPtr = ${var.fPtr}" -x "^sk_sp<.+>$" -w skia')
    debugger.HandleCommand(
        'type synthetic add -l skia.SkTArray_SynthProvider -x "^skia_private::S?TArray<.+>$" -w skia')
    debugger.HandleCommand(
        'type synthetic add -l skia.SkSpan_SynthProvider -x "^SkSpan<.+>$" -w skia')
    debugger.HandleCommand(
        'type summary add --summary-string "size=${svar%#}" -e -x "^skia_private::S?TArray<.+>$" -w skia')
    debugger.HandleCommand(
        'type synthetic add -l skia.AutoTArray_SynthProvider -x "^skia_private::AutoS?TArray<.+>$" -w skia')
    debugger.HandleCommand(
        'type summary add --summary-string "size=${svar%#}" -e -x "^skia_private::AutoS?TArray<.+>$" -w skia')
    debugger.HandleCommand("type category enable skia")
