.print()
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © FFriZz

//@version=5

// @description Single function to print any type to console
library("print")
import FFriZz/ObjectHelpers/7 as OBJ


printCheck(string str=na) => 
    string _str = str
    string join = ""
    if not na(_str)
        int len = str.length(_str)
        split = str.split(_str,"\n------\n")//↓
        while len > 3800
            split.shift()
            _str := split.join("\n------\n")
            len := str.length(_str)
    
    "___Debugger___"+_str



timeframeChar(string tf) =>                
    string _tfL = ''
    _tfs = timeframe.in_seconds(tf)
    if _tfs < 60
        _tfL := str.tostring(_tfs) + 's'
    if _tfs >= 60 and _tfs < 3600
        _tfL := tf + 'm'
    if _tfs >= 3600 and _tfs < 86400
        _tfL := str.tostring(str.tonumber(tf) / 60) + 'h'
    if _tfs >= 86400
        _tfL := tf
    _tfL


STR(inp)  => str.tostring(inp)
STRF(inp) => str.tostring(inp,"0.00")
STRR(inp) => str.replace_all(inp,"][","]\n[")


// @function `method` convert all types to string
//```
//(overload)
// *.str(any inp) => string
//```
// @param inp `any` - desc | Required
// @returns `string` formatted string
export method str(string inp) => STRR(STR(inp))
export method str(int    inp) => STRR(STR(inp))
export method str(float  inp) => STRR(STRF(inp))
export method str(bool   inp) => STRR(STR(inp))

export method str(linefill inp) =>
    _1 = linefill.get_line1(inp)
    _2 = linefill.get_line2(inp)
    [X,Y,XX,YY] = OBJ.Get(_1)
    [x,y,xx,yy] = OBJ.Get(_2)
    str = str.format('line1 x1={0,number,integer}  y1={1,number,0.00}  x2={2,number,integer}  y2={3,number,0.00}',X,Y,XX,YY)
    str += "\n"
    str += str.format('line2 x1={0,number,integer}  y1={1,number,0.00}  x2={2,number,integer}  y2={3,number,0.00}',x,y,xx,yy)
    str := str.replace_all(str,",","")

export method str(line  inp) => 
    [x,y,xx,yy] = OBJ.Get(inp)
    str.replace_all(str.format('x1={0,number,integer}  y1={1,number,0.00}  x2={2,number,integer}  y2={3,number,0.00}', x,y,xx,yy),",","")


export method str(box inp) => 
    [L,T,R,B] = OBJ.Get(inp)
    str.replace_all(str.format('left={0,number,integer}  top={1,number,0.00}  right={2,number,integer}  bottom={3,number,0.00}', L,T,R,B),",","")
  

export method str(label inp) => 
    [x,y,t] = OBJ.Get(inp)
    str.replace_all(str.format('x={0,number,integer}  y={1,number,0.00}\ntext={2}', x,y,t),",","")

aStr(inp) =>
    aStr = array.new<string>()
    for OB in inp
        aStr.push(OB.str())
    str.tostring(aStr)

mStr(inp) =>
    mStr = matrix.new<string>()
	for r = 0 to inp.rows() > 0 ? inp.rows()-1 : na
		aStr = array.new<string>()
        for c = 0 to inp.columns()-1
			aStr.push(inp.get(r,c).str())
		mStr.add_row(r,aStr)
	str.tostring(mStr)


export method str(matrix<string>   inp) => STRR(STR(inp))
export method str(matrix<int> 	   inp) => STRR(STR(inp))
export method str(matrix<float>    inp) => STRR(STRF(inp))
export method str(matrix<bool>     inp) => STRR(STR(inp))
export method str(matrix<linefill> inp) => STRR(mStr(inp))
export method str(matrix<line>     inp) => STRR(mStr(inp))
export method str(matrix<box>      inp) => STRR(mStr(inp))
export method str(matrix<label>    inp) => STRR(mStr(inp))

export method str(array<linefill>  inp) => STRR(str.replace_all(aStr(inp),", line1", ",\nline1"))
export method str(array<line>      inp) => STRR(str.replace_all(aStr(inp),", x1",",\n x1"))
export method str(array<box>       inp) => STRR(str.replace_all(aStr(inp),", left",",\n left"))
export method str(array<label>     inp) => STRR(str.replace_all(aStr(inp),", x",",\n x"))
export method str(array<string>    inp) => STRR(STR(inp))
export method str(array<int>       inp) => STRR(STR(inp))
export method str(array<float>     inp) => STRR(STRF(inp))
export method str(array<bool>      inp) => STRR(STR(inp))



// @function arrayShorten
// @param str `string` - the string to shorten | Required
// @returns `string` - a shortened version of the input string if it is an array with more than 7 elements, otherwise the original string
export method arrayShorten(string str) => 
    out = ''
    if str.startswith(str,'[') and str.endswith(str,']')
        a = str.split(str,"")
        a.pop()
        a.shift()
        out := a.join("")
        build = array.new<string>()
        final = array.new<string>()
        split = str.split(out,',')
        size = split.size()
        if size >= 6
            slice = split.slice(0,3)
            final.concat(slice)
            final.push(" ...")
            slice := split.slice(size-3,size)
            final.concat(slice)
            out := "["+final.join(",")+"]"
        else
            out := str
    else
        out := str
    out


