// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © julzen2

//@version=5
indicator("SwamiRSI_v1", shorttitle="SwamiRSI_v1", overlay=false)

//--- Input Parameters ---
var int g_visualMode  = input.int(0, "Visual Mode (0=original, 1=trend)", minval=0, maxval=1)
var int g_priceType   = input.int(0, "Applied Price (0=Close, 1=Open, 2=High, 3=Low, 4=Median, 5=Typical; 6=Weighted)", minval=0, maxval=6)
var int g_startLength = input.int(12, "RSI Start Period", minval=1)
var int g_endLength   = input.int(48, "RSI End Period", minval=1)
var int g_sampleLength = input.int(48, "Sample RSI Period (0=off)", minval=0)
var int g_smooth      = input.int(5, "Smoothing Period", minval=1)
var color g_upTrendColor  = input.color(color.lime, "UpTrend Color")
var color g_dnTrendColor  = input.color(color.red, "DownTrend Color")
var color g_flatColor     = input.color(color.yellow, "Flat Color (if CLR_NONE, 2 Color Mix)")
var int g_scaleMode   = input.int(1, "Scale Mode (0=J.Ehlers, 1=0...100)", minval=0, maxval=1)
var int g_swamiBars   = input.int(100, "Swami Bars (-1=off, 0=all Bars, >0=any number)")

// --- Helper Functions ---

// Get applied price based on input
f_get_price(priceType) =>
    switch priceType
        0 => close
        1 => open
        2 => high
        3 => low
        4 => (high + low) / 2
        5 => (high + low + close) / 3
        6 => (open + high + low + close) / 4
        => close // Default to close if invalid type

// Custom EMA function to mimic MQL4 behavior more closely
// It stores previous EMA value in a separate buffer for state management across bars
// `ema_array`: an array that stores the previous EMA value for each 'index' (used by MQL4's ema[][2])
// `price_val`: the current price value for the EMA calculation
// `period`: the EMA period
// `idx`: the index within the ema_array to store the value (corresponds to MQL4's `index` argument)
f_ema(ema_array, price_val, period, idx) =>
    prev_ema_val = array.get(ema_array, idx)
    current_ema_val = if bar_index == 0 or na(prev_ema_val)
        price_val
    else
        prev_ema_val + 1.0 / period * (price_val - prev_ema_val)
    array.set(ema_array, idx, current_ema_val)
    current_ema_val

// Custom _RSI calculation (mimics MQL4 logic)
// `index`: corresponds to the 'i' in the MQL4 loop, used for array indexing
// `change`: current price change (price[0] - price[1])
// `period`: RSI period (StartLength + index)
// `ema_array`: array to pass to the f_ema function
// `swamisize`: from MQL4's swamisize global
f_custom_rsi(index, change, period, ema_array, swamisize) =>
    totChg = math.abs(change)

    // MQL4's EMA function uses `ema[index][0]` and `ema[index][1]`, and `prevtime[index]`
    // Here, we use indices for `ema_array`:
    // NetChgAvg uses `index` (0 to swamisize-1)
    // TotChgAvg uses `index + swamisize`
    // Final RSI EMA uses `index + 2*swamisize`
    netChgAvg = f_ema(ema_array, change, period, index)
    totChgAvg = f_ema(ema_array, totChg, period, index + swamisize)

    chgRatio = if totChgAvg != 0
        netChgAvg / totChgAvg
    else
        0.0

    rsiVal = f_ema(ema_array, 2 * chgRatio + 0.5, g_smooth, index + 2 * swamisize)

    math.max(0, math.min(1, rsiVal)) // Clamp between 0 and 1

// --- Global Variables (converted from MQL4's global arrays) ---
var float[] g_ema = na // Will be initialized on first bar
var int[] g_trend = na   // Will be initialized on first bar

var float g_fuzzWidth = na
var float g_maxValue = na
var float g_minValue = na

var int g_swamisize = na
var float g_priceCurrent = na
var float g_pricePrevious = na

