Gann Square of 9
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ThiagoSchmitz

//@version=5
indicator("Gann Square of 9 by Thiago schmitz", "Gann Square of 9", overlay=true, max_lines_count = 500, max_labels_count = 500)

type tableCells 
    int column
    int row


// ╔────────────────────────────────. ■ .──────────────────────────────────────▼
// ║ ⇌ • Inputs • ⇋
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈□┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
var int levels = input.int(3, "Levels", group="calculation")
var float starting = input.float(20000.0, "Starting Number", group="calculation")
var float increase = input.float(500.0, "Increasing Number", group="calculation")
var string tablePosition = input.string("Top Right", "Square Position", ["Top Right", "Middle Right", "Bottom Right", "Top Left", "Middle Left", "Bottom Left"], group="table")
var float width = input.float(3.0, "Cell Width %", group="table")
var float height = input.float(4.0, "Cell Height %", group="table")
var int frameWidth = input.int(2, "Frame Width", group="table")
var color backgroundColor = input.color(color.new(color.black, 70), "Background Color", inline="color", group="table")
var color frameColor = input.color(color.new(color.white, 40), "Frame Color", inline="color", group="table")
var string tableTextSize = input.string(size.small, "Font Size", inline="fontcolor", group="table", options=[size.auto, size.tiny, size.small, size.normal, size.large, size.huge])
var color tableTextColor = input.color(color.new(color.white, 20), "Text Color", inline="fontcolor", group="table")
var bool showClosestValue = input.bool(true, "Show the closest value with the actual price", group="table")
var bool showDiagonal = input.bool(true, "Diagonals", group="diagonals", inline="diagonals")
var bool showDiagonalLevels = input.bool(true, "Levels", group="diagonals", inline="diagonals")
var bool showDiagonalLabels = input.bool(true, "Labels", group="diagonals", inline="diagonals")
var int diagonalLevelsShift = input.int(100, "Diagonal Levels Shift", minval = 1, maxval = 500, group="diagonals")
var int diagonalLabelShift = input.int(30, "Diagonal Levels Label Shift", minval = 1, maxval = 200, group="diagonals")
var color diagonalColor = input.color(color.new(color.white, 80), "Diagonal color", inline="diagonal", group="diagonals")
var color linesColor = input.color(color.new(color.white, 40), "Diagonal Levels Color", inline="diagonal", group="diagonals")
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
// ║ ⇌ • Inputs • ⇋
// ╚─────────────────────────────────...───────────────────────────────────────▲

// ╔────────────────────────────────. ■ .──────────────────────────────────────▼
// ║ ⇌ • Variables • ⇋
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈□┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
var string position = switch tablePosition
    "Top Right" => position.top_right
    "Middle Right" => position.middle_right
    "Bottom Right" => position.bottom_right
    "Top Left" => position.top_left
    "Middle Left" => position.middle_left
    => position.bottom_left
var int size = levels * 2 + 1
var table tab = table.new(position, size, size, backgroundColor, frameColor, frameWidth, frameColor, frameWidth)
var tableCells[] coordinates = array.new<tableCells>()
var float[] values = array.new_float()
var float[] diagonalValues = array.new_float()
var line[] lines = array.new_line()
var label[] labels = array.new_label()
var int count = 1
var int layer = 2
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
// ║ ⇌ • Variables • ⇋
// ╚─────────────────────────────────...───────────────────────────────────────▲

// ╔────────────────────────────────. ■ .──────────────────────────────────────▼
// ║ ⇌ • Functions • ⇋
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈□┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
createCell(int c, int r, float v, color bgcolor=na) => 
       table.cell(tab, c, r, str.tostring(v), width, height, tableTextColor, text_size=tableTextSize, bgcolor=bgcolor)

isDiagonal(int c, int l) => c % l == 1

getDiagonalColor() => showDiagonal ? diagonalColor : na

createCellWithValueAndColor(int column, int row, float value, color col) =>
    createCell(column, row, value, col)
    pivot = tableCells.new(column, row)
    array.push(coordinates, pivot)
    array.push(values, value)
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
// ║ ⇌ • Functions • ⇋
// ╚─────────────────────────────────...───────────────────────────────────────▲

// ╔────────────────────────────────. ■ .──────────────────────────────────────▼
// ║ ⇌ • Build Square • ⇋
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈□┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
if barstate.isfirst
    int currentColumn = levels
    int currentRow = levels
    int doubleLevels = levels * 2
    float startingValue = starting

    createCell(currentColumn, currentRow, starting, diagonalColor)
    array.push(diagonalValues, startingValue)

    for i = 1 to doubleLevels by 1
        int sign = i % 2 == 1 ? -1 : 1
        int limit = i == 1 ? i / 2 : i

        for j = 0 to i * 2 - 1 by 1
            startingValue := startingValue + increase
            count := count + 1
            layer := math.sqrt(count) == layer + 1 ? layer + 2 : layer
            isDiagonal = isDiagonal(count, layer)
            col = isDiagonal ? getDiagonalColor() : na
            currentColumn := j < limit ? currentColumn + sign : currentColumn
            currentRow := j >= limit ? currentRow + sign : currentRow
            createCellWithValueAndColor(currentColumn, currentRow, startingValue, col)
            if isDiagonal
                array.push(diagonalValues, startingValue)

        if i == doubleLevels
            for j = 0 to i - 1 by 1
                startingValue := startingValue + increase
                count := count + 1
                isDiagonal = isDiagonal(count, layer)
                col = isDiagonal ? getDiagonalColor() : na
                currentColumn := currentColumn - sign
                createCellWithValueAndColor(currentColumn, currentRow, startingValue, col)
                if isDiagonal
                    array.push(diagonalValues, startingValue)
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
// ║ ⇌ • Build Square • ⇋
// ╚─────────────────────────────────...───────────────────────────────────────▲

// ╔────────────────────────────────. ■ .──────────────────────────────────────▼
// ║ ⇌ • Update • ⇋
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈□┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
if barstate.islast
    index = array.binary_search_leftmost(values, close)
    if index > 0
        coord = array.get(coordinates, index)
        table.cell_set_text_color(tab, coord.column, coord.row, color.red)
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
// ║ ⇌ • Update • ⇋
// ╚─────────────────────────────────...───────────────────────────────────────▲

// ╔────────────────────────────────. ■ .──────────────────────────────────────▼
// ║ ⇌ • Build and Update Lines/Labels • ⇋
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈□┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
if showDiagonalLevels
    if array.size(lines) == 0 and array.size(diagonalValues) > 0
        for i = 0 to array.size(diagonalValues) - 1 by 1
            float value = array.get(diagonalValues, i)
            array.push(lines, line.new(bar_index, value, bar_index, value, color=linesColor, extend=extend.right, style=line.style_dotted))
            array.push(labels, label.new(bar_index, value, str.tostring(value), yloc=yloc.price, textcolor=linesColor, style=label.style_label_left, color=na))
    else
        for i = 0 to array.size(lines) - 1 by 1
            line lin = array.get(lines, i)
            label lab = array.get(labels, i)
            line.set_x1(lin, bar_index - diagonalLevelsShift)
            line.set_x2(lin, bar_index)
            label.set_x(lab, bar_index + diagonalLabelShift)
// ║┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
// ║ ⇌ • Build and Update Lines/Labels • ⇋
// ╚─────────────────────────────────...───────────────────────────────────────▲