Multi-Trend
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © faiyaz7283
//@version=5

indicator(title="Multi-Trend", overlay=true)

// ::Imports:: {

import faiyaz7283/tools/10 as tools
import faiyaz7283/printer/6 as prnt
import faiyaz7283/multidata/7 as mltd

// }

// ::Inputs:: {

var section00 = 'HEADER'
//---------------------:
var header = input.string(defval='Multi Trend', title='Header', inline='hdr', group=section00)
var headerShow = input.bool(defval = false, title = 'Show', inline = 'hdr', group = section00)

var section01 = 'POSITION, SIZE & COLORS'
//---------------------:

var displayLoc = input.string(defval = position.top_right, title = 'Display Location', options = [position.top_left, 
  position.top_center, position.top_right, position.middle_left, position.middle_center, position.middle_right, 
  position.bottom_left, position.bottom_center, position.bottom_right], group = section01)

var cellPad = input.int(defval=2, title='Cell Spacing', minval=0, maxval=5, group=section01)

var orientation = input.string(defval = 'vertical', title = 'Orientation',  options = ['horizontal', 'vertical'], 
  group = section01) == 'vertical' ? false : true 

var headerBgColor = input.color(defval=#1E283273, title = 'Header:\tBG', inline = 'hdrCl', group = section01)

var headerColor = input.color(defval=#E1E1E1 , title = 'Text', inline = 'hdrCl', group = section01)

var headerSize = input.string(defval=size.auto, title = 'Size', options = [size.auto, size.tiny, size.small, 
  size.normal, size.large, size.huge], inline = 'hdrCl', group = section01)

var headerAlign = input.string(defval=text.align_center, title = 'Align', 
  options = [text.align_left, text.align_center, text.align_right], inline = 'hdrCl', group = section01)

var titleBgColor = input.color(defval=#00000000, title = 'Title:\tBG', inline = 'ttlCl', group = section01)

var titleColor = input.color(defval=#80deea , title = 'Text', inline = 'ttlCl', group = section01)

var titleSize = input.string(defval=size.auto, title = 'Size', options = ['hide', size.auto, size.tiny, size.small, 
  size.normal, size.large, size.huge], inline = 'ttlCl', group = section01)

var titleAlign = input.string(defval=text.align_left, title = 'Align', 
  options = [text.align_left, text.align_center, text.align_right], inline = 'ttlCl', group = section01)

var keyColor = input.color(defval=#000000, title = 'Key:\tText', inline = 'keyCl', group = section01)

var keySize = input.string(defval=size.normal, title = 'Size', options = [size.auto, size.tiny, size.small, 
  size.normal, size.large, size.huge], inline = 'keyCl', group = section01)

var keyAlign = input.string(defval=text.align_center, title = 'Align', 
  options = [text.align_left, text.align_center, text.align_right], inline = 'keyCl', group = section01)

var textBgColor = input.color(defval=#00000033, title = 'Value:\tBG', inline = 'txtCl', group = section01)

var textSize = input.string(defval=size.large, title = 'Size', options = [size.auto, size.tiny, size.small, 
  size.normal, size.large, size.huge], inline = 'txtCl', group = section01)

var textAlign = input.string(defval=text.align_center, title = 'Align', 
  options = [text.align_left, text.align_center, text.align_right], inline = 'txtCl', group = section01)

var up = input.string(defval='↑', title='Up', inline = 'U', group=section01)
var upColor = input.color(defval = #00FF00, title = '', inline = 'U', group = section01)

var neutral = input.string(defval='→', title='Unchanged', inline = 'N', group=section01)
var neutralColor = input.color(defval = #fff9c4, title = '', inline = 'N', group = section01)

var down = input.string(defval='↓', title='Down', inline = 'D', group=section01)
var downColor = input.color(defval = #FF0000, title = '', inline = 'D', group = section01)

var section02 = 'TREND'
//----------------------------:

var calcTip1 = 'Close: Determine the trend direction by comparing the current close price with the close price of the '
var calcTip2 = calcTip1 + 'previous candle within the chosen timeframe.\n\nOpen: Establish the trend direction by '
var calcTip3 = calcTip2 + 'contrasting the current close price with the open price of the current candle within the '
var calcTip  = calcTip3 + 'selected timeframe.\n\nNote: "Candle" refers to each of the chosen timeframes below.'
var source = input.string(defval='close', title = 'Source', tooltip=calcTip, options = ['close', 'open'], 
  inline='dd', group = section02)

var showVol = input.bool(defval=false, title='Show Volume Trend', group=section02)

var section03 = 'TIMEFRAMES'
//--------------------------------:

var tfNote0 = "M: Multiplier | U: Unit\n\nValid Multipliers per Unit:\n- Seconds, ONLY use 1, 5, 10, 15 or 30."
var tfNote1 = tfNote0 + '\n- Minutes, 1 to 1439.\n- Days, 1 to 365.\n- Weeks, 1 to 52.\n- Months, 1 to 12.\n\n'
var tfNote2 = tfNote1 + 'N: nth candle\n\n- Nth 0 is current candle.\n- Nth 1 is previous candle.\n- Nth 2 is previous '
var tfNote = tfNote2 + '2nd candle in past.\n- Nth 3 is previous 3rd candle in past.\n ...\n\nCheck box to display.'

// Timeframe 1
var tfM1 = input.int(defval = 5, title = 'M', minval = 1, maxval = 1439, inline = 'tf1', 
  group = section03)
var tfU1 = input.string(defval = 'Minutes', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf1', group = section03)
var tfN1 = input.int(defval=0, title = 'N', minval=0, inline = 'tf1', group = section03)
var useTf1 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf1', group = section03)

// Timeframe 2
var tfM2 = input.int(defval = 15, title = 'M', minval = 1, maxval = 1439, inline = 'tf2', 
  group = section03)
var tfU2 = input.string(defval = 'Minutes', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf2', group = section03)
var tfN2 = input.int(defval=0, title = 'N', minval=0, inline = 'tf2', group = section03)
var useTf2 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf2', group = section03)

// Timeframe 3
var tfM3 = input.int(defval = 30, title = 'M', minval = 1, maxval = 1439, inline = 'tf3', 
  group = section03)
var tfU3 = input.string(defval = 'Minutes', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf3', group = section03)
var tfN3 = input.int(defval=0, title = 'N', minval=0, inline = 'tf3', group = section03)
var useTf3 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf3', group = section03)

// Timeframe 4
var tfM4 = input.int(defval = 60, title = 'M', minval = 1, maxval = 1439, inline = 'tf4', 
  group = section03)
var tfU4 = input.string(defval = 'Minutes', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf4', group = section03)
var tfN4 = input.int(defval=0, title = 'N', minval=0, inline = 'tf4', group = section03)
var useTf4 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf4', group = section03)

// Timeframe 5
var tfM5 = input.int(defval = 240, title = 'M', minval = 1, maxval = 1439, inline = 'tf5', 
  group = section03)
var tfU5 = input.string(defval = 'Minutes', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf5', group = section03)
var tfN5 = input.int(defval=0, title = 'N', minval=0, inline = 'tf5', group = section03)
var useTf5 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf5', group = section03)

// Timeframe 6
var tfM6 = input.int(defval = 480, title = 'M', minval = 1, maxval = 1439, inline = 'tf6', 
  group = section03)
var tfU6 = input.string(defval = 'Minutes', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf6', group = section03)
var tfN6 = input.int(defval=0, title = 'N', minval=0, inline = 'tf6', group = section03)
var useTf6 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf6', group = section03)

// Timeframe 7
var tfM7 = input.int(defval = 1, title = 'M', minval = 1, maxval = 1439, inline = 'tf7', 
  group = section03)
var tfU7 = input.string(defval = 'Days', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf7', group = section03)
var tfN7 = input.int(defval=0, title = 'N', minval=0, inline = 'tf7', group = section03)
var useTf7 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf7', group = section03)

// Timeframe 8
var tfM8 = input.int(defval = 1, title = 'M', minval = 1, maxval = 1439, inline = 'tf8', 
  group = section03)
var tfU8 = input.string(defval = 'Weeks', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf8', group = section03)
var tfN8 = input.int(defval=0, title = 'N', minval=0, inline = 'tf8', group = section03)
var useTf8 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf8', group = section03)

// Timeframe 9
var tfM9 = input.int(defval = 1, title = 'M', minval = 1, maxval = 1439, inline = 'tf9', 
  group = section03)
var tfU9 = input.string(defval = 'Months', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf9', group = section03)
var tfN9 = input.int(defval=0, title = 'N', minval=0, inline = 'tf9', group = section03)
var useTf9 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf9', group = section03)

// Timeframe 10
var tfM10 = input.int(defval = 12, title = 'M', minval = 1, maxval = 1439, inline = 'tf10', 
  group = section03)
var tfU10 = input.string(defval = 'Months', title = 'U', 
  options = ['Seconds', 'Minutes', 'Days', 'Weeks', 'Months'], inline = 'tf10', group = section03)
var tfN10 = input.int(defval=0, title = 'N', minval=0, inline = 'tf10', group = section03)
var useTf10 = input.bool(defval = true, title = 'Show', tooltip = tfNote, inline = 'tf10', group = section03)

// }

// Functions: {

displayTf(m, u) =>
    _m = m
    _u = switch 
        str.endswith(u, "S") => 
            m == 1 ? 'Second' : 'Seconds'
        str.endswith(u, "D") =>
            m == 1 ? 'Day' : 'Days'
        str.endswith(u, "W") =>
            m == 1 ? 'Week' : 'Weeks'
        str.endswith(u, "M") =>
            if m == 1
                'Month'
            else if m == 12
                _m := 1
                'Year'
            else
                'Months'
        =>
            if m == 1
                'Minute'
            else if m % 60 == 0
                _m := m / 60
                _m == 1 ? 'Hour' : 'Hours'
            else
                'Minutes'
    _m == 1 ? _u : str.format('{0} {1}', _m, _u)

getKv(multiplier, unit, nth) =>
    u = switch unit
        'Seconds' => 'S'
        'Minutes' => ''
        'Days' => 'D'
        'Weeks' => 'W'
        'Months' => 'M'
    tf = str.tostring(multiplier) + u
    srce = source == 'close' ? close[nth + 1] : open[nth]
    tckr = ticker.new(syminfo.prefix, syminfo.ticker, syminfo.session)
    [c, c1, v, v1] = request.security(tckr, tf, [close[nth], srce, volume[nth], volume[nth + 1]], ignore_invalid_symbol = true)
    dsplyPrice = c > c1 ? up : (c < c1 ? down : neutral)
    dsplyVol = v > v1 ? up : (v < v1 ? down : neutral)
    nthKey = nth > 0 ? (nth == 1 ? 'Previous ' : str.format('(Past {0}) ', tools.ordinal(nth))) : na
    key = nthKey + displayTf(multiplier, u)
    [mltd.kv(key, val=dsplyPrice), mltd.kv(key, val=dsplyVol)]

// }

if barstate.islast
    tbs = prnt.tableStyle.new(bgColor=na, frameColor=na, borderWidth=cellPad)
    hs = prnt.headerStyle.new(textHalign=headerAlign, textColor=headerColor, textSize=headerSize, bgColor=headerBgColor)
    ts = prnt.titleStyle.new(textHalign=titleAlign, textColor=titleColor, textSize=titleSize, bgColor=titleBgColor)
    kcs = prnt.cellStyle.new(textHalign=keyAlign, textSize=keySize)
    cs = prnt.cellStyle.new(horizontal=orientation, textHalign=textAlign, textSize=textSize)

    // Initialize the printer
    printer = prnt.printer(header=(headerShow ? header : na), stack=orientation, tableStyle=tbs, headerStyle=hs, 
      titleStyle=ts, keyCellStyle=kcs, cellStyle=cs, loc=displayLoc)

    // Data {
    price = array.new<mltd.kv>()
    vol = array.new<mltd.kv>()

    if useTf1
        [p, v] = getKv(tfM1, tfU1, tfN1)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf2
        [p, v] = getKv(tfM2, tfU2, tfN2)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf3
        [p, v] = getKv(tfM3, tfU3, tfN3)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf4
        [p, v] = getKv(tfM4, tfU4, tfN4)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf5
        [p, v] = getKv(tfM5, tfU5, tfN5)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf6
        [p, v] = getKv(tfM6, tfU6, tfN6)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf7
        [p, v] = getKv(tfM7, tfU7, tfN7)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf8
        [p, v] = getKv(tfM8, tfU8, tfN8)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf9
        [p, v] = getKv(tfM9, tfU9, tfN9)
        price.push(p)
        if showVol
            vol.push(v)

    if useTf10
        [p, v] = getKv(tfM10, tfU10, tfN10) 
        price.push(p)
        if showVol
            vol.push(v)

    if price.size() > 0
        // Store multidata
        d2dPrice = mltd.data2d(price)
        d2dVol = showVol ? mltd.data2d(vol) : na

        // Setup dynamic styles 
        d2dStyles = map.new<string, prnt.cellStyle>()
        keyStyle = prnt.cellStyle.copy(printer.keyCellStyle)
        dc = prnt.dynamicColor.new(stringUp=up, stringNeutral=neutral, stringDown=down, up=upColor, 
          neutral=neutralColor, down=downColor)
        keyStyle.dynamicColor := prnt.dynamicColor.copy(dc)
        keyStyle.dynamicColor.offsetItem :='text'
        keyStyle.dynamicColor.offsetColor := keyColor
        valStyle = prnt.cellStyle.copy(printer.cellStyle)
        valStyle.dynamicColor := prnt.dynamicColor.copy(dc)
        valStyle.dynamicColor.offsetItem := 'bg'
        valStyle.dynamicColor.offsetColor := textBgColor
        d2dStyles.put('key', keyStyle)
        d2dStyles.put('val', valStyle)

        // Output
        pTitle = titleSize != 'hide' ? 'Price' : na
        printer.print(d2dPrice, title=pTitle, styles=d2dStyles, dynamicKey=true)
        if showVol
            vTitle = titleSize != 'hide' ? 'Volume' : na
            printer.print(d2dVol, title=vTitle, styles=d2dStyles, dynamicKey=true)

    // }