log
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © GETpacman

//@version=5
// @description A dual logging library for developers.
library("log")

export type log
    matrix<string>  mqx
    matrix<int>     hqx
	matrix<color>	cqx
    array<int>     	intDB
    array<bool>     boolDB
    array<string>  	stringDB
    matrix<color>   colorDB
	array<string>  	levelDB
	color			HeaderColor
	color			HeaderColorBG
	color			StatusColor
	color			StatusColorBG
	color			TextColor
	color			TextColorBG
	color			FrameColor
	int 			FrameSize		= 1 
	int 			CellBorderSize	= 0
	color 			CellBorderColor
    color           SeparatorColor  = color.gray
	table			tbx
	bool IsConsole			= true
	string HeaderQbarIndex  = 'Bar#'
	string HeaderQdateTime  = 'Date'
	string HeaderQerrorCode	= 'eCode'
	string HeaderQlogLevel	= 'State'
	string HeaderQ1			= 'h.Q1'
	string HeaderQ2			= 'h.Q2'
	string HeaderQ3			= 'h.Q3'
	string HeaderQ4			= 'h.Q4'
	string HeaderQ5			= 'h.Q5'
	string HeaderQ6			= 'h.Q6'
    string Status           = ''
    int PageHistory         = 0
	bool PageOnEveryBar     = false
	bool MoveLogUp          = true
	bool ShowStatusBar		= true
	bool StatusBarAtBottom	= true
	bool ColorText     		= true
	bool HighlightText      = false
	bool ShowMetaStatus     = true
	bool ShowBarIndex       = false
	bool ShowDateTime       = false
	bool ShowLogLevels    	= false
	bool ReplaceWithErrorCodes=false
    bool RestrictLevelsToKey7     = false
	bool MarkNewBar			= true
	int MinWidth       		= 40
	bool ShowHeader			= true
	bool HeaderAtTop		= true
	bool ShowQ1             = true
	bool ShowQ2             = true
	bool ShowQ3             = true
	bool ShowQ4             = true
	bool ShowQ5             = true
	bool ShowQ6             = true
	bool AutoMerge          = true
	int TabSizeQ1	        = 0
	int TabSizeQ2           = 0
	int TabSizeQ3           = 0
	int TabSizeQ4           = 0
	int TabSizeQ5           = 0
	int TabSizeQ6           = 0
	bool PrefixLogLevel     = false

_consolePosition(int position) =>
    _position=switch position
        1 =>  'top'
        2 =>  'right'
        4 =>  'left'
        =>  'bottom'
_consolePosition(string position='anywhere') =>
    _position = switch str.lower(position)
        'top'   =>  1
        'right' =>  2
        'left'  =>  4
        => 3
    _position

method _resetLocations(log this) =>
    this.intDB.set(17,0),this.intDB.set(18,0),this.intDB.set(19,1),this.intDB.set(20,8)
    this.intDB.set(13,0),this.intDB.set(14,10),this.intDB.set(15,this.intDB.get(1)-1+(this.ShowHeader?1:0)+(this.ShowStatusBar?1:0)),this.intDB.set(16,18)
    this.intDB.set(11,3)

method _switchMode(log this) =>
    if not this.boolDB.get(1) and this.IsConsole
        for x=0 to this.mqx.rows()>0 ? (this.mqx.rows()-1) : na
            _toLog=  (this.ShowBarIndex ? (str.tostring(this.hqx.get(x,1))+', ') : '') + 
                     (this.ShowDateTime ? (str.format('{0,date,'+this.stringDB.get(4)+'}',this.hqx.get(x,2))+'  ') : '') + 
                     ((this.TabSizeQ1>0 and this.hqx.get(x,0)>1)? str.substring(this.stringDB.get(3),0,(math.min(7,this.hqx.get(x,0))-1)*this.TabSizeQ1):'') +
                     ( (this.PrefixLogLevel and this.hqx.get(x,0)>=1 and this.hqx.get(x,0)<=7 and (this.hqx.get(x,0)+1)<=this.levelDB.size())? (this.levelDB.get(this.hqx.get(x,0)+1) +':  ' ):'') +
                     this.mqx.get(x,0)
            this.mqx.set(x,5,this.mqx.get(x,0))
            this.mqx.set(x,0,_toLog)
            this.hqx.set(x,4,0)
            this.hqx.set(x,3,str.length(_toLog))
    if this.boolDB.get(1) and not this.IsConsole
        for x=0 to this.mqx.rows()>0?(this.mqx.rows()-1) : na
            this.mqx.set(x,0,this.mqx.get(x,5))
            this.mqx.set(x,5,'')
            this.hqx.set(x,3,str.length(this.mqx.get(x,0)))
            this.hqx.set(x,4,this.hqx.get(x,3)+str.length(this.mqx.get(x,1))+str.length(this.mqx.get(x,2))+str.length(this.mqx.get(x,3))+str.length(this.mqx.get(x,4))+str.length(this.mqx.get(x,5)))
    this.boolDB.set(1, this.IsConsole)

// @function Clears all the queue, including bar_index and time queues, of existing messages
export method clear (log this) =>
    this.mqx.fill( '', 0, this.mqx.rows(), 0, this.mqx.columns())
    this.hqx.fill( -1, 0, this.hqx.rows(), 0, this.hqx.columns())
    this.intDB.set(7,0)

// @function Resizes the message queues. If size is decreased then removes the oldest messages
// @param rows The new size needed for the queues. Default value is 40.
export method resize (log this, int rows=40) =>
    int _txn    = this.intDB.get(0)
    int _qsize  = this.intDB.get(1)
    int _qc     = (_txn<=-1 or _qsize<1) ? na : (_txn%_qsize)
    int _qn     = (_txn<=-1 or _qsize<1) ? na : ((_txn+1)%_qsize)

    if rows<=0
        this.clear()
    else
        if _qsize > rows and _qsize != rows
            
            for x = 1 to (_qsize - rows)
                this.mqx.remove_row(_qn),this.hqx.remove_row(_qn),this.cqx.remove_row(_qn)
                _qn := (_txn<=-1) ? na : ((_txn+1)%_qsize)
                if _qn>_qc
                    _qn := (_qc+1 > (_qsize-1-x)) ? 0 : _qn 
                else
                    _qc -= 1
        if _qsize < rows and _qsize != rows
            for x =1 to (rows - _qsize)
                this.mqx.add_row(_qn,array.new_string(this.mqx.columns(),'')),this.hqx.add_row(_qn,array.new_int(this.hqx.columns(),-1)),this.cqx.add_row(_qn,array.new_color(this.cqx.columns(),na))
        this.intDB.set(1, rows)
        _txn := (_txn+1) < _qsize ? _txn : (rows-1+_qn)
        this.intDB.set(0, _txn)
        this.intDB.set(15, rows-1+(this.ShowHeader?1:0)+(this.ShowStatusBar?1:0) + (this.intDB.get(11)==1 ? 2 : 0))

