X-Mas Tree
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © HeWhoMustNotBeNamed

//   __    __            __       __  __                  __       __                        __      __    __              __      _______             __    __                                          __ 
//  /  |  /  |          /  |  _  /  |/  |                /  \     /  |                      /  |    /  \  /  |            /  |    /       \           /  \  /  |                                        /  |
//  $$ |  $$ |  ______  $$ | / \ $$ |$$ |____    ______  $$  \   /$$ | __    __   _______  _$$ |_   $$  \ $$ |  ______   _$$ |_   $$$$$$$  |  ______  $$  \ $$ |  ______   _____  ____    ______    ____$$ |
//  $$ |__$$ | /      \ $$ |/$  \$$ |$$      \  /      \ $$$  \ /$$$ |/  |  /  | /       |/ $$   |  $$$  \$$ | /      \ / $$   |  $$ |__$$ | /      \ $$$  \$$ | /      \ /     \/    \  /      \  /    $$ |
//  $$    $$ |/$$$$$$  |$$ /$$$  $$ |$$$$$$$  |/$$$$$$  |$$$$  /$$$$ |$$ |  $$ |/$$$$$$$/ $$$$$$/   $$$$  $$ |/$$$$$$  |$$$$$$/   $$    $$< /$$$$$$  |$$$$  $$ | $$$$$$  |$$$$$$ $$$$  |/$$$$$$  |/$$$$$$$ |
//  $$$$$$$$ |$$    $$ |$$ $$/$$ $$ |$$ |  $$ |$$ |  $$ |$$ $$ $$/$$ |$$ |  $$ |$$      \   $$ | __ $$ $$ $$ |$$ |  $$ |  $$ | __ $$$$$$$  |$$    $$ |$$ $$ $$ | /    $$ |$$ | $$ | $$ |$$    $$ |$$ |  $$ |
//  $$ |  $$ |$$$$$$$$/ $$$$/  $$$$ |$$ |  $$ |$$ \__$$ |$$ |$$$/ $$ |$$ \__$$ | $$$$$$  |  $$ |/  |$$ |$$$$ |$$ \__$$ |  $$ |/  |$$ |__$$ |$$$$$$$$/ $$ |$$$$ |/$$$$$$$ |$$ | $$ | $$ |$$$$$$$$/ $$ \__$$ |
//  $$ |  $$ |$$       |$$$/    $$$ |$$ |  $$ |$$    $$/ $$ | $/  $$ |$$    $$/ /     $$/   $$  $$/ $$ | $$$ |$$    $$/   $$  $$/ $$    $$/ $$       |$$ | $$$ |$$    $$ |$$ | $$ | $$ |$$       |$$    $$ |
//  $$/   $$/  $$$$$$$/ $$/      $$/ $$/   $$/  $$$$$$/  $$/      $$/  $$$$$$/  $$$$$$$/     $$$$/  $$/   $$/  $$$$$$/     $$$$/  $$$$$$$/   $$$$$$$/ $$/   $$/  $$$$$$$/ $$/  $$/  $$/  $$$$$$$/  $$$$$$$/ 
//                                                                                                                                                                                                          
//                                                                                                                                                                                                          
//
//@version=5
indicator("X-Mas Tree", overlay=true, max_lines_count=500, max_labels_count=500, max_bars_back=5000)

showZigZag1 = input.bool(true, title='L1 Length', group='Zigzag', inline='main')
zigzag1Length = input.int(5, step=1, minval=3, title='', group='Zigzag', inline='main')
zigzag1Color = input.color(color.rgb(30, 86, 49, 0), title='', group='Zigzag', inline='main')

showZigZag2 = input.bool(true, title='L2 Length', group='Zigzag', inline='sub1')
zigzag2Length = input.int(3, step=1, minval=2, title='', group='Zigzag', inline='sub1')
zigzag2Color = input.color(color.rgb(164, 222, 2, 0), title='', group='Zigzag', inline='sub1')

showZigZag3 = input.bool(true, title='L3 Length', group='Zigzag', inline='sub2')
zigzag3Length = input.int(3, step=1, minval=2, title='', group='Zigzag', inline='sub2')
zigzag3Color = input.color(color.rgb(172, 223, 135, 0), title='', group='Zigzag', inline='sub2')

showZigZag4 = input.bool(true, title='L4 Length', group='Zigzag', inline='sub3')
zigzag4Length = input.int(3, step=1, minval=2, title='', group='Zigzag', inline='sub3')
zigzag4Color = input.color(color.rgb(104, 187, 89, 0), title='', group='Zigzag', inline='sub3')

greetingSize = input.string(size.large, "Greeting Size", options=[size.huge, size.large, size.normal, size.small, size.tiny, size.auto])

