Debugging Made Easy
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © DinGrogu

//@version=5

library('debug')

// ############################################################
// # FUNCTIONS
// ############################################################

f_debug_label(_output, _delete=true, _position='') =>
    position = _position == 'DOWN' ? label.style_label_down : _position == 'UP' ? label.style_label_up : label.style_label_left

    bar = bar_index
    if (position == label.style_label_left)
        bar := bar_index + 2

    debug_label = label.new(bar, close, color=color.blue, style=position, text=str.tostring(_output), textcolor=color.white)
    if _delete
        label.delete(debug_label[1])

f_debug_array(_items, _max=500, _reversed=false) =>
    debug_array_list = ''
    total = array.size(_items)
    if (total)
        if (_reversed)
            array.reverse(_items)

        for i = 0 to total - 1
            if (i <= _max)
                debug_array_list := debug_array_list + str.tostring(array.get(_items, i))
                if (i != total - 1)
                    debug_array_list := debug_array_list + "\n"
    response = debug_array_list

f_debug_array_table(_items, _max=500, _reversed=false) =>
    table_margin = '       '
    table_text_size = size.small
    table_position = position.top_right

    total = array.size(_items)
    if (total > 0)
        if (_reversed)
            array.reverse(_items)

        var debug_table = table.new(position=table_position, columns=2, rows=total + 1, border_width=3)
        table.cell(debug_table, column=0, row=0, text='Results', bgcolor=color.blue, text_color=color.white, text_size=table_text_size)
        table.cell(debug_table, column=1, row=0, text=table_margin, bgcolor=color.new(color.blue, 100), text_color=color.new(color.white, 100))

        for i = 0 to total - 1
            if (i <= (_max - 1))
                table.cell(debug_table, column=0, row=i + 1, text=str.tostring(array.get(_items, i)), bgcolor=color.blue, text_color=color.white, text_size=table_text_size)
    else
        var debug_table = table.new(position=table_position, columns=2, rows=1, border_width=3)
        table.cell(debug_table, column=0, row=0, text='Empty Array', bgcolor=color.blue, text_color=color.white, text_size=table_text_size)
        table.cell(debug_table, column=1, row=0, text=table_margin, bgcolor=color.new(color.blue, 100), text_color=color.new(color.white, 100))
        
f_debug_message(_message, _type, _toggle) =>
    alert_type = _type == 'error' ? color.red : _type == 'warning' ? color.orange : color.blue

    var table alert_table = table.new(position.bottom_center, 1, 1, border_width=1)
    if (_toggle and (barstate.islast or barstate.islastconfirmedhistory or barstate.isrealtime))
        // We only populate the table on the last bar.
        table.set_frame_color(alert_table, color.white)
        table.set_frame_width(alert_table, 1)
        table.cell(alert_table, 0, 0, ' ' + _message + ' ', bgcolor=alert_type, text_color=color.white, text_size=size.normal)
    
// ############################################################
// # EXPORT FUNCTIONS
// ############################################################

// @function - Debug label
// @param _output <string> Label output string
// @param _delete <bool> Delete all labels and only show the last one
export label(string _output, bool _delete=false, string _position='DOWN') =>
    f_debug_label(_output, _delete, _position)

export label(float _output, bool _delete=false, string _position='DOWN') =>
    f_debug_label(_output, _delete, _position)

export label(int _output, bool _delete=false, string _position='DOWN') =>
    f_debug_label(_output, _delete, _position)

export label(bool _output, bool _delete=false, string _position='DOWN') =>
    f_debug_label(_output, _delete, _position)

// @function - Debug label on last bar
// @param _output <string> Label output string
// @param _delete <bool> Delete all labels and only show the last one
export label_last(string _output, bool _delete=true, string _position='LEFT') =>
    f_debug_label(_output, _delete, _position)

export label_last(float _output, bool _delete=true, string _position='LEFT') =>
    f_debug_label(_output, _delete, _position)

export label_last(int _output, bool _delete=true, string _position='LEFT') =>
    f_debug_label(_output, _delete, _position)

export label_last(bool _output, bool _delete=true, string _position='LEFT') =>
    f_debug_label(_output, _delete, _position)

// @function - Debug label on last bar.
// @param _items <array float> array of items.
// @param _max <int> Maximum items to display
// @param _reversed <bool> Show reversed array
export label_array(array<float> _items, int _max=500, bool _reversed=false) =>
    f_debug_label(f_debug_array(_items, _max, _reversed), true, 'LEFT')

export label_array(array<string> _items, int _max=500, bool _reversed=false) =>
    f_debug_label(f_debug_array(_items, _max, _reversed), true, 'LEFT')

export label_array(array<int> _items, int _max=500, bool _reversed=false) =>
    f_debug_label(f_debug_array(_items, _max, _reversed), true, 'LEFT')

// @function - Debug label on last bar.
// @param _items <array float> array of items.
// @param _max <int> Maximum items to display
// @param _reversed <bool> Show reversed array
export array(array<float> _items, int _max=500, bool _reversed=false) =>
    f_debug_array_table(_items, _max, _reversed)

// @function - Debug error message.
// @param _message <string> Label output string
// @param _display <bool> Toggle to show hide the message.
export error(string _message, bool _display=true) =>
    f_debug_message(_message, 'error', _display)

export error(float _message, bool _display=true) =>
    f_debug_message(str.tostring(_message), 'error', _display)

export error(int _message, bool _display=true) =>
    f_debug_message(str.tostring(_message), 'error', _display)

// @function - Debug warning message.
// @param _message <string> Label output string
// @param _display <bool> Toggle to show hide the message.
export warning(string _message, bool _display=true) =>
    f_debug_message(_message, 'warning', _display)

export warning(float _message, bool _display=true) =>
    f_debug_message(str.tostring(_message), 'warning', _display)

export warning(int _message, bool _display=true) =>
    f_debug_message(str.tostring(_message), 'warning', _display)

// @function - Debug info message.
// @param _message <string> Label output string
// @param _display <bool> Toggle to show hide the message.
export info(string _message, bool _display=true) =>
    f_debug_message(_message, 'info', _display)

export info(float _message, bool _display=true) =>
    f_debug_message(str.tostring(_message), 'info', _display)

export info(int _message, bool _display=true) =>
    f_debug_message(str.tostring(_message), 'info', _display)