// @function Re/set the date time format used for displaying date and time. Default resets to dd.MMM.yy HH:mm
export method dateTimeFormat(log this, string format='') =>
	this.stringDB.set(4, format==''?'dd.MMM.yy HH:mm':format)

// @function Resets the text color of the log to library default.
export method resetTextColor(log this) =>
    this.TextColor := color.new(chart.fg_color,60)

// @function Resets the background color of the log to library default.
export method resetTextColorBG(log this) =>
    this.TextColorBG := chart.bg_color

    
// @function Resets the color used for Headers, to library default.
export method resetHeaderColor(log this) =>
    this.TextColor := color.new(chart.fg_color,30)

// @function Resets the background color used for Headers, to library default.
export method resetHeaderColorBG(log this) =>
    this.HeaderColorBG := color.new(#434651,80)


// @function Resets the text color of the status row, to library default.
export method resetStatusColor(log this) =>
    this.StatusColor := color.new(#9598a1,50)
    
// @function Resets the background color of the status row, to library default.
export method resetStatusColorBG(log this) =>
    this.StatusColorBG := color.new(#434651,70)
    
// @function Resets the color used for the frame around the log table, to library default.
export method resetFrameColor(log this) =>
    this.FrameColor := chart.bg_color

// @function Resets the color used for the highlighting when Highlight Text option is used, to library default
export method resetColorsHC(log this) =>
    if this.colorDB.rows()<37
        for x=1 to 37-this.colorDB.rows()
            this.colorDB.add_row(0)

    this.colorDB.set(1, 2,color.new(color.white,10)),this.colorDB.set(2, 2,color.new(color.white,10)),this.colorDB.set(3, 2,color.new(color.white,10)),this.colorDB.set(4, 2,color.new(color.white,10)),this.colorDB.set(5, 2,color.new(color.black,10)),this.colorDB.set(6, 2,color.new(color.black,10)),this.colorDB.set(7, 2,color.new(color.white,10))
    this.colorDB.set(8, 2,color.new(color.white,10)),this.colorDB.set(9, 2,color.new(color.white,10)),this.colorDB.set(10,2,color.new(color.white,10)),this.colorDB.set(11,2,color.new(color.white,10)),this.colorDB.set(12,2,color.new(color.white,10)),this.colorDB.set(13,2,color.new(color.white,10)),this.colorDB.set(14,2,color.new(color.black,10))
    this.colorDB.set(15,2,color.new(color.white,10)),this.colorDB.set(16,2,color.new(color.white,10)),this.colorDB.set(17,2,color.new(color.white,10)),this.colorDB.set(18,2,color.new(color.white,10)),this.colorDB.set(19,2,color.new(color.white,10)),this.colorDB.set(20,2,color.new(color.white,10)),this.colorDB.set(21,2,color.new(color.black,10))
    this.colorDB.set(22,2,color.new(color.black,10)),this.colorDB.set(23,2,color.new(color.white,10)),this.colorDB.set(24,2,color.new(color.white,10)),this.colorDB.set(25,2,color.new(color.white,10)),this.colorDB.set(26,2,color.new(color.black,10)),this.colorDB.set(27,2,color.new(color.white,10)),this.colorDB.set(28,2,color.new(color.white,10))
    this.colorDB.set(29,2,color.new(color.white,10)),this.colorDB.set(30,2,color.new(color.white,10)),this.colorDB.set(31,2,color.new(color.white,10)),this.colorDB.set(32,2,color.new(color.white,10)),this.colorDB.set(33,2,color.new(color.white,10)),this.colorDB.set(34,2,color.new(color.black,10)),this.colorDB.set(35,2,color.new(color.black,10))
    this.colorDB.set(36,2,color.new(color.white,10))

// @function Resets the background color used for setting the background color, when the Color Text option is used, to library default
export method resetColorsBG(log this) =>
    if this.colorDB.rows()<37
        for x=1 to 37-this.colorDB.rows()
            this.colorDB.add_row(0)

	for x=0 to this.colorDB.rows()-1
		this.colorDB.set(x,1,chart.bg_color)

// @function Resets the color used for respective error codes, when the Color Text option is used, to library default
export method resetColors(log this) =>
    if this.colorDB.rows()<37
        for x=1 to 37-this.colorDB.rows()
            this.colorDB.add_row(0)

    this.colorDB.set(1, 0, color.new(#2196f3,10)),this.colorDB.set(2, 0, color.new(#4caf50,10)),this.colorDB.set(3, 0, color.new(#9598a1,20)),this.colorDB.set(4, 0, color.new(#c29857,20)),this.colorDB.set(5, 0, color.new(#fbc02d,10)),this.colorDB.set(6, 0, color.new(#f57f17,00)),this.colorDB.set(7, 0, color.new(#f44336,10))
    this.colorDB.set(8, 0, color.new(color.aqua,10)),this.colorDB.set(9, 0, color.new(color.blue,10)),this.colorDB.set(10,0, color.new(color.red,10)),this.colorDB.set(11,0, color.new(color.fuchsia,10)),this.colorDB.set(12,0, color.new(color.gray,10)),this.colorDB.set(13,0, color.new(color.green,10)),this.colorDB.set(14,0, color.new(color.lime,10))
    this.colorDB.set(15,0, color.new(color.maroon,10)),this.colorDB.set(16,0, color.new(color.navy,10)),this.colorDB.set(17,0, color.new(color.olive,10)),this.colorDB.set(18,0, color.new(color.purple,10)),this.colorDB.set(19,0, color.new(color.silver,10)),this.colorDB.set(20,0, color.new(color.teal,10)),this.colorDB.set(21,0, color.new(color.white,10))
    this.colorDB.set(22,0, color.new(color.yellow,10)),this.colorDB.set(23,0, color.new(#9C6E1B,00)),this.colorDB.set(24,0, color.new(#AA00FF,10)),this.colorDB.set(25,0, color.new(#673ab7,10)),this.colorDB.set(26,0, color.new(#CCCC00,10)),this.colorDB.set(27,0, color.new(#c29857,10)),this.colorDB.set(28,0, color.new(#5d606b,00))
    this.colorDB.set(29,0, color.new(#fc5d07,10)),this.colorDB.set(30,0, color.new(#004d40,10)),this.colorDB.set(31,0, color.new(#b71c1c,10)),this.colorDB.set(32,0, color.new(#7e57c2,50)),this.colorDB.set(33,0, color.new(#388e3c,20)),this.colorDB.set(34,0, color.new(#ffeb3b,00)),this.colorDB.set(35,0, color.new(#fbc02d,00))
    this.colorDB.set(36,0, color.new(#ff9850,10))
    this.resetColorsHC(),this.resetColorsBG()

// @function Sets the colors corresponding to error codes
//  Index 0 of input array c is color is reserved for future use.
//  Index 1 of input array c is color for debug code 1.
//  Index 2 of input array c is color for debug code 2.
//  There are 2 modes of coloring
//  1 . Using the Foreground color
//  2 . Using the Foreground color as background color and a white/black/gray color as foreground color
//      This is denoting or highlighting. Which effectively puts the foreground color as background color
// @param c Array of colors to be used for corresponding error codes. If the corresponding code is not found, then text color is used
export method setColors(log this, color[] c) =>
    sc=c.size()
    sq=this.colorDB.rows()
    if sq<sc
        trow=this.colorDB.row(0)
        for x=1 to sc-sq
            this.colorDB.add_row(0)
    if sq>sc and sc>0
        for x=1 to sq-sc
            this.colorDB.remove_row(0)


    for x=0 to (sc > 0 ? sc-1 : na)
        this.colorDB.set(x,0,c.get(x))
	this.intDB.set(21,this.colorDB.rows())

// @function Sets the highlight colors corresponding to error codes
//  Index 0 of input array c is color is reserved for future use.
//  Index 1 of input array c is color for debug code 1.
//  Index 2 of input array c is color for debug code 2.
//  There are 2 modes of coloring
//  1 . Using the Foreground color
//  2 . Using the Foreground color as background color and a white/black/gray color as foreground color
//      This is denoting or highlighting. Which effectively puts the foreground color as background color
// @param c Array of highlight colors to be used for corresponding error codes. If the corresponding code is not found, then text color BG is used
export method setColorsHC(log this, color[] c) =>
    sc=c.size()
    sq=this.colorDB.rows()
    if sq<sc
        trow=this.colorDB.row(0)
        for x=1 to sc-sq
            this.colorDB.add_row(0)
    if sq>sc and sc>0
        for x=1 to sq-sc
            this.colorDB.remove_row(0)
    for x=0 to (sc > 0 ? sc-1 : na)
        this.colorDB.set(x,2,c.get(x))
	this.intDB.set(21,this.colorDB.rows())

// @function Sets the highlight colors corresponding to debug codes
//  Index 0 of input array c is color is reserved for future use.
//  Index 1 of input array c is color for debug code 1.
//  Index 2 of input array c is color for debug code 2.
//  There are 2 modes of coloring
//  1 . Using the Foreground color
//  2 . Using the Foreground color as background color and a white/black/gray color as foreground color
//      This is denoting or highlighting. Which effectively puts the foreground color as background color
// @param c Array of background colors to be used for corresponding error codes. If the corresponding code is not found, then text color BG is used
export method setColorsBG(log this, color[] c) =>
    sc=c.size()
    sq=this.colorDB.rows()
    if sq<sc
        trow=this.colorDB.row(0)
        for x=1 to sc-sq
            this.colorDB.add_row(0)
    if sq>sc and sc>0
        for x=1 to sq-sc
            this.colorDB.remove_row(0)
    for x=0 to (sc > 0 ? sc-1 : na)
        this.colorDB.set(x,1,c.get(x))
	this.intDB.set(21,this.colorDB.rows())

// @function Resets the log level names used for corresponding error codes
// With prefix/suffix, the default Level name will be like => prefix + Code + suffix
// @param prefix    Prefix to use when resetting level names
// @param suffix    Suffix to use when resetting level names
export method resetLevelNames(log this, string prefix='Level ', string suffix='') =>
    this.levelDB.clear()
    this.levelDB:=array.new_string(this.colorDB.rows()<8 ? 8 : (this.colorDB.rows()))

    this.levelDB.set(0,''),this.levelDB.set(1,'TRACE'),this.levelDB.set(2,'DEBUG'),this.levelDB.set(3,'INFO'),this.levelDB.set(4,'WARNING'),this.levelDB.set(5,'ERROR'),this.levelDB.set(6,'CRITICAL'),this.levelDB.set(7,'FATAL')
    for x=8 to this.levelDB.size() > 8 ? (this.levelDB.size()-1) : na
        this.levelDB.set(x,prefix+str.tostring(x)+suffix)

// @function Resets the log level names used for corresponding error codes
//  Index 0 of input array names is reserved for future use.
//  Index 1 of input array names is name used for error code 1.
//  Index 2 of input array names is name used for error code 2.
// @param names Array of log level names be used for corresponding error codes. If the corresponding code is not found, then an empty string is used
export method setLevelNames(log this, string[] names) =>
    if names.size()>0
        this.levelDB.clear(),this.levelDB:=array.new_string(names.size()<8 ? 8 : names.size())

        this.levelDB.set(0,''),this.levelDB.set(1,''),this.levelDB.set(2,''),this.levelDB.set(3,''),this.levelDB.set(4,''),this.levelDB.set(5,''),this.levelDB.set(6,''),this.levelDB.set(7,'')
        for x=0 to  names.size()-1
            this.levelDB.set(x+1,names.get(x))

// @function Sets up data for logging. It consists of 6 separate message queues, and 3 additional queues for bar index, time and log level/error code. Do not directly alter the contents, as library could break.
// @param rows Log size, excluding the header/status. Default value is 50.
// @param isConsole Whether to init the log as console or logx. True= as console, False = as Logx. Default is true, hence init as console.
export method init(log this, int rows=50, bool isConsole=true) =>
    var bool  _init       = false
    if not _init and barstate.isfirst and barstate.isnew
	    this.IsConsole:=isConsole

		this.mqx:= matrix.new<string>	(rows,7,'')
    	this.hqx:= matrix.new<int>		(rows,6,-1)
    	this.cqx:= matrix.new<color>   	(rows,2)

		this.intDB			:= array.new<int>  		(30,0)
		this.boolDB			:= array.new<bool>  	(10,false)
		this.stringDB		:= array.new<string>  	(10,'')
    	this.colorDB		:= matrix.new<color>   	(37,3)
		this.levelDB		:= array.new<string> 	(37,'')
		this.intDB.set(21,this.colorDB.rows())

        this.resetColors()
        this.dateTimeFormat()
        this.resetLevelNames('ec <','>')

        this.HeaderColor	:= color.new(chart.fg_color,30)
        this.HeaderColorBG	:= color.new(#434651,80)

        this.StatusColor	:= color.new(#9598a1,50)
        this.StatusColorBG	:= color.new(#434651,70)

        this.TextColor		:= color.new(chart.fg_color,60)
        this.TextColorBG	:= chart.bg_color

        this.FrameColor		:= chart.bg_color
        this.CellBorderColor:= chart.bg_color

        this.stringDB.set(0,'                                                                                                                                                                                                                                                                                                                                                                                                                ')
        this.stringDB.set(1,'______________'),this.intDB.set(22,str.length(this.stringDB.get(0))),this.intDB.set(23,str.length(this.stringDB.get(1))),this.stringDB.set(3,'                            ')


        this.intDB.set(0,-1),this.intDB.set(1,math.max(0,rows)),this.intDB.set(2,-1),this.intDB.set(3,-1),this.intDB.set(4,-1),this.intDB.set(6,0),this.intDB.set(5,0),this.intDB.set(7,0)
        
        this.boolDB.set(1,this.IsConsole)
        this.boolDB.set(2,false)
        this.boolDB.set(3,false)

        this.stringDB.set(4,'dd.MMM.yy HH:mm')
        this.stringDB.set(5,position.top_right)
        
        this.intDB.set(24,80)
        this.intDB.set(25,40)
        this._resetLocations()
    
		_init:=true
        this.boolDB.set(0,_init)

// @function Logs messages to the queues , including, time/date, bar_index, and error code
// @param ec    Error/Code to be assigned.
// @param m1    Message needed to be logged to Q1, or for console.
// @param m2    Message needed to be logged to Q2. Not used/ignored when in console mode
// @param m3    Message needed to be logged to Q3. Not used/ignored when in console mode
// @param m4    Message needed to be logged to Q4. Not used/ignored when in console mode
// @param m5    Message needed to be logged to Q5. Not used/ignored when in console mode
// @param m6    Message needed to be logged to Q6. Not used/ignored when in console mode
// @param tv    Time to be used. Default value is time, which logs the start time of bar.
// @param log   Whether to log the message or not. Default is true.
export method log (log this, int ec=0, string m1=na, string m2=na, string m3=na, string m4=na, string m5=na, string m6=na, int tv=time, bool log=true) =>
    this.PageHistory:=this.PageHistory < 0 ? 0 : this.PageHistory
    if this.PageOnEveryBar and bar_index-this.intDB.get(3) > this.PageHistory
        if this.PageHistory==0
            this.clear()
        else if this.intDB.get(1)>0
            for x=0 to this.intDB.get(1)-1
                this.hqx.set(x,1,(bar_index-this.hqx.get(x,1)) > this.PageHistory ? -1 :  this.hqx.get(x,1))
                this.hqx.set(x,3,(bar_index-this.hqx.get(x,1)) > this.PageHistory ? 0  :  this.hqx.get(x,3))
                this.hqx.set(x,4,(bar_index-this.hqx.get(x,1)) > this.PageHistory ? 0  :  this.hqx.get(x,4))
        this.intDB.set(3,bar_index-this.PageHistory)
    if this.IsConsole!=this.boolDB.get(1)
        this._switchMode()

    int _txn    = this.intDB.get(0)
    int _qn     = ((_txn+1)%this.intDB.get(1))
    if log
        if (this.MarkNewBar and this.intDB.get(4)!=bar_index)
            _logMark='#.'+str.tostring(bar_index)+' '+this.stringDB.get(1)
            _markLen=str.length(_logMark)
            this.hqx.set(_qn,0, ec),this.hqx.set(_qn,1, bar_index),this.hqx.set(_qn,2, tv),this.hqx.set(_qn,3, _markLen),this.hqx.set(_qn,4, _markLen),this.hqx.set(_qn,5, this.intDB.get(6))
            this.mqx.set(_qn,0, _logMark),this.mqx.set(_qn,1, ''),this.mqx.set(_qn,2, ''),this.mqx.set(_qn,3, ''),this.mqx.set(_qn,4, ''),this.mqx.set(_qn,5, ''),this.mqx.set(_qn,6, '')
            this.cqx.set(_qn,0,(ec<1 or ec>this.colorDB.rows() ) ? this.TextColor   : this.ColorText ? this.colorDB.get(ec,0) : this.HighlightText ? this.colorDB.get(ec, 2) : this.TextColor)
            this.cqx.set(_qn,1,(ec<1 or ec>this.colorDB.rows() ) ? this.TextColorBG : this.ColorText ? this.colorDB.get(ec,1) : this.HighlightText ? this.colorDB.get(ec, 0) : this.TextColorBG)
            _txn+=1
            _qn := ((_txn+1)%this.intDB.get(1))

        _toLog  =this.IsConsole?'' : (( (this.TabSizeQ1>0 and ec>1 and ec<=7 and str.length(m1)>0)?str.substring(this.stringDB.get(3),0,(math.min(7,ec)-1)*math.min(4,this.TabSizeQ1)):'') + m1)
        _mq2    =this.IsConsole?'' : (( (this.TabSizeQ2>0 and ec>1 and ec<=7 and str.length(m2)>0)?str.substring(this.stringDB.get(3),0,(math.min(7,ec)-1)*math.min(4,this.TabSizeQ2)):'') + m2)
        _mq3    =this.IsConsole?'' : (( (this.TabSizeQ3>0 and ec>1 and ec<=7 and str.length(m3)>0)?str.substring(this.stringDB.get(3),0,(math.min(7,ec)-1)*math.min(4,this.TabSizeQ3)):'') + m3)
        _mq4    =this.IsConsole?'' : (( (this.TabSizeQ4>0 and ec>1 and ec<=7 and str.length(m4)>0)?str.substring(this.stringDB.get(3),0,(math.min(7,ec)-1)*math.min(4,this.TabSizeQ4)):'') + m4)
        _mq5    =this.IsConsole?'' : (( (this.TabSizeQ5>0 and ec>1 and ec<=7 and str.length(m5)>0)?str.substring(this.stringDB.get(3),0,(math.min(7,ec)-1)*math.min(4,this.TabSizeQ5)):'') + m5)
        _mq6    =this.IsConsole?m1 : (( (this.TabSizeQ6>0 and ec>1 and ec<=7 and str.length(m6)>0)?str.substring(this.stringDB.get(3),0,(math.min(7,ec)-1)*math.min(4,this.TabSizeQ6)):'') + m6)
        if this.IsConsole
            _totalLen=array.sum(this.hqx.col(3))
            _toLog:= (this.ShowBarIndex ? (str.tostring(bar_index)+', ') : '') + 
                     (this.ShowDateTime ? (str.format('{0,date,'+this.stringDB.get(4)+'}',tv)+'  ') : '') + 
                     ((this.TabSizeQ1>0 and ec>1 and ec<=7)? str.substring(this.stringDB.get(3),0,(math.min(7,ec)-1)*math.min(4,this.TabSizeQ1)):'') +
                     ( (this.PrefixLogLevel and ec>=0 and ec<=this.levelDB.size())? (this.levelDB.get(ec) +': ' ):'') +
                     m1
            int _mLen=str.length(_toLog)
            _overflow=(_totalLen+_mLen+this.intDB.get(1)-1)>4096?true:false
            _freeup=_overflow?_totalLen+_mLen+this.intDB.get(1)-1-4096:0
            _rowsout=1
            while _freeup > 0 and _rowsout <= this.intDB.get(1)
                _qn:=((_txn+_rowsout)%this.intDB.get(1))
                _freeup -= this.hqx.get(_qn,3)

                this.hqx.set(_qn,0,0),this.hqx.set(_qn,1,0),this.hqx.set(_qn,2,0),this.hqx.set(_qn,3,0),this.hqx.set(_qn,4,0)
                this.mqx.set(_qn,0,''),this.mqx.set(_qn,1,''),this.mqx.set(_qn,2,''),this.mqx.set(_qn,3,''),this.mqx.set(_qn,4,''),this.mqx.set(_qn,5,'')
                _rowsout+=1
            _qn := ((_txn+1)%this.intDB.get(1))
            _totalLen:=array.sum(this.hqx.col(3))
            this.intDB.set(5,  _totalLen)  
            this.stringDB.set(2, ' ='+str.tostring(4096-_totalLen)+(this.PageOnEveryBar ? ' ● ' : ' ')+(_overflow ? 'x' : '') )

        this.hqx.set(_qn,0, ec),this.hqx.set(_qn,1, bar_index),this.hqx.set(_qn,2, tv),this.hqx.set(_qn,3, str.length(_toLog)),this.hqx.set(_qn,4, str.length(_mq2)+str.length(_mq3)+str.length(_mq4)+str.length(_mq5)+str.length(_mq6)+this.hqx.get(_qn,3)),this.hqx.set(_qn,5, this.intDB.get(6))
		this.cqx.set(_qn,0,(ec<1 or ec>this.colorDB.rows() ) ? this.TextColor   : this.ColorText ? this.colorDB.get(ec,0) : this.HighlightText ? this.colorDB.get(ec, 2) : this.TextColor)
		this.cqx.set(_qn,1,(ec<1 or ec>this.colorDB.rows() ) ? this.TextColorBG : this.ColorText ? this.colorDB.get(ec,1) : this.HighlightText ? this.colorDB.get(ec, 0) : this.TextColorBG)
        this.mqx.set(_qn,0, _toLog),this.mqx.set(_qn,1, _mq2),this.mqx.set(_qn,2, _mq3),this.mqx.set(_qn,3, _mq4),this.mqx.set(_qn,4, _mq5),this.mqx.set(_qn,5, _mq6),this.mqx.set(_qn,6, (ec<0 or ec>this.levelDB.size())?'':this.levelDB.get(ec))
        this.intDB.set(4,bar_index)
        this.intDB.set(0, _txn+1)       
    else if not log and this.IsConsole
        this.stringDB.set(2, ' ='+str.tostring(4096-this.intDB.get(5)) + (this.PageOnEveryBar ? ' ● ' : ' ') + '◼︎'   )
    
// @function Logs messages to the queues , including, time/date, bar_index, and error code. All messages from previous bars are cleared
// @param ec    Error/Code to be assigned.
// @param m1    Message needed to be logged to Q1, or for console.
// @param m2    Message needed to be logged to Q2. Not used/ignored when in console mode
// @param m3    Message needed to be logged to Q3. Not used/ignored when in console mode
// @param m4    Message needed to be logged to Q4. Not used/ignored when in console mode
// @param m5    Message needed to be logged to Q5. Not used/ignored when in console mode
// @param m6    Message needed to be logged to Q6. Not used/ignored when in console mode
// @param tv    Time to be used. Default value is time, which logs the start time of bar.
// @param page  Whether to log the message or not. Default is true.
export method page (log this, int ec, string m1=na, string m2=na, string m3=na, string m4=na, string m5=na, string m6=na, int tv=time, bool page=true) => 
    if this.intDB.get(3)!=bar_index and page
        this.clear()
        this.intDB.set(3, bar_index )
    this.log(ec,m1,m2,m3,m4,m5,m6,tv,log=page)

// @function Set the messages to be on a new page, clearing messages from previous page.
// This is not dependent on PageHisotry option, as this method simply just clears all the messages, like turning old pages to a new page. 
export method turnPage (log this, bool turn=true) => 
    this.intDB.set(6, this.intDB.get(6)+1)
    for x=0 to (turn and this.intDB.get(1)>0)?(this.intDB.get(1)-1):na
        this.hqx.set(x,1, (this.intDB.get(6)-this.hqx.get(x,5)) > this.PageHistory ? -1 :  this.hqx.get(x,1))

_display (table tt, log this, string hhalign=text.align_left, string hvalign=text.align_top, string hsize=size.auto, string thalign=text.align_left, string tvalign=text.align_top, string tsize=size.auto) =>
    if barstate.islast
        int _txn    = this.intDB.get(0)
        int _qsize  = this.intDB.get(1)
        int _txnS   = (_txn<=-1 or _qsize<1) ? na : _txn<_qsize ? 0 : (_txn - _qsize + 1)
        int _txnLen = (_txn<=-1 or _qsize<1) ? na : _txn<_qsize ? _txn : _qsize - 1 

        int _qS  = this.MoveLogUp ? _txnS :_txn
        int _dx  = this.MoveLogUp ? 1 : -1
        int _qc = (_qS%_qsize)

        //From Left        
        int _hqcolBar       = this.intDB.get(14)-1+(this.ShowBarIndex?1:0)
        int _hqcolDateTime  = _hqcolBar+(this.ShowDateTime?1:0)
        int _hqcolEC        = _hqcolDateTime+(this.ShowLogLevels?1:0)
        this.ShowLogLevels and this.ReplaceWithErrorCodes
        int _mqcolQ1        = _hqcolEC+(this.ShowQ1?1:0)
        int _mqcolQ2        = _mqcolQ1+(this.ShowQ2?1:0)
        int _mqcolQ3        = _mqcolQ2+(this.ShowQ3?1:0)
        int _mqcolQ4        = _mqcolQ3+(this.ShowQ4?1:0)
        int _mqcolQ5        = _mqcolQ4+(this.ShowQ5?1:0)
        int _mqcolQ6        = _mqcolQ5+(this.ShowQ6?1:0)

        int _logx_headerRowDelta = this.intDB.get(13) + ((this.ShowHeader and this.HeaderAtTop) ? 1 : 0) + ((this.ShowStatusBar and not this.StatusBarAtBottom) ? 1 : 0)

        int _hrow   = 0
        int _totalFGc = this.colorDB.rows()
        int _ec = 0
        string _toShow=''
        if this.ShowHeader and not this.IsConsole
            _hrow := this.intDB.get(13) + (this.HeaderAtTop ?  (this.ShowStatusBar ? (this.StatusBarAtBottom ? 0 : 1) : 0) : (this.intDB.get(1) + (this.ShowStatusBar ? (this.StatusBarAtBottom ? 0 : 1 ) : 0)))
            if this.ShowBarIndex
                tt.cell(column=_hqcolBar,      row=_hrow, text=this.HeaderQbarIndex,     text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowDateTime
                tt.cell(column=_hqcolDateTime, row=_hrow, text=this.HeaderQdateTime,   text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowLogLevels and this.ReplaceWithErrorCodes
                tt.cell(column=_hqcolEC,       row=_hrow, text=this.HeaderQerrorCode,   text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowLogLevels and not this.ReplaceWithErrorCodes
                tt.cell(column=_hqcolEC,       row=_hrow, text=this.HeaderQlogLevel,   text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowQ1
                tt.cell(column=_mqcolQ1,       row=_hrow, text=this.HeaderQ1,           text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowQ2
                tt.cell(column=_mqcolQ2,       row=_hrow, text=this.HeaderQ2,           text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowQ3
                tt.cell(column=_mqcolQ3,       row=_hrow, text=this.HeaderQ3,           text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowQ4
                tt.cell(column=_mqcolQ4,       row=_hrow, text=this.HeaderQ4,           text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowQ5
                tt.cell(column=_mqcolQ5,       row=_hrow, text=this.HeaderQ5,           text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if this.ShowQ6
                tt.cell(column=_mqcolQ6,       row=_hrow, text=this.HeaderQ6,           text_halign=hhalign, text_valign=hvalign, text_size=hsize, text_color=this.HeaderColor , bgcolor=this.HeaderColorBG)
            if _mqcolQ6<this.intDB.get(16)
                tt.merge_cells(start_column=_mqcolQ6, start_row=_hrow, end_column=this.intDB.get(16), end_row=_hrow)

        for x=0 to _txnLen
            _qc := (_qS%_qsize)
            _qS += _dx
            _toShow+=(this.hqx.get(_qc,1)>=0?(this.mqx.get(_qc,0)+(x==_txnLen?'':'\n')):'')
            if this.hqx.get(_qc,1) >=0 and not this.IsConsole
                if this.ShowBarIndex
                    tt.cell(column=_hqcolBar,       row=x+_logx_headerRowDelta, text=str.tostring( this.hqx.get(_qc,1)),    text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowDateTime
                    tt.cell(column=_hqcolDateTime,  row=x+_logx_headerRowDelta, text=str.format('{0,date,'+this.stringDB.get(4)+'}',this.hqx.get(_qc,2)), bgcolor=this.cqx.get(_qc,1), text_color=this.cqx.get(_qc,0), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowLogLevels and this.ReplaceWithErrorCodes
                    tt.cell(column=_hqcolEC,        row=x+_logx_headerRowDelta, text=str.tostring(this.hqx.get(_qc,0)),      text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowLogLevels and not this.ReplaceWithErrorCodes
                    tt.cell(column=_hqcolEC,        row=x+_logx_headerRowDelta, text=((this.RestrictLevelsToKey7 and this.hqx.get(_qc,0) >=0 and this.hqx.get(_qc,0) <=7) or not this.RestrictLevelsToKey7)? this.mqx.get(_qc,6) : '' ,                 text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowQ1
                    tt.cell(column=_mqcolQ1,        row=x+_logx_headerRowDelta, text=this.mqx.get(_qc,0),                    text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowQ2
                    tt.cell(column=_mqcolQ2,        row=x+_logx_headerRowDelta, text=this.mqx.get(_qc,1),                    text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowQ3
                    tt.cell(column=_mqcolQ3,        row=x+_logx_headerRowDelta, text=this.mqx.get(_qc,2),                    text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowQ4
                    tt.cell(column=_mqcolQ4,        row=x+_logx_headerRowDelta, text=this.mqx.get(_qc,3),                    text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowQ5
                    tt.cell(column=_mqcolQ5,        row=x+_logx_headerRowDelta, text=this.mqx.get(_qc,4),                    text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.ShowQ6
                    tt.cell(column=_mqcolQ6,        row=x+_logx_headerRowDelta, text=this.mqx.get(_qc,5),                    text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign, text_valign=tvalign, text_size=tsize)
                if this.AutoMerge
                    if str.length(this.mqx.get(_qc,5))==0                     
                        if str.length(this.mqx.get(_qc,4))==0                 
                            if str.length(this.mqx.get(_qc,3))==0             
                                if str.length(this.mqx.get(_qc,2))==0         
                                    if str.length(this.mqx.get(_qc,1))==0
                                        tt.merge_cells( start_column = _mqcolQ1, end_column = this.intDB.get(16), start_row = x+_logx_headerRowDelta, end_row = x+_logx_headerRowDelta)
                                    else
                                        tt.merge_cells( start_column = _mqcolQ2, end_column = this.intDB.get(16), start_row = x+_logx_headerRowDelta, end_row = x+_logx_headerRowDelta)
                                else
                                    tt.merge_cells( start_column = _mqcolQ3, end_column = this.intDB.get(16), start_row = x+_logx_headerRowDelta, end_row = x+_logx_headerRowDelta)
                            else
                                tt.merge_cells( start_column = _mqcolQ4, end_column = this.intDB.get(16), start_row = x+_logx_headerRowDelta, end_row = x+_logx_headerRowDelta)
                        else
                            tt.merge_cells( start_column = _mqcolQ5, end_column = this.intDB.get(16), start_row = x+_logx_headerRowDelta, end_row = x+_logx_headerRowDelta)
                    else if _mqcolQ6<this.intDB.get(16)
                        tt.merge_cells( start_column = _mqcolQ6, end_column = this.intDB.get(16), start_row = x+_logx_headerRowDelta, end_row = x+_logx_headerRowDelta)
                else if _mqcolQ6<this.intDB.get(16)
                    tt.merge_cells(start_column=_mqcolQ6, start_row=x+_logx_headerRowDelta, end_column=this.intDB.get(16), end_row=x+_logx_headerRowDelta)
        if this.IsConsole
            int _crowUP = this.intDB.get(17) + (this.ShowStatusBar ? (this.StatusBarAtBottom? 0 : 1) : 0)
            int _crowDN = this.intDB.get(19) - (this.ShowStatusBar ? (this.StatusBarAtBottom? 1 : 0) : 0)

            tt.merge_cells(start_column=this.intDB.get(18), end_column=this.intDB.get(20),start_row=_crowUP,end_row  =_crowDN)
            tt.cell(column=this.intDB.get(18), row=_crowUP, text=_toShow, text_color=this.cqx.get(_qc,0), bgcolor=this.cqx.get(_qc,1), text_halign=thalign,text_valign=tvalign, text_size=tsize)

        if this.ShowStatusBar and not this.IsConsole
            int _srow = this.intDB.get(13) + (this.StatusBarAtBottom ? (this.intDB.get(1) + (this.ShowHeader?1:0)) : 0)
            int _endMerge=this.ShowMetaStatus ? ((this.ShowBarIndex?1:0) + (this.ShowDateTime?1:0) + (this.ShowLogLevels?1:0) ) : 0
            _minStatusLen=math.max(this.intDB.get(24),this.MinWidth)-(this.ShowMetaStatus?11:0)
            _status=this.Status + (str.length(this.Status)>_minStatusLen ? '': str.substring(this.stringDB.get(0),0,_minStatusLen-str.length(this.Status)))
            if _endMerge >1 
                tt.merge_cells(start_column=this.intDB.get(14),            start_row=_srow, end_column=this.intDB.get(14)+_endMerge-1, end_row=_srow)
                tt.merge_cells(start_column=this.intDB.get(14)+_endMerge,  start_row=_srow, end_column=this.intDB.get(16),             end_row=_srow)
                tt.cell(column=this.intDB.get(14),           row=_srow, text=(this.PageOnEveryBar?'●':'')+' @'+str.tostring(bar_index)+(this.StatusBarAtBottom?' ▲':' ▼'),  text_halign=text.align_left, text_valign=text.align_center, text_size=tsize, text_color=this.StatusColor , bgcolor=this.StatusColorBG)
                tt.cell(column=this.intDB.get(14)+_endMerge, row=_srow, text=_status,        			text_halign=text.align_left, text_valign=text.align_top, 	text_size=tsize, text_color=this.StatusColor , bgcolor=this.StatusColorBG)
            else
                tt.merge_cells(start_column=this.intDB.get(14)+(this.ShowMetaStatus?1:0), start_row=_srow, end_column=this.intDB.get(16), end_row=_srow)
                if this.ShowMetaStatus
                    tt.cell(column=this.intDB.get(14),   	row=_srow, 					text=(this.PageOnEveryBar?'●':'')+' @'+str.tostring(bar_index)+(this.StatusBarAtBottom?' ▲':' ▼'),  	text_halign=text.align_left, text_valign=text.align_center, text_size=tsize, text_color=this.StatusColor , bgcolor=this.StatusColorBG)
                tt.cell(column=this.intDB.get(14)+(this.ShowMetaStatus?1:0), row=_srow,  text=_status,        			text_halign=text.align_left, text_valign=text.align_top, 	text_size=tsize, text_color=this.StatusColor , bgcolor=this.StatusColorBG)
        if this.ShowStatusBar and this.IsConsole
            int _srow   = this.StatusBarAtBottom? this.intDB.get(19) : this.intDB.get(17)
            _hideECmeta= (this.hqx.get(_qc,0)<=0 or (not this.ShowLogLevels) or (this.ShowLogLevels and this.hqx.get(_qc,0)>this.levelDB.size()) or (this.ShowLogLevels and this.RestrictLevelsToKey7 and this.hqx.get(_qc,0)>7))            
            _minStatusLen=math.max(this.intDB.get(25),this.MinWidth)-(this.ShowMetaStatus?20:0)

            if _hideECmeta
                _status=this.Status + (str.length(this.Status)>_minStatusLen ? '': str.substring(this.stringDB.get(0),0,_minStatusLen-str.length(this.Status)))
                tt.merge_cells(start_column=this.intDB.get(18),start_row=_srow, end_column=this.intDB.get(20)-(this.ShowMetaStatus?1:0),end_row=_srow)
                if this.ShowMetaStatus
                    tt.cell(column=this.intDB.get(20), row=_srow, text=(this.StatusBarAtBottom?'▲':'▼')+' @'+str.tostring(bar_index)+this.stringDB.get(2) ,  text_halign=text.align_right, text_valign=text.align_top,   text_size=tsize, text_color=this.StatusColor, bgcolor=this.StatusColorBG)
                tt.cell(column=this.intDB.get(18), row=_srow, text=_status ,  text_halign=text.align_left, text_valign=text.align_top,   text_size=tsize, text_color=this.StatusColor, bgcolor=this.StatusColorBG)
            else
                string _ecText    = this.levelDB.get(this.hqx.get(_qc,0))
                color _ecColor    = this.hqx.get(_qc,0)<=this.colorDB.rows() ? this.colorDB.get(this.hqx.get(_qc,0),0) : this.StatusColorBG
                color _ecColorHC  = this.hqx.get(_qc,0)<=this.colorDB.rows() ? this.colorDB.get(this.hqx.get(_qc,0),2) : this.StatusColor
                _minStatusLen-=str.length(_ecText)

                _status=this.Status + (str.length(this.Status)>_minStatusLen ? '': str.substring(this.stringDB.get(0),0,_minStatusLen-str.length(this.Status)))

                tt.cell(column=this.intDB.get(18),    row=_srow, text=_ecText,                                    text_halign=text.align_center, text_valign=text.align_top, text_size=tsize, text_color=_ecColorHC,         bgcolor=_ecColor)
                tt.merge_cells(start_column=this.intDB.get(18)+1,start_row=_srow, end_column=this.intDB.get(20)-(this.ShowMetaStatus?1:0),end_row=_srow)
                if this.ShowMetaStatus
                    tt.cell(column=this.intDB.get(20), row=_srow, text=(this.StatusBarAtBottom?'▲':'▼')+' @'+str.tostring(bar_index)+this.stringDB.get(2) ,  text_halign=text.align_right, text_valign=text.align_top,   text_size=tsize, text_color=this.StatusColor, bgcolor=this.StatusColorBG)
                tt.cell(column=this.intDB.get(18)+1,  row=_srow, text=_status,    text_halign=text.align_left, text_valign=text.align_top,   text_size=tsize, text_color=this.StatusColor, bgcolor=this.StatusColorBG)

// @function Display Message Q, Index Q, Time Q, and Log Levels
//  All options for postion/alignment accept TV values, such as position.bottom_right, text.align_left, size.auto etc.
// @param  position         Position of the table used for displaying the messages. Default is Bottom Right.
// @param  hhalign          Horizontal alignment of Header columns 
// @param  hvalign          Vertical alignment of Header columns
// @param  hsize            Size of Header text Options
// @param  thalign          Horizontal alignment of all messages
// @param  tvalign          Vertical alignment of all messages
// @param  tsize            Size of text across the table
// @param  show             Whether to display the logs or not. Default is true.
// @param  attach           Console that has been attached via attach method. If na then console will not be shown
export method show (log this, string position=position.bottom_right, string hhalign=text.align_left, string hvalign=text.align_top, string hsize=size.auto, string thalign=text.align_left, string tvalign=text.align_top, string tsize=size.auto, bool show=true, log attach=na) =>
    this.stringDB.set(5,position)
    if barstate.islast
        if this.IsConsole!=this.boolDB.get(1)
            this._switchMode()
    if barstate.islast and not show and not na(this.tbx)
        this.tbx.delete()
    if barstate.islast and show and not this.boolDB.get(3)
        this._resetLocations()
    if barstate.islast and show and not this.boolDB.get(2)
		this.tbx := table.new(frame_color=this.FrameColor,border_color=this.CellBorderColor,position=position,columns=19,rows=5+this.intDB.get(1),frame_width=this.FrameSize,border_width=this.CellBorderSize)

        if not na(attach)
            if attach.intDB.get(2)!=-1
                attach.resize(this.intDB.get(1))

            if this.boolDB.get(3) and not na(this.SeparatorColor)
                this.tbx.merge_cells(start_column=this.intDB.get(11)==1?this.intDB.get(14):this.intDB.get(11)==2?this.intDB.get(16)+1:this.intDB.get(11)==4?this.intDB.get(14)-1:this.intDB.get(14),start_row=this.intDB.get(11)==1?this.intDB.get(13)-1:this.intDB.get(11)==2?this.intDB.get(13):this.intDB.get(11)==4?this.intDB.get(13):this.intDB.get(15)+1,end_column=this.intDB.get(11)==1?this.intDB.get(16):this.intDB.get(11)==2?this.intDB.get(16)+1:this.intDB.get(11)==4?this.intDB.get(14)-1:this.intDB.get(16),end_row=this.intDB.get(11)==1?this.intDB.get(13)-1:this.intDB.get(11)==2?this.intDB.get(15):this.intDB.get(11)==4?this.intDB.get(15):this.intDB.get(15)+1)
                this.tbx.cell(text=' ',bgcolor=this.SeparatorColor, text_color=this.SeparatorColor,row=this.intDB.get(11)==1?this.intDB.get(13)-1:this.intDB.get(11)==2?this.intDB.get(13)  :this.intDB.get(11)==4?this.intDB.get(13)  :this.intDB.get(15)+1,column=this.intDB.get(11)==1?this.intDB.get(14):this.intDB.get(11)==2?this.intDB.get(16)+1:this.intDB.get(11)==4?this.intDB.get(14)-1:this.intDB.get(14))
            if attach.boolDB.get(2)
                _display(this.tbx, attach, hhalign=hhalign, hvalign=hvalign, hsize=hsize, thalign=thalign, tvalign=tvalign, tsize=tsize)

        _display(this.tbx, this, hhalign=hhalign, hvalign=hvalign, hsize=hsize, thalign=thalign, tvalign=tvalign, tsize=tsize)

// @function Attaches a console to Logx, or moves already attached console around Logx
// All options for position/alignment accept TV values, such as position.bottom_right, text.align_left, size.auto etc.
// @param attach        Console object that has been previously attached.
// @param position      Position of Console in relation to Logx. Can be Top, Right, Bottom, Left. Default is Bottom. If unknown specified then defaults to bottom.
export method attach(log this,log attach, string position='anywhere') =>
    if not this.boolDB.get(3) 
        if (this.intDB.get(11)%2==0)
            attach.intDB.set(2,attach.intDB.get(1))
            attach.resize(this.intDB.get(1))
        else
            attach.intDB.set(2,-1)
    if this.boolDB.get(3)
        if _consolePosition(position)%2!=this.intDB.get(11)%2
            if attach.intDB.get(2)==-1 
                attach.intDB.set(2,attach.intDB.get(1))
                attach.resize(this.intDB.get(1))
            else
                attach.resize(attach.intDB.get(2))
                attach.intDB.set(2,-1)
    attach.tbx.delete()

    this.intDB.set(11,_consolePosition(position))

    this.IsConsole:=false,attach.IsConsole:=true
    this.boolDB.set(2,false),this.boolDB.set(3,true)
    attach.boolDB.set(2,true),attach.boolDB.set(3,false)

    _ate=not(na(this.SeparatorColor))
    int _czeroRow = this.intDB.get(11)==3 ? (this.intDB.get(1)+(this.ShowHeader?1:0)+(this.ShowStatusBar?1:0) + (_ate?1:0) ) : 0
    int _czeroCol = this.intDB.get(11)==2 ? (9 + (_ate?1:0)) : 0
    int _clastCol = _czeroCol + 8
    int _clastRow = _czeroRow + (this.intDB.get(11)%2==1?1:this.intDB.get(1)-1+(this.ShowHeader?1:0)+(this.ShowStatusBar?1:0))


    int _lzeroRow = this.intDB.get(11)==1 ? (_clastRow+1+(_ate?1:0)) : 0
    int _lzeroCol = this.intDB.get(11)==4? (_clastCol+1+(_ate?1:0)): 0
    int _llastRow = _lzeroRow + this.intDB.get(1)-1+(this.ShowHeader?1:0)+(this.ShowStatusBar?1:0)
    int _llastCol = _lzeroCol + 8

    this.intDB.set(14,_lzeroCol),this.intDB.set(16,_llastCol),this.intDB.set(13,_lzeroRow),this.intDB.set(15,_llastRow)
    attach.intDB.set(18,_czeroCol),attach.intDB.set(20,_clastCol),attach.intDB.set(17,_czeroRow),attach.intDB.set(19,_clastRow)
    attach.intDB.set(14,_lzeroCol),attach.intDB.set(16,_llastCol),attach.intDB.set(13,_lzeroRow),attach.intDB.set(15,_llastRow)

// @function Detaches the attached console from Logx.
// All options for position/alignment accept TV values, such as position.bottom_right, text.align_left, size.auto etc.
// @param attach         Console object that has been previously attached.
export method detach(log this,log attach) =>
    this.boolDB.set(2,false),this.boolDB.set(3,false)
    attach.boolDB.set(2,false),attach.boolDB.set(3,false)
    this._resetLocations(),attach._resetLocations()

    if attach.intDB.get(2)!=-1
        attach.resize(attach.intDB.get(2))

//  libhs.log.alpha.57  dev.143