tbl = table.new(position.middle_center, 100, 100)

//****************************************************** Make Snow *****************************************************/
varip counter = 1
counter := counter+1

var colPos = array.new_int()
var snowStyle = array.new_int()
if(array.size(colPos) == 0)
    for i=0 to 99
        random = int(math.random(0, 100))
        style = int(math.random(0,3))
        array.push(colPos, random)
        array.push(snowStyle, style)

snowflakeArray = array.from('❆', '❄', '❅')

if(barstate.islast)
    for i=0 to 99
        for j=0 to 99
            val = ((counter+array.get(colPos, i)+(100-j))%10)
            if val == 0
                table.cell(tbl, i, j, array.get(snowflakeArray, array.get(snowStyle, i)), text_color=color.white)


//********************************************** Draw tree - using zigzags *********************************************/
useAlternativeSource = false
source = close

max_array_size = 50
useZigZagChain = false
waitForConfirmation = false

var zigzagpivots1 = array.new_float(0)
var zigzagpivotbars1 = array.new_int(0)
var zigzagpivotdirs1 = array.new_int(0)

var zigzagpivots2 = array.new_float(0)
var zigzagpivotbars2 = array.new_int(0)
var zigzagpivotdirs2 = array.new_int(0)

var zigzagpivots3 = array.new_float(0)
var zigzagpivotbars3 = array.new_int(0)
var zigzagpivotdirs3 = array.new_int(0)

var zigzagpivots4 = array.new_float(0)
var zigzagpivotbars4 = array.new_int(0)
var zigzagpivotdirs4 = array.new_int(0)

var zigzag1lines = array.new_line(0)
var zigzag2lines = array.new_line(0)
var zigzag3lines = array.new_line(0)
var zigzag4lines = array.new_line(0)

add_to_array(arr, val, maxItems) =>
    array.unshift(arr, val)
    if array.size(arr) > maxItems
        array.pop(arr)

pivots(length) =>
    highsource = useAlternativeSource ? source : high
    lowsource = useAlternativeSource ? source : low
    float phigh = ta.highestbars(highsource, length) == 0 ? highsource : na
    float plow = ta.lowestbars(lowsource, length) == 0 ? lowsource : na
    [phigh, plow, bar_index, bar_index]

outerpivots(startIndex, length, zigzagpivots, zigzagpivotbars) =>
    zigzagminimal = array.slice(zigzagpivots, startIndex, length - 1)
    lastPivot = array.get(zigzagpivots, startIndex)
    lastPivotBar = array.get(zigzagpivotbars, startIndex)
    highestPivot = array.max(zigzagminimal)
    lowestPivot = array.min(zigzagminimal)
    float phigh = lastPivot == highestPivot ? lastPivot : na
    float plow = lastPivot == lowestPivot ? lastPivot : na
    int phighbar = lastPivot == highestPivot ? lastPivotBar : na
    int plowbar = lastPivot == lowestPivot ? lastPivotBar : na
    [phigh, plow, phighbar, plowbar]

addnewpivot(zigzagpivots, zigzagpivotbars, zigzagpivotdirs, value, bar, dir) =>
    newDir = dir
    if array.size(zigzagpivots) >= 2
        LastPoint = array.get(zigzagpivots, 1)
        newDir := dir * value > dir * LastPoint ? dir * 2 : dir
        newDir
    add_to_array(zigzagpivots, value, max_array_size)
    add_to_array(zigzagpivotbars, bar, max_array_size)
    add_to_array(zigzagpivotdirs, newDir, max_array_size)

zigzagcore(phigh, plow, phighbar, plowbar, zigzagpivots, zigzagpivotbars, zigzagpivotdirs) =>
    pDir = 1
    newZG = false
    doubleZG = phigh and plow
    if array.size(zigzagpivots) >= 1
        pDir := array.get(zigzagpivotdirs, 0)
        pDir := pDir % 2 == 0 ? pDir / 2 : pDir
        pDir

    if (pDir == 1 and phigh or pDir == -1 and plow) and array.size(zigzagpivots) >= 1
        pivot = array.shift(zigzagpivots)
        pivotbar = array.shift(zigzagpivotbars)
        pivotdir = array.shift(zigzagpivotdirs)
        value = pDir == 1 ? phigh : plow
        bar = pDir == 1 ? phighbar : plowbar

        useNewValues = value * pivotdir > pivot * pivotdir
        value := useNewValues ? value : pivot
        bar := useNewValues ? bar : pivotbar
        newZG := newZG or useNewValues
        addnewpivot(zigzagpivots, zigzagpivotbars, zigzagpivotdirs, value, bar, pDir)

    if pDir == 1 and plow or pDir == -1 and phigh
        value = pDir == 1 ? plow : phigh
        bar = pDir == 1 ? plowbar : phighbar
        dir = pDir == 1 ? -1 : 1
        newZG := true
        addnewpivot(zigzagpivots, zigzagpivotbars, zigzagpivotdirs, value, bar, dir)

    [newZG, doubleZG]

