#property copyright "Algobot"
#property link      "https://www.algobot.live"
#property version   "1.00"
#property strict

#include <Trade\Trade.mqh>
CTrade trade;

//--- inputs (mirror DescribeParameters / GetInput defaults) ---
input int    FastEma      = 20;     // fast EMA period
input int    SlowEma      = 50;     // slow EMA period
input int    CciPeriod    = 20;     // CCI period
input double CciThreshold = 100.0;  // CCI extreme threshold
input int    AtrPeriod    = 14;     // ATR period
input double AtrSlMult    = 1.5;    // ATR stop multiple
input double AtrTpMult    = 2.5;    // ATR target multiple
input double Lots         = 0.10;   // fixed lot size
input long   Magic        = 1023;   // magic number

//--- indicator handles (one per indicator) ---
int g_emaFast = INVALID_HANDLE;
int g_emaSlow = INVALID_HANDLE;
int g_cci     = INVALID_HANDLE;
int g_atr     = INVALID_HANDLE;

//--- runtime state ---
int g_slowEma = 0;   // slow EMA period after the "inverted inputs" guard

int OnInit()
{
    // guard against inverted EMA inputs (C#: if (_fastEma >= _slowEma) _slowEma = _fastEma + 1)
    g_slowEma = SlowEma;
    if(FastEma >= g_slowEma) g_slowEma = FastEma + 1;

    trade.SetExpertMagicNumber(Magic);

    // EMA trend filter (PRICE_CLOSE, MODE_EMA) == Indicators.Ema(closes, p)
    g_emaFast = iMA(_Symbol, _Period, FastEma,   0, MODE_EMA, PRICE_CLOSE);
    g_emaSlow = iMA(_Symbol, _Period, g_slowEma, 0, MODE_EMA, PRICE_CLOSE);

    // CCI on typical price (High+Low+Close)/3 == C# ComputeCci
    g_cci = iCCI(_Symbol, _Period, CciPeriod, PRICE_TYPICAL);

    // ATR for stop / target framing == Indicators.Atr
    g_atr = iATR(_Symbol, _Period, AtrPeriod);

    if(g_emaFast == INVALID_HANDLE || g_emaSlow == INVALID_HANDLE ||
       g_cci == INVALID_HANDLE || g_atr == INVALID_HANDLE)
    {
        Print("CciTrendPullback: failed to create indicator handles");
        return INIT_FAILED;
    }
    return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
    if(g_emaFast != INVALID_HANDLE) IndicatorRelease(g_emaFast);
    if(g_emaSlow != INVALID_HANDLE) IndicatorRelease(g_emaSlow);
    if(g_cci     != INVALID_HANDLE) IndicatorRelease(g_cci);
    if(g_atr     != INVALID_HANDLE) IndicatorRelease(g_atr);
}

void OnTick()
{
    // Act once per finished bar. When bar-0 advances, bar-1 has just closed.
    if(!IsNewBar()) return;

    // Need enough history for the slow EMA, the CCI window (+ previous value)
    // and ATR. C# uses shift 1 (just-closed) and shift 2 (prior CCI), so keep margin.
    int need = MathMax(g_slowEma + 1, MathMax(CciPeriod + 2, AtrPeriod + 1)) + 2;
    if(Bars(_Symbol, _Period) < need) return;

    // ---- copy indicator buffers (index 0 = current forming bar) ----
    double emaFastBuf[2], emaSlowBuf[2], cciBuf[3], atrBuf[2];
    ArraySetAsSeries(emaFastBuf, true);
    ArraySetAsSeries(emaSlowBuf, true);
    ArraySetAsSeries(cciBuf,     true);
    ArraySetAsSeries(atrBuf,     true);

    if(CopyBuffer(g_emaFast, 0, 0, 2, emaFastBuf) < 2) return;
    if(CopyBuffer(g_emaSlow, 0, 0, 2, emaSlowBuf) < 2) return;
    if(CopyBuffer(g_cci,     0, 0, 3, cciBuf)     < 3) return;
    if(CopyBuffer(g_atr,     0, 0, 2, atrBuf)     < 2) return;

    // ---- EMA trend filter (evaluated on the just-closed bar, shift 1) ----
    double emaFast = emaFastBuf[1];
    double emaSlow = emaSlowBuf[1];
    bool upTrend   = emaFast > emaSlow;
    bool downTrend = emaFast < emaSlow;

    // ---- CCI pullback gate: cciNow = just-closed bar (shift 1), cciPrev = prior (shift 2) ----
    double cciNow  = cciBuf[1];
    double cciPrev = cciBuf[2];

    // ---- ATR on the just-closed bar (shift 1) ----
    double atr = atrBuf[1];
    if(atr <= 0.0) return;

    // ---- manage any existing position: close it if the trend flips against it ----
    // C#: if open positions exist, close mismatched ones and return (one position per magic).
    if(ManagePositions(upTrend, downTrend)) return;

    // ---- confirming candle body of the just-closed bar (shift 1) ----
    double cOpen  = iOpen(_Symbol,  _Period, 1);
    double cClose = iClose(_Symbol, _Period, 1);
    bool bullCandle = cClose > cOpen;
    bool bearCandle = cClose < cOpen;

    // LONG: uptrend, CCI dipped below -threshold then snapped back up, bullish confirmation
    bool longSignal  = upTrend   && cciPrev <= -CciThreshold && cciNow > -CciThreshold && bullCandle;
    // SHORT: downtrend, CCI popped above +threshold then rolled back down, bearish confirmation
    bool shortSignal = downTrend && cciPrev >=  CciThreshold && cciNow <  CciThreshold && bearCandle;

    if(longSignal)
    {
        double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double sl = NormalizeDouble(price - atr * AtrSlMult, _Digits);
        double tp = NormalizeDouble(price + atr * AtrTpMult, _Digits);
        if(trade.Buy(Lots, _Symbol, 0.0, sl, tp, "CciTrendPullback"))
            PrintFormat("CciTrendPullback Buy %s @ %.5f sl=%.5f tp=%.5f", _Symbol, price, sl, tp);
    }
    else if(shortSignal)
    {
        double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double sl = NormalizeDouble(price + atr * AtrSlMult, _Digits);
        double tp = NormalizeDouble(price - atr * AtrTpMult, _Digits);
        if(trade.Sell(Lots, _Symbol, 0.0, sl, tp, "CciTrendPullback"))
            PrintFormat("CciTrendPullback Sell %s @ %.5f sl=%.5f tp=%.5f", _Symbol, price, sl, tp);
    }
}

// Returns true if at least one position for this symbol+magic exists (matches C#
// open.Count > 0). Closes buys when the trend has flipped down and sells when it flipped up.
bool ManagePositions(bool upTrend, bool downTrend)
{
    bool had = false;
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong t = PositionGetTicket(i);
        if(PositionSelectByTicket(t) &&
           PositionGetString(POSITION_SYMBOL) == _Symbol &&
           PositionGetInteger(POSITION_MAGIC) == Magic)
        {
            had = true;
            long type = PositionGetInteger(POSITION_TYPE);
            if(type == POSITION_TYPE_BUY && downTrend)       trade.PositionClose(t);
            else if(type == POSITION_TYPE_SELL && upTrend)   trade.PositionClose(t);
        }
    }
    return had;
}

bool IsNewBar()
{
    static datetime last = 0;
    datetime cur = iTime(_Symbol, _Period, 0);
    if(cur != last){ last = cur; return true; }
    return false;
}
