#-== @h1
# Utilities ( bwk.utils )
#
#-== Helper methods and convenience functions for bwk.

#-== @method
def get_dimensions(multiline_string):
    #-== Returns the width and height of a multiline string.
    # This is helpful when trying to find the size to place it in a window
    # or position it on the screen.
    #@params
    # multiline_string: the string (which can be 1 or more lines) to get dimensions from
    #@returns
    # A tuple of (width, height) in characters.
    #
    #-== The width returned is the maximum width found in the entire string.
    # The height is simply the number of lines in the string.

    height = 0
    width = 0
    for line in multiline_string.splitlines():
        height += 1
        if len(line) > width:
            width = len(line)
    return width, height


#-== @method
def margin(multiline_string, top=0, right=0, bottom=0, left=0, all=None):
    #-== Returns a new string with empty spaces added to it
    # according to the margins provided.
    #@params
    # multiline_string: the string to add the margins to
    # top: an integer indicating how many empty lines to add at the top
    # right: an integer indicating the number of empty spaces to add at the end of each line
    # bottom: an integer indicating how many empty lines to add at the bottom
    # left: an integer indicating the number of empty spaces to add at the beginning of each line
    # all: an integer which sets all 4 margins ( /top , /right , /bottom , /left ) to the same value
    #@returns
    # A new multiline string with the margins added to it.
    #@note
    # The /all argument will override any individual margin values provided.

    if all is not None:
        top = all
        right = all
        bottom = all
        left = all
    leftmargin = ' '*left
    rightmargin = ' '*right
    outlist = []
    outlist.extend(['']*top)
    for line in multiline_string.splitlines():
        newstr = f'{leftmargin}{line}{rightmargin}'
        outlist.append(newstr)
    outlist.extend(['']*bottom)
    return '\n'.join(outlist)


#-== @method
def adjust_index(index, min=None, max=None, iterable=None, wrap=False):
    #-== Returns an index which is valid for the min and max provided.
    #@params
    # index: the value to check and adjust
    # min: the minimum value allowed
    # max: the maximum value allowed
    # iterable: an object which has a /__len__ attribute to inform the /min and /max values
    # wrap: a boolean indicating if the index should wrap around from max to min and vice versa
    #@returns
    # A new index value adjusted by the provided data
    #@note
    # The /iterable argument will override any /min or /max value provided.
    #
    #-== This method checks the provided /index value and returns a new value
    # based on whether it resides within the /min and /max .
    # If an /iterable is provided, the /min and /max values are disregarded
    # and the size of the /iterable is used instead.
    # The returned value will be the same /index value, unless the /index value provided
    # does not fit within the /min and /max ( or the min\/max values of an /iterable ).
    #
    #-== When /wrap is set to /True , the returned value will "wrap around"
    # when the /index is outside the acceptable range.
    # This means that an /index below the /min will be reset to the /max value,
    # and an /index above the /max will be reset to the /min value.
    # If /wrap is /False , an /index below the /min is reset to the /min,
    # and an /index above the /max is reset to /max .

    # check the min and max or set them if an iterable is provided
    if iterable is None:
        assert min is not None, 'No minimum value provided'
        assert max is not None, 'No maximum value provided'
    else:
        min = 0
        max = len(iterable)-1
    # adjust the index based on the provided values
    if index > max:
        index = min if wrap else max
    elif index < min:
        index = max if wrap else min
    return index