# indie:lang_version = 5
# =============================================================================
# AlphaTrend Indicator
# Ported from TradingView Pine Script by KivancOzbilgic (MPL 2.0)
# Alerts not ported (8 conditions in original)
# =============================================================================

from math import nan, isnan
from indie import indicator, param, plot, MainContext, color, MutSeriesF, source
from indie.algorithms import Atr, Rsi, Mfi

@indicator('AT', overlay_main_pane=True)
@param.float('coeff', default=1.0, title='Multiplier', min=0.1, step=0.1)
@param.int('period', default=14, title='Common Period', min=1)
@param.source('src', default=source.CLOSE, title='Source')
@param.bool('show_signals', default=True, title='Show Signals?')
@param.bool('no_volume_data', default=False, title='Change calculation (no volume data)?')
@plot.line('k1', color=color.rgba(0, 200, 220), line_width=3)
@plot.line('k2', color=color.rgba(255, 140, 0), line_width=3)
@plot.fill('k1', 'k2', id='fill')
@plot.marker('buy_marker', style=plot.marker_style.LABEL, position=plot.marker_position.CENTER, color=color.rgba(0, 180, 255))
@plot.marker('sell_marker', style=plot.marker_style.LABEL, position=plot.marker_position.CENTER, color=color.rgba(255, 100, 0))
class Main(MainContext):
    def calc(self, coeff, period, src, show_signals, no_volume_data):
        # Series state
        at = MutSeriesF.new(nan, size=4)
        at_nz = MutSeriesF.new(nan, size=4)  # nz() emulation: last valid value
        bs_buy = MutSeriesF.new(nan)
        bs_sell = MutSeriesF.new(nan)
        bs_buy_prev = MutSeriesF.new(nan)
        bs_sell_prev = MutSeriesF.new(nan)
        
        # ATR and trend bounds
        atr = Atr.new(period, ma_algorithm='SMA')[0]
        up_t = self.low[0] - atr * coeff
        down_t = self.high[0] + atr * coeff
        
        # Trend condition: RSI or MFI >= 50
        cond = Rsi.new(src, period)[0] >= 50 if no_volume_data else Mfi.new(self.hlc3, period)[0] >= 50
        
        # nz(AlphaTrend[1]) - returns last valid value, not 0.0
        # Pine nz() propagates last known value through nan gaps
        nz_prev = at_nz[1] if not isnan(at_nz[1]) else 0.0
        
        # AlphaTrend calculation
        # Bullish: upT < prev ? prev : upT (ratchet up)
        # Bearish: downT > prev ? prev : downT (ratchet down)
        at[0] = (nz_prev if up_t < nz_prev else up_t) if cond else (nz_prev if down_t > nz_prev else down_t)
        
        # Update nz series with current value
        at_nz[0] = at[0] if not isnan(at[0]) else at_nz[1]
        
        # Historical values for crossover detection
        at0 = at[0]
        at1_nz = at_nz[1] if not isnan(at_nz[1]) else nan
        at2_nz = at_nz[2] if not isnan(at_nz[2]) else nan
        at3_nz = at_nz[3] if not isnan(at_nz[3]) else nan
        
        # Fill color using stabilized series
        bull_color = color.rgba(0, 200, 220, 0.3)
        bear_color = color.rgba(255, 120, 50, 0.3)
        
        fill_c = bull_color if (not isnan(at0) and not isnan(at2_nz) and at0 > at2_nz) else (
            bear_color if (not isnan(at0) and not isnan(at2_nz) and at0 < at2_nz) else (
                bull_color if (not isnan(at1_nz) and not isnan(at3_nz) and at1_nz > at3_nz) else bear_color
            )
        )
        
        # Crossover/crossunder - requires 4 valid bars
        # Pine ta.crossover returns false if any value is na
        is_buy = False
        is_sell = False
        
        if not isnan(at2_nz) and not isnan(at3_nz):
            is_buy = at0 > at2_nz and at1_nz <= at3_nz
            is_sell = at0 < at2_nz and at1_nz >= at3_nz
        
        # barssince counters
        bs_buy[0] = 0.0 if is_buy else (bs_buy[1] + 1.0 if not isnan(bs_buy[1]) else nan)
        bs_sell[0] = 0.0 if is_sell else (bs_sell[1] + 1.0 if not isnan(bs_sell[1]) else nan)
        
        k1 = bs_buy[0]
        k2 = bs_sell[0]
        
        # barssince(signal[1]) - separate counters for previous bar signals
        buy_prev = not isnan(bs_buy[1]) and bs_buy[1] == 0.0
        sell_prev = not isnan(bs_sell[1]) and bs_sell[1] == 0.0
        
        bs_buy_prev[0] = 0.0 if buy_prev else (bs_buy_prev[1] + 1.0 if not isnan(bs_buy_prev[1]) else nan)
        bs_sell_prev[0] = 0.0 if sell_prev else (bs_sell_prev[1] + 1.0 if not isnan(bs_sell_prev[1]) else nan)
        
        o1 = bs_buy_prev[0]
        o2 = bs_sell_prev[0]
        
        # Signal filter: prevents duplicate signals without reversal
        # BUY requires previous buy further back than last sell
        show_buy = is_buy and show_signals and (not isnan(o1)) and (not isnan(k2)) and (o1 > k2)
        show_sell = is_sell and show_signals and (not isnan(o2)) and (not isnan(k1)) and (o2 > k1)
        
        # Marker position: absolute Y at AlphaTrend[2] level
        buy_val = at2_nz * 0.9999 if (show_buy and not isnan(at2_nz)) else nan
        sell_val = at2_nz * 1.0001 if (show_sell and not isnan(at2_nz)) else nan
        
        # Output: k1=AlphaTrend, k2=AlphaTrend[2], fill, markers
        return (
            plot.Line(at0),
            plot.Line(at2_nz),
            plot.Fill(fill_c),
            plot.Marker(buy_val, text='BUY'),
            plot.Marker(sell_val, text='SELL')
        )