arrayOfMatrixShorten(str) =>
    _str = str
    if not str.startswith(_str,"[")
        _str := "["+_str
    if not str.endswith(_str,"]")
        _str += "]"
    arrayShorten(_str)

// @function matrixShorten
// @param str `string` - the string to shorten | Required
// @returns `string` - the shortened matrix string if the input is a matrix, otherwise returns the input string as is
export method matrixShorten(string str) => 
    out = ''
    if str.startswith(str,'[') and str.endswith(str,']')
        split = str.split(str,"]\n[")
        if split.size() > 5
            size = split.size()
            s1 = array.slice(split,0,2)
            s2 = array.slice(split,size-2,size)
            
            build1 = ""
            build2 = ""

            for [k,i] in s1
                build1 += arrayOfMatrixShorten(i)
                if k < 1
                    build1 += "\n"

            for [k,i] in s2
                build2 += arrayOfMatrixShorten(i)
                if k < 1
                    build2 += '\n'

            out := "[\n"+build1+"\n...\n"+build2+"\n]"
        else  
            for i in str.split(str,"\n")
                out += arrayOfMatrixShorten(i)
            out := "[\n"+out+"\n]"
    else
        out := str
    out




method labelCell(label _label,str="",_type,ID) =>
    _str = str
    _str := "\n------\n"+_type+" "+ID+" = "+str
    getText = _label.get_text()
    buildText = ""

    match = str.match(getText,"<\\w+>\\s+"+ID+"\\s+=\\s+.*")
    if match != ""
        matchMatch = str.match(match,ID+"\\s+=\\s+.*")
        if matchMatch != ""
            match := str.replace_all(match,matchMatch,ID+" = "+str)
            buildText := match
    else
        buildText := getText+_str

    check = printCheck(str.replace_all(buildText,"___Debugger___",""))
    _label.set_text(check)

    build2 = "[Debugger] -- "+str.format_time(time, "[MM/dd/yy] -- [HH:mm]", "UTC-5")+"\n------\n"+check

    split = str.split(str.replace(build2,"___Debugger___",""),"\n------\n")
    table = table.new(position.bottom_right,2,split.size(),#000000,color.lime,1,color.lime,1)
    for [y,z] in split
        addStr = ""
        align = text.align_right
        zSplit = str.split(z," =  ")
        if y == 0
            addStr := " -- ["+timeframeChar(timeframe.period)+"] -- [Con"+((table.all).size()).str()+"]"
            align := text.align_center
            table.merge_cells(table,0,0,1,0)
        table.cell(table,0,y,zSplit.get(0) + addStr,0,0,color.lime,align,text.align_top,"small",#000000,font.family_monospace)
        if y == 0    
            continue
        table.cell(table,1,y,zSplit.get(1),0,0,color.lime,text.align_left,text.align_top,"small",#000000,font.family_monospace)
    if (table.all).size() > 1
        (table.all).get(1).delete()

    check
    

labelCheck(str,id,_type) =>
    labelAll = label.all
    _size = labelAll.size()
    var found = false

    for i in labelAll
        txt = i.get_text()
        match = str.contains(txt,"___Debugger___")
        if match
            found := true
            new = i.copy()
            i.delete()
            new.labelCell(str,_type,id)
            break

    if not found
        label.new(na,na,"___Debugger___"+_type+" "+id+" = "+str,color=color(na),textcolor=color(na))

  

_print(x,string ID, string _type) => 
    switch ID
        "" => runtime.error("Must provide (unique) ID for each print call")
    if barstate.islast 
        labelCheck(" "+x.str(),ID,_type)


// @function print all types to theh same console with just this `method/function`
//```
//(overload)
// *.print(any x, string ID, bool shorten=true?) => console
//"param 'shorten' - only for arrays and matrixs" | true 
//```
// @param x - `any` input to convert
// @param ID - `string` unique id for label on console `MUST BE UNIQUE`
// @param shorten - `bool` true will shorten arrays and matrixs | true
// @returns adds the `ID` and the `inp` to the console on the chart
export method print(string            x, string ID) => _print(x.str(), ID, "<string>"), x 
export method print(float             x, string ID) => _print(x.str(), ID, "<float>"), x
export method print(int               x, string ID) => _print(x.str(), ID, "<int>"), x 
export method print(box               x, string ID) => _print(x.str(), ID, "<box>"), x 
export method print(bool              x, string ID) => _print(x.str(), ID, "<bool>"), x 
export method print(label             x, string ID) => _print(x.str(), ID, "<label>"), x
export method print(line              x, string ID) => _print(x.str(), ID, "<line>"), x
export method print(linefill          x, string ID) => _print(x.str(), ID, "<linefill>"), x

export method print(array<string>     x, string ID, bool shorten=true) => _x = shorten ? x.str().arrayShorten() : x.str(), _print(_x, ID, "array<string>"), x
export method print(array<float>      x, string ID, bool shorten=true) => _x = shorten ? x.str().arrayShorten() : x.str(), _print(_x, ID, "array<float>"), x
export method print(array<int>        x, string ID, bool shorten=true) => _x = shorten ? x.str().arrayShorten() : x.str(), _print(_x, ID, "array<int>"), x
export method print(array<box>        x, string ID, bool shorten=true) => _x = shorten ? x.str().arrayShorten() : x.str(), _print(_x, ID, "array<box>"), x
export method print(array<bool>       x, string ID, bool shorten=true) => _x = shorten ? x.str().arrayShorten() : x.str(), _print(_x, ID, "array<bool>"), x
export method print(array<label>      x, string ID, bool shorten=true) => _x = shorten ? x.str().arrayShorten() : x.str(), _print(_x, ID, "array<label>"), x
export method print(array<line>       x, string ID, bool shorten=true) => _x = shorten ? x.str().arrayShorten() : x.str(), _print(_x, ID, "array<line>"), x
export method print(array<linefill>   x, string ID, bool shorten=true) => _x = shorten ? x.str().arrayShorten() : x.str(), _print(_x, ID, "array<linefill>"), x

export method print(matrix<string>    x, string ID, bool shorten=true) => _x = shorten ? x.str().matrixShorten() : x.str(), _print(_x, ID, "matrix<string>"), x
export method print(matrix<float>     x, string ID, bool shorten=true) => _x = shorten ? x.str().matrixShorten() : x.str(), _print(_x, ID, "matrix<float>"), x
export method print(matrix<int>       x, string ID, bool shorten=true) => _x = shorten ? x.str().matrixShorten() : x.str(), _print(_x, ID, "matrix<int>"), x
export method print(matrix<box>       x, string ID, bool shorten=true) => _x = shorten ? x.str().matrixShorten() : x.str(), _print(_x, ID, "matrix<box>"), x
export method print(matrix<bool>      x, string ID, bool shorten=true) => _x = shorten ? x.str().matrixShorten() : x.str(), _print(_x, ID, "matrix<bool>"), x
export method print(matrix<label>     x, string ID, bool shorten=true) => _x = shorten ? x.str().matrixShorten() : x.str(), _print(_x, ID, "matrix<label>"), x
export method print(matrix<line>      x, string ID, bool shorten=true) => _x = shorten ? x.str().matrixShorten() : x.str(), _print(_x, ID, "matrix<line>"), x
export method print(matrix<linefill>  x, string ID, bool shorten=true) => _x = shorten ? x.str().matrixShorten() : x.str(), _print(_x, ID, "matrix<linefill>"), x





if barstate.islast
    label myLabel = label.new(bar_index,high,"Hello World",color=color.white).print("label")
    box myBox = box.new(bar_index-10,high,bar_index,low,border_color=color.red,border_width=2,bgcolor=color.green).print("box")
    line myLine = line.new(bar_index-5,high,bar_index + 5,high,color=color.blue,width=2).print("line")
    line myLineCopy = myLine.copy().print("lineCopy")
    
    line.set_y1(myLineCopy,low)
    line.set_y2(myLineCopy,low)
    
    linefill myLineFill = linefill.new(myLine,myLineCopy,color=color.purple).print("linefill")
    lineArray = array.from(myLine,myLineCopy).print("lineArray")
    
    boxArray = array.new<box>()
    boxArray.push(myBox)
    boxArray.push(myBox)
    boxArray.print("boxArray")
    
    boxMatrix = matrix.new<box>()
    boxMatrix.add_row(0,boxArray)
    boxMatrix.add_row(0,boxArray)
    boxMatrix.add_row(0,boxArray)
    boxMatrix.print("boxMatrix")

    for i = 0 to 2
        math.random(0,100000).print("random test "+i.str()) 
    
    m = matrix.new<int>()
    m.add_row(0,array.from(1345,11345,10767,186,1055500,105236,10057,100547))
    m.add_row(0,array.from(1111,11111,11111,111,1111111,111111,11111,111111))
    m.add_row(0,array.from(2222,22222,22222,222,2222222,222222,22222,222222))
    m.add_row(0,array.from(3333,33333,33333,333,3333333,333333,33333,333333))
    m.add_row(0,array.from(4444,44444,44444,444,4444444,444444,44444,444444))
    m.add_row(0,array.from(5555,55555,55555,555,5555555,555555,55555,555555))
    m.add_row(0,array.from(6666,66666,66666,666,6666666,666666,66666,666666))
    m.add_row(0,array.from(7777,77777,77777,777,7777777,777777,77777,777777))
    m.add_row(0,array.from(8853,88382,88588,888,3588238,538888,83538,835678))
    m.print("matrix")

    array<float> A = array.from(343.33,24.01,33.222,1.1112,44.343,0.443,33.3,3.34).print("array")

    tA = (table.all).size().print("tableall")
    lA = (label.all).size().print("labelAll")




