Logger Library For Pinescript (Logging and Debugging)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © paragjyoti2012

//@version=5

// @description

// // This is a logging library for Pinescript. It is aimed to help developers testing and debugging scripts with a simple to use logger function.
// // Pinescript lacks a native logging implementation. This library would be helpful to mitigate this insufficiency. 
// // This library uses table to print outputs into its view. It is simple, customizable and robust. 
// // You can start using it's .log() method just like any other logging method in other languages.


// //////////////////
// USAGE
// //////////////////


// Importing the Library
// ---------------------

// import paragjyoti2012/LoggerLib/<version_no> as Logger



// .init() : Initializes the library and returns the logger pointer. (Later will be used as a function parameter)

// .initTable: Initializes the Table View for the Logger and returns the table id. (Later will be used as a function parameter)

// parameters:
//     logger: The logger pointer got from .init()
//     max_rows_count: Number of Rows to display in the Logger Table (default is 10)
//     offset: The offset value for the rows (Used for scrolling the view)
//     position: Position of the Table View
//         Values could be:
//             left
//             right
//             top-right
//         (default is left)
    
//     size: Font Size of content
//         Values could be:
//             small
//             normal
//             large
//         (default is small)
//     hide_date: Wheter to hide the Date/Time column in the Logger (default is false)
//     stickyRows: Some logs could be sticked at the bottom of the logs. This type of of logs will be updated without adding more to the list of logs. 
//                 These kind of logs will be helpful to log at the last bar or logs that needs constant attention.
//                 Maximum recommended rows is 5. Default is set to 0, so it won't show sticky logs without explicitly setting it.
//                 To log a sticky log, you need to pass the stickPosition parameter (ranging from 1 to 5) for the .log() method.

// returns: Table

// example usage of .initTable()

// import paragjyoti2012/LoggerLib/1 as Logger

// var logger=Logger.init()
// var logTable=Logger.initTable(logger, max_rows_count=20, offset=0, position="top-right")


// -------------------
// LOGGING
// -------------------

// .log() : Logging Method 
// params: (string message, string[] logger, table table_id, string type="message")

// logger: pass the logger pointer from .init()
// table_id: pass the table pointer from .initTable()
// message: The message to log
// type: Type of the log message
//     Values could be:
//         message
//         warning
//         error
//         info
//         success
//     (Default is message)
//
// stickPosition: To show logs as sticky logs (Some logs could be sticked at the bottom of the logs, this type of of logs will be updated without adding more to the list of logs.)
//                You need to pass the stickPosition parameter. It is the position number that you want to display the log at the bottom of the list. (Ranging from 1 to 5)
//                If you want to stick the log at position 3, you need to pass 3 as stickPosition.


// returns: void

// ///////////////////////////////////////
// Full Boilerplate For Using In Indicator
// ///////////////////////////////////////

// offset=input.int(0,"Offset",minval=0)
// size=input.string("small","Font Size",options=["normal","small","large"])
// rows=input.int(15,"No Of Rows")
// position=input.string("left","Position",options=["left","right","top-right"])
// hide_date=input.bool(false,"Hide Time")

// import paragjyoti2012/LoggerLib/5 as Logger

// var logger=Logger.init()
// var logTable=Logger.initTable(logger,rows,offset,position,size,hide_date)

// rsi=ta.rsi(close,14)
// [macd,signal,hist]=ta.macd(close,12,26,9)


// if(ta.crossunder(close,34000))
//     Logger.log("Dropped Below 34000",logger,logTable,"warning")
// if(ta.crossunder(close,35000))
//     Logger.log("Dropped Below 35000",logger,logTable)
// if(ta.crossover(close,38000))
//     Logger.log("Crossed 38000",logger,logTable,"info")
// if(ta.crossunder(rsi,20))
//     Logger.log("RSI Below 20",logger,logTable,"error")
// if(ta.crossover(macd,signal))
//     Logger.log("Macd Crossed Over Signal",logger,logTable)
// if(ta.crossover(rsi,80))
//     Logger.log("RSI Above 80",logger,logTable,"success")
    
// /////////////////////
// For any assistance using this library or reporting issues, please write in the comment section below.
// I will try my best to guide you and update the library. Thanks :)
// /////////////////////    

library("LoggerLib")
// library("LogLib_Pvt")

import paragjyoti2012/STR_Dictionary_Lib/7 as DictLib


export init()=>
    obj=DictLib.init("barIndex=,time=,message=,type=,rows=,size=,hide_date=,offset=,stickyRows=")
    DictLib.initStack(obj)



