# indie:lang_version = 5
# HalfTrend Port
# Original: HalfTrend by everget (Pine Script v6)
# GPL-3.0 license

from math import nan, isnan
from indie import indicator, param, plot, color, MutSeriesF, algorithm, SeriesF
from indie.algorithms import Atr, Sma, SinceHighest, SinceLowest
from indie.color import rgba


# =============================================================================
# Pine nz() equivalent
# =============================================================================

def nz(value: float, replacement: float = 0.0) -> float:
    """Returns replacement if value is NaN, otherwise returns value"""
    return replacement if isnan(value) else value


# =============================================================================
# MAIN INDICATOR
# =============================================================================

@indicator('HalfTrend', overlay_main_pane=True)
# === Calculation Parameters (ORIGINAL) ===
@param.int('amplitude', default=2, min=1, title='Amplitude')
@param.int('channel_deviation', default=2, min=1, title='Channel Deviation')
# === Visual Parameters ===
@param.bool('show_arrows', default=True, title='Show Arrows')
@param.bool('show_channels', default=True, title='Show Channels')
@param.bool('show_labels', default=True, title='Show Buy/Sell Labels')
@param.bool('use_classic_colors', default=False, title='Classic Colors')
# === Plot declarations ===
@plot.line(id='ht_line', line_width=2, title='HalfTrend')
@plot.line(id='atr_high', title='ATR High')
@plot.line(id='atr_low', title='ATR Low')
@plot.fill('ht_line', 'atr_high', title='ATR High Ribbon')
@plot.fill('ht_line', 'atr_low', title='ATR Low Ribbon')
@plot.marker(style=plot.marker_style.LABEL, position=plot.marker_position.BELOW, size=7, title='Buy Signal')
@plot.marker(style=plot.marker_style.LABEL, position=plot.marker_position.ABOVE, size=7, title='Sell Signal')
def Main(self, amplitude, channel_deviation, show_arrows, show_channels, show_labels, use_classic_colors):
    # =========================================================================
    # COLORS 
    # =========================================================================
    classic_buy = color.BLUE
    classic_sell = color.RED
    tp_buy = rgba(0, 220, 255)
    tp_sell = rgba(255, 0, 180)
    
    buy_color = classic_buy if use_classic_colors else tp_buy
    sell_color = classic_sell if use_classic_colors else tp_sell
    buy_fill_color = color.BLUE(0.12) if use_classic_colors else rgba(0, 220, 255, 0.12)
    sell_fill_color = color.RED(0.12) if use_classic_colors else rgba(255, 0, 180, 0.12)
    
    # =========================================================================
    # STATE VARIABLES
    # =========================================================================
    trend = MutSeriesF.new(init=0)
    next_trend = MutSeriesF.new(init=0)
    max_low_price = MutSeriesF.new(init=nz(self.low[1], self.low[0]))
    min_high_price = MutSeriesF.new(init=nz(self.high[1], self.high[0]))
    up = MutSeriesF.new(init=0.0)
    down = MutSeriesF.new(init=0.0)
    
    atr_high_val: float = 0.0
    atr_low_val: float = 0.0
    arrow_up: float = nan
    arrow_down: float = nan
    
    # =========================================================================
    # CALCULATIONS
    # =========================================================================
    
    # Pine: atr2 = ta.atr(100) / 2
    atr2 = Atr.new(100)[0] / 2
    
    # Pine: dev = channelDeviation * atr2
    dev = channel_deviation * atr2
    

    high_offset = int(SinceHighest.new(self.high, amplitude)[0])
    low_offset = int(SinceLowest.new(self.low, amplitude)[0])
    
    # Request size to safely access historical values
    self.high.request_size(amplitude + 1)
    self.low.request_size(amplitude + 1)
    
    high_price = self.high[high_offset]
    low_price = self.low[low_offset]
    
    # Pine: highma = ta.sma(high, amplitude)
    highma = Sma.new(self.high, amplitude)[0]
    
    # Pine: lowma = ta.sma(low, amplitude)
    lowma = Sma.new(self.low, amplitude)[0]
    
    # =========================================================================
    # TREND LOGIC (ORIGINAL - UNCHANGED)
    # =========================================================================
    
    if next_trend[0] == 1:
        max_low_price[0] = max(low_price, max_low_price[0])
        
        if highma < max_low_price[0] and self.close[0] < nz(self.low[1], self.low[0]):
            trend[0] = 1
            next_trend[0] = 0
            min_high_price[0] = high_price
    else:
        min_high_price[0] = min(high_price, min_high_price[0])
        
        if lowma > min_high_price[0] and self.close[0] > nz(self.high[1], self.high[0]):
            trend[0] = 0
            next_trend[0] = 1
            max_low_price[0] = low_price
    
    # =========================================================================
    # UP/DOWN CALCULATION (ORIGINAL - UNCHANGED)
    # =========================================================================
    
    if trend[0] == 0:
        if not isnan(trend[1]) and trend[1] != 0:
            up[0] = down[0] if isnan(down[1]) else down[1]
            arrow_up = up[0] - atr2
        else:
            up[0] = max_low_price[0] if isnan(up[1]) else max(max_low_price[0], up[1])
        
        atr_high_val = up[0] + dev
        atr_low_val = up[0] - dev
    else:
        if not isnan(trend[1]) and trend[1] != 1:
            down[0] = up[0] if isnan(up[1]) else up[1]
            arrow_down = down[0] + atr2
        else:
            down[0] = min_high_price[0] if isnan(down[1]) else min(min_high_price[0], down[1])
        
        atr_high_val = down[0] + dev
        atr_low_val = down[0] - dev
    
    ht = up[0] if trend[0] == 0 else down[0]
    
    # =========================================================================
    # SIGNALS (ORIGINAL - UNCHANGED)
    # =========================================================================
    
    buy_signal = not isnan(arrow_up) and trend[0] == 0 and trend[1] == 1
    sell_signal = not isnan(arrow_down) and trend[0] == 1 and trend[1] == 0
    
    # =========================================================================
    # PLOTTING
    # =========================================================================
    
    ht_color = buy_color if trend[0] == 0 else sell_color
    
    ht_plot = plot.Line(ht, color=ht_color)
    atr_high_plot = plot.Line(atr_high_val if show_channels else nan, color=sell_color)
    atr_low_plot = plot.Line(atr_low_val if show_channels else nan, color=buy_color)
    fill_high = plot.Fill(color=sell_fill_color)
    fill_low = plot.Fill(color=buy_fill_color)
    
    buy_marker = plot.Marker(
        atr_low_val if (show_arrows and buy_signal) else nan,
        color=buy_color,
        text='Buy' if show_labels else ''
    )
    
    sell_marker = plot.Marker(
        atr_high_val if (show_arrows and sell_signal) else nan,
        color=sell_color,
        text='Sell' if show_labels else ''
    )
    
    return (
        ht_plot,
        atr_high_plot,
        atr_low_plot,
        fill_high,
        fill_low,
        buy_marker,
        sell_marker
    )