// --- For Box Management ---
// We need a global array to store all created boxes so we can delete them.
var box[] all_swami_boxes = array.new_box(0)

// --- Main Calculation ---
g_priceCurrent := f_get_price(g_priceType)
g_pricePrevious := f_get_price(g_priceType)[1] // Price of the previous bar

// Initialize/resize arrays and set initial values on first bar
if bar_index == 0
    g_swamisize := g_endLength - g_startLength + 1
    // Corrected lines: Directly assign newly created arrays
    g_ema := array.new_float(g_swamisize * 3, na) // Initialize with 'na' values
    g_trend := array.new_int(g_swamisize, 0)     // Initialize with 0s

    if g_sampleLength > 0
        g_sampleLength := math.max(g_startLength, g_sampleLength)
        g_sampleLength := math.min(g_endLength, g_sampleLength)

    if g_scaleMode == 0 // J. Ehlers mode
        g_fuzzWidth := 1.0
        g_maxValue := float(g_endLength)
        g_minValue := float(g_startLength)
    else // 0-100 mode
        g_fuzzWidth := 100.0 / (g_endLength - g_startLength)
        g_maxValue := 100.0
        g_minValue := 0.0

// Variables for plotting
var float rsiPlot = na
var float sumRSIPlot = na

// Only calculate if enough bars are available
if bar_index >= g_endLength
    currentChange = g_priceCurrent - g_pricePrevious

    swamiSum = 0.0
    swamiRSI_values = array.new_float(g_swamisize) // Temporary array for current bar's SwamiRSI values

    for i = 0 to g_swamisize - 1
        currentRsiPeriod = g_startLength + i
       
        // Calculate the custom RSI (clamped between 0 and 1)
        rsi_value_clamped = f_custom_rsi(i, currentChange, currentRsiPeriod, g_ema, g_swamisize)

        if g_visualMode == 0 // Original mode
            array.set(swamiRSI_values, i, rsi_value_clamped)
            swamiSum += rsi_value_clamped
        else // Trend mode
            // Get the trend state from the previous bar's calculation (stored in g_trend)
            current_trend_state = array.get(g_trend, i)
           
            if rsi_value_clamped > 0.5
                current_trend_state := 1
            else if rsi_value_clamped < 0.5
                current_trend_state := 0
           
            array.set(g_trend, i, current_trend_state) // Update global trend array for next bar
            array.set(swamiRSI_values, i, float(current_trend_state))
            swamiSum += float(current_trend_state)

    if g_scaleMode == 0 // J. Ehlers mode
        if g_sampleLength > 0
            // Find the index for sampleLength
            sampleIndex = g_sampleLength - g_startLength
            rsiPlot := float(g_startLength) + (float(g_swamisize) - 1.0) * array.get(swamiRSI_values, sampleIndex)
        else
            rsiPlot := na // If SampleLength is 0, MQL4 also seems to not plot RSI
        sumRSIPlot := float(g_startLength) + (float(g_swamisize) - 1.0) * (swamiSum / float(g_swamisize))
    else // 0-100 mode
        if g_sampleLength > 0
            // Find the index for sampleLength
            sampleIndex = g_sampleLength - g_startLength
            rsiPlot := 100.0 * array.get(swamiRSI_values, sampleIndex)
        else
            rsiPlot := na // If SampleLength is 0, MQL4 also seems to not plot RSI
        sumRSIPlot := 100.0 * (swamiSum / float(g_swamisize))

    // --- Dynamic Rectangle Plotting (Swami Bars) ---
    plot_boxes = false
    if g_swamiBars == 0
        plot_boxes = true // Plot on all bars
    else if g_swamiBars > 0 and bar_index >= (last_bar_index - g_swamiBars)
        plot_boxes = true // Plot for the last 'g_swamiBars' bars

    // Clear all existing boxes at the start of the current bar's calculation
    // or if the indicator is re-initializing (barstate.isfirst)
    if barstate.islast or barstate.isfirst
        // Added check for array size before looping to prevent out-of-bounds error
        if array.size(all_swami_boxes) > 0
            // Iterate through our stored box IDs and delete them
            for i = 0 to array.size(all_swami_boxes) - 1
                box.delete(array.get(all_swami_boxes, i))
        // Clear the array after deleting objects (can always clear an empty array safely)
        array.clear(all_swami_boxes)

    if plot_boxes
        for i = 0 to g_swamisize - 1
            currentSwamiRSI = array.get(swamiRSI_values, i)
            barColor = color.new(color.white, 0) // Initialize with a dummy color and full transparency

            isFlatColorNone = (g_flatColor == color.new(color.fuchsia, 0)) // Using fuchsia as proxy for CLR_NONE

            if isFlatColorNone // This implies a 2-color mix (UpTrendColor and DnTrendColor)
                r1 = color.r(g_dnTrendColor) + currentSwamiRSI * (color.r(g_upTrendColor) - color.r(g_dnTrendColor))
                g1 = color.g(g_dnTrendColor) + currentSwamiRSI * (color.g(g_upTrendColor) - color.g(g_dnTrendColor))
                b1 = color.b(g_dnTrendColor) + currentSwamiRSI * (color.b(g_upTrendColor) - color.b(g_dnTrendColor))
                barColor := color.rgb(math.round(r1), math.round(g1), math.round(b1))
            else // 3 Color Mix with FlatColor
                if currentSwamiRSI >= 0.5
                    r1 = color.r(g_upTrendColor) + 2 * (1 - currentSwamiRSI) * (color.r(g_flatColor) - color.r(g_upTrendColor))
                    g1 = color.g(g_upTrendColor) + 2 * (1 - currentSwamiRSI) * (color.g(g_flatColor) - color.g(g_upTrendColor))
                    b1 = color.b(g_upTrendColor) + 2 * (1 - currentSwamiRSI) * (color.b(g_flatColor) - color.b(g_upTrendColor))
                    barColor := color.rgb(math.round(r1), math.round(g1), math.round(b1))
                else
                    r1 = color.r(g_dnTrendColor) + 2 * currentSwamiRSI * (color.r(g_flatColor) - color.r(g_dnTrendColor))
                    g1 = color.g(g_dnTrendColor) + 2 * currentSwamiRSI * (color.g(g_flatColor) - color.g(g_dnTrendColor))
                    b1 = color.b(g_dnTrendColor) + 2 * currentSwamiRSI * (color.b(g_flatColor) - color.b(g_dnTrendColor))
                    barColor := color.rgb(math.round(r1), math.round(g1), math.round(b1))

            currentValue = if g_scaleMode == 0
                float(i + g_startLength)
            else
                float(i) * g_fuzzWidth

            // Create the box and store its ID in our global array
            newBox = box.new(time[0], currentValue - 0.5 * g_fuzzWidth,
                             time, currentValue + 0.5 * g_fuzzWidth,
                             border_color=barColor, border_width=0, bgcolor=color.new(barColor, 50))
            array.push(all_swami_boxes, newBox) // Add the new box ID to our array
    else
        // If not plotting boxes for this bar, ensure all boxes are deleted
        if bar_index == last_bar_index and g_swamiBars == -1
            // Added check for array size before looping
            if array.size(all_swami_boxes) > 0
                for i = 0 to array.size(all_swami_boxes) - 1
                    box.delete(array.get(all_swami_boxes, i))
            array.clear(all_swami_boxes)


// --- Plotting the Lines ---
plot(rsiPlot, "SwamiRSI_Sample", color.rgb(139, 0, 139), style=plot.style_line, linewidth=2) // DarkOrchid
plot(sumRSIPlot, "SummaryRSI", color.rgb(210, 105, 30), style=plot.style_line, linewidth=2) // Chocolate

// Plot horizontal lines for min/max values
plot(g_minValue, "Min Value", color.gray, style=plot.style_stepline)
plot(g_maxValue, "Max Value", color.gray, style=plot.style_stepline)
