# indie:lang_version = 5
from math import floor, sqrt, nan
from indie import (
    indicator, MainContext, sec_context, param, param_ref,
    plot, color, MutSeriesF, algorithm, SeriesF, Optional, Color
)
from indie.algorithms import Sma, Ema, Wma, Vwma, Rma
from indie.math import cross


# ─── Hull Moving Average ──────────────────────────────────────────────────────
@algorithm
def HullMa(self, src: SeriesF, length: int) -> SeriesF:
    half = max(1, length // 2)
    wma_h = Wma.new(src, half)
    wma_f = Wma.new(src, length)
    raw = MutSeriesF.new(2 * wma_h[0] - wma_f[0])
    return Wma.new(raw, max(1, floor(sqrt(length))))


# ─── Triple EMA ───────────────────────────────────────────────────────────────
@algorithm
def Tema(self, src: SeriesF, length: int) -> SeriesF:
    e1 = Ema.new(src, length)
    e2 = Ema.new(e1, length)
    e3 = Ema.new(e2, length)
    return MutSeriesF.new(3 * (e1[0] - e2[0]) + e3[0])


# ─── Tilson T3 (direct 6-EMA formulation, equivalent to cascaded GD) ─────────
@algorithm
def TilsonT3(self, src: SeriesF, length: int, factor: float) -> SeriesF:
    f2 = factor * factor
    f3 = f2 * factor
    e1 = Ema.new(src, length)
    e2 = Ema.new(e1, length)
    e3 = Ema.new(e2, length)
    e4 = Ema.new(e3, length)
    e5 = Ema.new(e4, length)
    e6 = Ema.new(e5, length)
    val = (-f3) * e6[0] + (3 * f2 + 3 * f3) * e5[0] + (-(6 * f2 + 3 * factor + 3 * f3)) * e4[0] + (1 + 3 * factor + f3 + 3 * f2) * e3[0]
    return MutSeriesF.new(val)


# ─── Secondary Context: compute only the selected MA per slot ─────────────────
@sec_context
@param_ref('length')
@param_ref('atype')
@param_ref('factor_t3')
@param_ref('length2')
@param_ref('atype2')
@param_ref('sfactor_t3')
def SecMain(self, length, atype, factor_t3, length2, atype2, sfactor_t3):
    src = self.close
    factor = factor_t3 * 0.1
    sfactor = sfactor_t3 * 0.1

    ma1: float = nan
    if atype == 2:
        ma1 = Ema.new(src, length)[0]
    elif atype == 3:
        ma1 = Wma.new(src, length)[0]
    elif atype == 4:
        ma1 = HullMa.new(src, length)[0]
    elif atype == 5:
        ma1 = Vwma.new(src, length)[0]
    elif atype == 6:
        ma1 = Rma.new(src, length)[0]
    elif atype == 7:
        ma1 = Tema.new(src, length)[0]
    elif atype == 8:
        ma1 = TilsonT3.new(src, length, factor)[0]
    else:
        ma1 = Sma.new(src, length)[0]

    ma2: float = nan
    if atype2 == 2:
        ma2 = Ema.new(src, length2)[0]
    elif atype2 == 3:
        ma2 = Wma.new(src, length2)[0]
    elif atype2 == 4:
        ma2 = HullMa.new(src, length2)[0]
    elif atype2 == 5:
        ma2 = Vwma.new(src, length2)[0]
    elif atype2 == 6:
        ma2 = Rma.new(src, length2)[0]
    elif atype2 == 7:
        ma2 = Tema.new(src, length2)[0]
    elif atype2 == 8:
        ma2 = TilsonT3.new(src, length2, sfactor)[0]
    else:
        ma2 = Sma.new(src, length2)[0]

    return ma1, ma2


# ─── Main Indicator ───────────────────────────────────────────────────────────
@indicator('Ultimate MA MTF', overlay_main_pane=True)
@param.bool('use_current_res', default=True, title='Use Current Chart Resolution?')
@param.time_frame('sec_time_frame', default='1D', title='Custom Timeframe (uncheck box above)')
@param.int('length', default=20, min=1, title='MA Length (Lookback Period)')
@param.int('atype', default=1, min=1, max=8, title='MA Type (1=SMA 2=EMA 3=WMA 4=Hull 5=VWMA 6=RMA 7=TEMA 8=T3)')
@param.int('factor_t3', default=7, min=0, title='T3 Factor (*0.10, so 7=0.7)')
@param.bool('spc', default=False, title='Highlight Bar When Price Crosses MA1?')
@param.bool('cc', default=True, title='Color MA1 by Direction?')
@param.int('smoothe', default=2, min=1, max=10, title='Color Smoothing (1=none)')
@param.bool('doma2', default=False, title='Show Optional 2nd MA?')
@param.bool('spc2', default=False, title='Highlight Bar When Price Crosses MA2?')
@param.int('length2', default=50, min=1, title='2nd MA Length')
@param.int('sfactor_t3', default=7, min=0, title='2nd MA T3 Factor (*0.10)')
@param.int('atype2', default=1, min=1, max=8, title='2nd MA Type (1=SMA 2=EMA 3=WMA 4=Hull 5=VWMA 6=RMA 7=TEMA 8=T3)')
@param.bool('cc2', default=True, title='Color MA2 by Direction?')
@param.bool('sd', default=False, title='Show Cross Markers on MA Cross?')
@plot.line(line_width=4, title='MA1')
@plot.marker(style=plot.marker_style.CIRCLE, position=plot.marker_position.CENTER, size=4, title='MA2')
@plot.marker(style=plot.marker_style.CROSS, position=plot.marker_position.CENTER, size=7, color=color.AQUA, title='MA Cross')
@plot.bar_color()
class Main(MainContext):
    def __init__(
        self,
        use_current_res, sec_time_frame,
        length, atype, factor_t3,
        spc, cc, smoothe,
        doma2, spc2,
        length2, sfactor_t3, atype2, cc2,
        sd
    ):
        tf = None if use_current_res else sec_time_frame
        self._ma1, self._ma2 = self.calc_on(SecMain, time_frame=tf)
        self._spc = spc
        self._cc = cc
        self._smoothe = smoothe
        self._doma2 = doma2
        self._spc2 = spc2
        self._cc2 = cc2
        self._sd = sd

    def calc(self):
        ma1 = self._ma1[0]
        ma2 = self._ma2[0]

        col1: Color = color.AQUA
        if self._cc:
            if ma1 >= self._ma1[self._smoothe]:
                col1 = color.LIME
            else:
                col1 = color.RED

        ma2_val: float = nan
        if self._doma2:
            ma2_val = ma2

        col2: Color = color.WHITE
        if self._cc2:
            if ma2 >= self._ma2[self._smoothe]:
                col2 = color.LIME
            else:
                col2 = color.RED

        cross_val: float = nan
        if self._sd:
            if cross(self._ma1, self._ma2):
                cross_val = ma2

        bar_col: Optional[Color] = None
        if self._spc or self._spc2:
            o = self.open[0]
            c = self.close[0]
            if self._spc and ((o < ma1) != (c < ma1)):
                bar_col = color.YELLOW
            elif self._spc2 and ((o < ma2) != (c < ma2)):
                bar_col = color.YELLOW

        return (
            plot.Line(ma1, color=col1),
            plot.Marker(ma2_val, color=col2),
            plot.Marker(cross_val),
            plot.BarColor(bar_col),
        )