zigzag(length, zigzagpivots, zigzagpivotbars, zigzagpivotdirs) =>
    [phigh, plow, phighbar, plowbar] = pivots(length)
    zigzagcore(phigh, plow, phighbar, plowbar, zigzagpivots, zigzagpivotbars, zigzagpivotdirs)

outerzigzag(outerzigzagLength, zigzagpivots, zigzagpivotbars, outerzigzagpivots, outerzigzagpivotbars, outerzigzagpivotdirs, ignore) =>
    newOuterZG = false
    newDoubleOuterZG = false
    startIndex = waitForConfirmation ? 1 : 0
    if array.size(zigzagpivots) >= outerzigzagLength * 2 + startIndex
        [phigh, plow, phighbar, plowbar] = outerpivots(startIndex, outerzigzagLength * 2 + startIndex, zigzagpivots, zigzagpivotbars)
        [newZG, doubleZG] = zigzagcore(phigh, plow, phighbar, plowbar, outerzigzagpivots, outerzigzagpivotbars, outerzigzagpivotdirs)
        newOuterZG := newZG
        newDoubleOuterZG := doubleZG
        newDoubleOuterZG
    [newOuterZG, newDoubleOuterZG]

draw_zg_line(idx1, idx2, zigzaglines, zigzagpivots, zigzagpivotbars, zigzagcolor, zigzagwidth, zigzagstyle) =>
    if array.size(zigzagpivots) > 2
        y1 = array.get(zigzagpivots, idx1)
        y2 = array.get(zigzagpivots, idx2)
        x1 = array.get(zigzagpivotbars, idx1)
        x2 = array.get(zigzagpivotbars, idx2)
        zline = line.new(x1=x1, y1=y1, x2=x2, y2=y2, color=zigzagcolor, width=zigzagwidth, style=zigzagstyle)
        if array.size(zigzaglines) >= 1
            lastLine = array.get(zigzaglines, 0)
            if x2 == line.get_x2(lastLine) and y2 == line.get_y2(lastLine)
                line.delete(lastLine)
        array.unshift(zigzaglines, zline)
        if array.size(zigzaglines) > max_array_size * 10
            line.delete(array.pop(zigzaglines))

draw_zigzag(doubleZG, zigzaglines, zigzagpivots, zigzagpivotbars, zigzagcolor, zigzagwidth, zigzagstyle) =>
    if doubleZG
        draw_zg_line(1, 2, zigzaglines, zigzagpivots, zigzagpivotbars, zigzagcolor, zigzagwidth, zigzagstyle)

    if array.size(zigzagpivots) >= 2
        draw_zg_line(0, 1, zigzaglines, zigzagpivots, zigzagpivotbars, zigzagcolor, zigzagwidth, zigzagstyle)

getCellColorByDirection(dir) =>
    dir == 2 ? color.green : dir == 1 ? color.lime : dir == -1 ? color.orange : dir == -2 ? color.red : color.silver

[level1ZG, doublel1ZG] = zigzag(zigzag1Length, zigzagpivots1, zigzagpivotbars1, zigzagpivotdirs1)
[level2ZG, doublel2ZG] = outerzigzag(zigzag2Length, zigzagpivots1, zigzagpivotbars1, zigzagpivots2, zigzagpivotbars2, zigzagpivotdirs2, level1ZG)
[level3ZG, doublel3ZG] = outerzigzag(useZigZagChain ? zigzag3Length : zigzag2Length + zigzag3Length, useZigZagChain ? zigzagpivots2 : zigzagpivots1, useZigZagChain ? zigzagpivotbars2 : zigzagpivotbars1, zigzagpivots3, zigzagpivotbars3, zigzagpivotdirs3, useZigZagChain ? level2ZG : level1ZG)
[level4ZG, doublel4ZG] = outerzigzag(useZigZagChain ? zigzag4Length : zigzag2Length + zigzag3Length + zigzag4Length, useZigZagChain ? zigzagpivots3 : zigzagpivots1, useZigZagChain ? zigzagpivotbars3 : zigzagpivotbars1, zigzagpivots4, zigzagpivotbars4, zigzagpivotdirs4, useZigZagChain ? level2ZG : level1ZG)
if level1ZG and showZigZag1
    draw_zigzag(doublel1ZG, zigzag1lines, zigzagpivots1, zigzagpivotbars1, zigzag1Color, 1, line.style_solid)