export initTable(string[] logger, int max_rows_count=10, int offset=0, string position="left", string size="small", bool hide_date=false,int stickyRows=0)=>
    
    _stickyRows=stickyRows+2
    
    mytable=table.new(position=="right"?position.bottom_right:position=="top-right"?position.top_right:position.bottom_left, hide_date?2:3, max_rows_count + 1 + (stickyRows? _stickyRows+1:0), border_width = 1)
    
    if(max_rows_count!=10)
        DictLib.setIntElement(logger,0,"rows", max_rows_count)
    if(size!="small")
        DictLib.setElement(logger,0,"size", size)
    if(hide_date)
        DictLib.setElement(logger,0,"hide_date", "true")
    if(offset)
        DictLib.setIntElement(logger,0,"offset", offset)
    if(stickyRows)
        DictLib.setIntElement(logger,0,"stickyRows",_stickyRows)
        for i=0 to _stickyRows-1
            sticky_obj=DictLib.init("barIndex=,time=,message=,type=")
            DictLib.insertToStack(logger,sticky_obj,1)
    mytable
    

renderCell(logger,log_index,table_id,cell_id,tb_size,show_date)=>
    
    msgtype = DictLib.getElement(logger,log_index,"type")
        
    color msg_color =  switch msgtype
        'message' => #cccccc
        'warning' => #F5AC4E 
        'error'   => #DD4224
        'success' => #00c121
        'info'    => #02d0fd
        => na
    color txt_color =  switch msgtype
        'message' => #000000
        'warning' => #000000 
        'error'   => #ffffff
        'success' => #ffffff
        'info'    => #000000
        => na
        
    timestr=DictLib.getElement(logger,log_index,"time")
    msg=DictLib.getElement(logger,log_index,"message")
    barindex=DictLib.getElement(logger,log_index,"barIndex")
    
    table.cell(table_id, 0, cell_id , msg, bgcolor = msg_color, text_size = tb_size, text_color=txt_color)
    table.cell(table_id, 1, cell_id , barindex, bgcolor = msg_color, text_size = tb_size, text_color=txt_color)
    if(show_date)
        table.cell(table_id, 2, cell_id , timestr, bgcolor = msg_color, text_size = tb_size, text_color=txt_color)
    
    

export log(string message, string[] logger, table table_id, string type="message", int stickPosition=0) => 
    obj=DictLib.init("barIndex=,time=,message=,type=")
    tm=str.tostring(year) + "-" + str.tostring(month) + "-" + str.tostring(dayofmonth) + " | " + str.tostring(hour) + ":" + str.tostring(minute) + (timeframe.isseconds? ":" + str.tostring(second):"")
    
    _stickPosition=stickPosition+2
    stickyRows=0
    if(DictLib.getElement(logger,0,"stickyRows")!="")
        stickyRows:=DictLib.getIntElement(logger,0,"stickyRows")

    DictLib.setInt(obj,"barIndex", bar_index)
    DictLib.set(obj,"time", tm)
    DictLib.set(obj,"message", message)
    DictLib.set(obj,"type", type)
    
    if(stickyRows and stickPosition)
        DictLib.updateStack(logger,obj,_stickPosition)
    else
        DictLib.insertToStack(logger,obj,stickyRows+1)

    
    
    max_rows_count=10
    size="small"
    show_date=true
    offset=0
    
    if(DictLib.getElement(logger,0,"rows")!="")
        max_rows_count:=DictLib.getIntElement(logger,0,"rows")
    if(DictLib.getElement(logger,0,"size")!="")
        size:=DictLib.getElement(logger,0,"size")
    if(DictLib.getElement(logger,0,"hide_date")=="true")
        show_date:=false
    if(DictLib.getElement(logger,0,"offset")!="")
        offset:=DictLib.getIntElement(logger,0,"offset")
    
    tb_size=size=="small"?size.small:size=="large"?size.large:size.normal
    
    
    table.cell(table_id, 0, 0, "Log", bgcolor = color.black, text_size = tb_size,text_color=color.white)
    
    table.cell(table_id, 1, 0, "Bar #",   bgcolor = color.black, text_size = tb_size,text_color=color.white)
    if(show_date)
        table.cell(table_id, 2, 0, "Time",    bgcolor = color.black, text_size = tb_size,text_color=color.white)
    
    
    
    

    
    for row_id = 1 to max_rows_count
       
        log_index = row_id + offset + stickyRows
        
        if(log_index>array.size(logger)-1)
            break
    
        cell_id=math.min(max_rows_count+1,array.size(logger)-stickyRows)-row_id
        
        renderCell(logger,log_index,table_id,cell_id,tb_size,show_date)
    
    if(stickyRows)
        table.cell(table_id, 0, max_rows_count+1)
        table.cell(table_id, 0, max_rows_count+2, "Sticky Log",   bgcolor = color.black, text_size = tb_size,text_color=color.white)
        table.cell(table_id, 1, max_rows_count+2, " ",   bgcolor = color.black, text_size = tb_size,text_color=color.white)
        if(show_date)
            table.cell(table_id, 2, max_rows_count+2, " ",   bgcolor = color.black, text_size = tb_size,text_color=color.white)
        
        for s_row_id = 3 to stickyRows
           
            log_index = s_row_id

            cell_id=max_rows_count+1+s_row_id
            
            renderCell(logger,log_index,table_id,cell_id,tb_size,show_date)