if level2ZG and showZigZag2
    draw_zigzag(doublel2ZG, zigzag2lines, zigzagpivots2, zigzagpivotbars2, zigzag2Color, 1, line.style_solid)

if level1ZG and showZigZag3
    draw_zigzag(doublel3ZG, zigzag3lines, zigzagpivots3, zigzagpivotbars3, zigzag3Color, 1, line.style_solid)

if level2ZG and showZigZag4
    draw_zigzag(doublel4ZG, zigzag4lines, zigzagpivots4, zigzagpivotbars4, zigzag4Color, 1, line.style_solid)

//****************************************************** Decorate *****************************************************/
var decorationsUp = array.from("🦋", "🪲", "🐝", "🐞")
var staticDecorations = array.from("🎗", "💠", "🔶", "🎅", "⛄","🎄"),
var decorationsDown = array.from("🔴", "🔵", "🟠", "🟡", "🟢", "🟣")
barcolor(color.green)
var labelArray = array.new_label()
for lbl in labelArray
    label.delete(lbl)
    
if barstate.islast
    for i=0 to array.size(zigzagpivots4)-1
        z4Price = array.get(zigzagpivots4, i)
        z4Bar = array.get(zigzagpivotbars4, i)
        z4Dir = array.get(zigzagpivotdirs4, i)

        if(z4Dir > 0)
            uh = label.new(z4Bar, z4Price, text = "🌟", xloc = xloc.bar_index, yloc=yloc.price, style=label.style_none, size=size.huge)
            array.push(labelArray, uh)
        else
            ul = label.new(z4Bar, z4Price-4*ta.tr, text = "🔔", xloc = xloc.bar_index, yloc=yloc.price, style=label.style_none, size=size.huge)
            array.push(labelArray, ul)

    for i=0 to array.size(zigzagpivots2)-1
        z2Price = array.get(zigzagpivots2, i)
        z2Bar = array.get(zigzagpivotbars2, i)
        z2Dir = array.get(zigzagpivotdirs2, i)

        if(not array.includes(zigzagpivotbars4, z2Bar) and not array.includes(zigzagpivotbars3, z2Bar))
            if(z2Dir > 0)
                decorationsIndex = int(math.random(0,array.size(decorationsUp)))
                uh = label.new(z2Bar, z2Price, text = array.get(decorationsUp,decorationsIndex) , xloc = xloc.bar_index, yloc=yloc.price, style=label.style_none, size=size.huge)
                array.push(labelArray, uh)
            else
                decorationsIndex = int(math.random(0,array.size(decorationsDown)))
                ul = label.new(z2Bar, z2Price-4*ta.tr, text = array.get(decorationsDown,decorationsIndex), xloc = xloc.bar_index, yloc=yloc.price, style=label.style_none, size=size.large)
                array.push(labelArray, ul)
            
    for i=0 to array.size(zigzagpivots3)-1
        z3Price = array.get(zigzagpivots3, i)
        z3Bar = array.get(zigzagpivotbars3, i)
        z3Dir = array.get(zigzagpivotdirs3, i)
        if(not array.includes(zigzagpivotbars4, z3Bar))
            if(z3Dir > 0)
                uh = label.new(z3Bar, z3Price, text = array.get(staticDecorations, i%array.size(staticDecorations)), xloc = xloc.bar_index, yloc=yloc.price, style=label.style_none, size=size.large)
                array.push(labelArray, uh)
            else
                uh = label.new(z3Bar, z3Price-2*ta.tr, text = array.get(staticDecorations, i%array.size(staticDecorations)), xloc = xloc.bar_index, yloc=yloc.price, style=label.style_none, size=size.large)
                array.push(labelArray, uh)
        

//****************************************************** Greetings *****************************************************/
tblGreeting = table.new(position.bottom_center, 20, 5)
table.cell(tblGreeting, 2,0, "🄼", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 3,0, "🄴", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 4,0, "🅁", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 5,0, "🅁", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 6,0, "🅈", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 7,0, " ", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 8,0, "🅇", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 9,0, "🄼", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 10,0, "🄰", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 11,0, "🅂", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))

table.cell(tblGreeting, 6,1, "🄰", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 7,1, "🄽", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 8,1, "🄳", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))

table.cell(tblGreeting, 0,2, "🄷", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 1,2, "🄰", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 2,2, "🄿", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 3,2, "🄿", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 4,2, "🅈", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 5,2, " ", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 6,2, "🄽", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 7,2, "🄴", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 8,2, "🅆", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 9,2, " ", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 10,2, "🅈", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 11,2, "🄴", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 12,2, "🄰", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))
table.cell(tblGreeting, 13,2, "🅁", text_color=color.red, text_size=greetingSize, bgcolor=color.new(color.green, 80))