//TimeFilteredEA.mq5                    
#property description "Time-Filtered EA with streamlined CTrade usage."
#property copyright   "Clemence Benjamin"
#property version     "1.0"

#include <Trade/Trade.mqh>
#include <Trade/SymbolInfo.mqh>
#include <SessionVisualizer.mqh>
#include <TimeFilters.mqh>

input int    InpGMTOffsetHours = 0;     // GMT offset for session alignment
input bool   InpDrawSessions    = true; // Enable session visualization
input int    InpLookbackDays    = 5;    // Days to draw sessions
input double InpLotSize         = 0.01; // Fixed lot size
input int    InpStopLossPips    = 50;   // SL in pips (0 = none; TP auto-set to 2x if >0)
input int    InpTakeProfitPips  = 0;    // TP in pips (0 = auto 2x SL if SL>0)
input int    InpMagicNumber     = 12345; // Magic number for trades
input int    InpDeviationPips   = 10;   // Max slippage in pips

CTrade              trade;
CSymbolInfo         gSymbolInfo;
CSessionVisualizer  gSV("TF_SESS_");
CTimeFilterContext  gCTX;

// MA handles
int       gFastMAHandle   = INVALID_HANDLE;
int       gSlowMAHandle   = INVALID_HANDLE;
datetime  gLastSignalBar  = 0;

//+------------------------------------------------------------------+
//| Professional Trade Manager Wrapper                               |
//+------------------------------------------------------------------+
class CTradeManager
{
private:
   CTrade* m_trade;
   int     m_magic;
   int     m_deviation;
   double  m_minVolume;
   double  m_maxVolume;
   double  m_volumeStep;
   int     m_digits;
   double  m_point;

public:
   CTradeManager() : m_trade(NULL), m_magic(0), m_deviation(0) {}
   ~CTradeManager() {}

   bool Init(int magic, int deviation)
   {
      m_trade = new CTrade();
      if(m_trade == NULL) return false;
      
      m_magic = magic;
      m_deviation = deviation;
      
      m_trade.SetExpertMagicNumber(m_magic);
      m_trade.SetDeviationInPoints(m_deviation);
      m_trade.LogLevel(LOG_LEVEL_ERRORS);  // Match reference: log errors only
      
      // Cache symbol props (no filling set - default to broker/symbol)
      m_minVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
      m_maxVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
      m_volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
      m_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
      m_point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
      
      Print("TradeManager initialized: Magic=", m_magic, " Deviation=", m_deviation, "pts (default filling)");
      return true;
   }

   void Deinit()
   {
      if(m_trade != NULL)
      {
         delete m_trade;
         m_trade = NULL;
      }
   }

   bool IsValidVolume(double volume)
   {
      if(volume < m_minVolume || volume > m_maxVolume) return false;
      double normalized = NormalizeDouble(volume / m_volumeStep, 0) * m_volumeStep;
      return (MathAbs(volume - normalized) < m_point);
   }

   double NormalizeVolume(double volume)
   {
      return NormalizeDouble(MathMax(m_minVolume, MathMin(m_maxVolume, NormalizeDouble(volume / m_volumeStep, 0) * m_volumeStep)), 2);
   }

   bool ValidateAccount()
   {
      double balance = AccountInfoDouble(ACCOUNT_BALANCE);
      double equity = AccountInfoDouble(ACCOUNT_EQUITY);
      double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
      
      if(balance <= 0 || equity <= 0 || freeMargin < 0)
      {
         Print("TradeManager: Invalid account state - Balance=", balance, " Equity=", equity, " FreeMargin=", freeMargin);
         return false;
      }
      
      Print("TradeManager: Account validated - Balance=", balance, " FreeMargin=", freeMargin);
      return true;
   }

   bool ExecuteBuy(double volume, double sl = 0, double tp = 0, string comment = "")
   {
      if(!ValidateAccount()) return false;
      
      volume = NormalizeVolume(volume);
      if(!IsValidVolume(volume))
      {
         Print("TradeManager: Invalid volume ", volume);
         return false;
      }
      
      gSymbolInfo.Name(_Symbol);
      gSymbolInfo.RefreshRates();
      double ask = gSymbolInfo.Ask();
      double bid = gSymbolInfo.Bid();
      double spread = ask - bid;
      
      double price = NormalizeDouble(ask, m_digits);
      double stoploss = 0.0;
      double takeprofit = 0.0;
      
      // Compute SL/TP with spread check (match reference)
      double pipValue = (m_digits == 3 || m_digits == 5) ? m_point * 10 : m_point;
      if(InpStopLossPips > 0)
      {
         if(spread >= InpStopLossPips * m_point)
         {
            PrintFormat("StopLoss (%d points) <= current spread = %.0f points. Spread value will be used", InpStopLossPips, spread / m_point);
            stoploss = NormalizeDouble(price - spread, m_digits);
         }
         else
         {
            stoploss = NormalizeDouble(price - InpStopLossPips * pipValue, m_digits);
         }
         // Auto-set TP to 2x SL distance for 1:2 RR if TP=0
         if(InpTakeProfitPips == 0)
         {
            takeprofit = NormalizeDouble(price + (InpStopLossPips * 2 * pipValue), m_digits);
            Print("TradeManager: Auto-set TP for 1:2 RR: ", takeprofit);
         }
         else
         {
            if(spread >= InpTakeProfitPips * m_point)
            {
               PrintFormat("TakeProfit (%d points) < current spread = %.0f points. Spread value will be used", InpTakeProfitPips, spread / m_point);
               takeprofit = NormalizeDouble(price + spread, m_digits);
            }
            else
            {
               takeprofit = NormalizeDouble(price + InpTakeProfitPips * pipValue, m_digits);
            }
         }
      }
      else if(InpTakeProfitPips > 0)
      {
         if(spread >= InpTakeProfitPips * m_point)
         {
            PrintFormat("TakeProfit (%d points) < current spread = %.0f points. Spread value will be used", InpTakeProfitPips, spread / m_point);
            takeprofit = NormalizeDouble(price + spread, m_digits);
         }
         else
         {
            takeprofit = NormalizeDouble(price + InpTakeProfitPips * pipValue, m_digits);
         }
      }
      
      // Ensure directions: for BUY, SL < price < TP
      if(stoploss > 0 && stoploss >= price)
      {
         Print("TradeManager: Invalid SL for BUY - resetting to 0");
         stoploss = 0;
      }
      if(takeprofit > 0 && takeprofit <= price)
      {
         Print("TradeManager: Invalid TP for BUY - resetting to 0");
         takeprofit = 0;
      }
      
      // Adjust for min distance if needed (optional, as reference doesn't, but keeps for safety)
      long stopsLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
      if(stopsLevel > 0)
      {
         double minDist = stopsLevel * m_point;
         if(stoploss > 0)
         {
            double dist = price - stoploss;
            if(dist < minDist)
            {
               stoploss = NormalizeDouble(price - minDist, m_digits);
               Print("TradeManager: SL adjusted to min dist: ", stoploss);
            }
         }
         if(takeprofit > 0)
         {
            double dist = takeprofit - price;
            if(dist < minDist)
            {
               takeprofit = NormalizeDouble(price + minDist, m_digits);
               Print("TradeManager: TP adjusted to min dist: ", takeprofit);
            }
         }
      }
      
      Print("TradeManager: Executing BUY - Entry=", price, " Vol=", volume, " SL=", stoploss, " TP=", takeprofit);
      
      ResetLastError();  // Clear any prior errors (e.g., from objects/indicators)
      bool result = m_trade.Buy(volume, _Symbol, price, stoploss, takeprofit, comment);
      
      if(!result)
      {
         uint retcode = m_trade.ResultRetcode();
         string ret_desc = m_trade.ResultRetcodeDescription();
         PrintFormat("Failed %s buy %G at %G (sl=%G tp=%G) Retcode=%u (%s) MQL Error=%d", _Symbol, volume, price, stoploss, takeprofit, retcode, ret_desc, GetLastError());
         m_trade.PrintResult();
         Print("   ");
      }
      else
      {
         Print("TradeManager: BUY success - Deal=", m_trade.ResultDeal(), " Price=", m_trade.ResultPrice());
      }
      
      return result;
   }
};

// Global Trade Manager
CTradeManager gTradeMgr;

//+------------------------------------------------------------------+
//| OnInit                                                           |
//+------------------------------------------------------------------+
int OnInit()
{
   Print("=== TIMEFILTEREDEA INITIALIZATION (v1.0) ===");
   
   // Time filter setup
   gCTX.AttachVisualizer(gSV);
   gCTX.SetGMTOffset(InpGMTOffsetHours);
   if(InpDrawSessions)
      gSV.RefreshSessions(InpLookbackDays);
   
   // Initialize Trade Manager (simple CTrade setup matching reference)
   if(!gTradeMgr.Init(InpMagicNumber, InpDeviationPips))
   {
      Print("FAIL: TradeManager initialization failed");
      return INIT_FAILED;
   }
   
   // Validate account early
   if(!gTradeMgr.ValidateAccount())
   {
      Print("FAIL: Account validation failed - Check deposit in tester");
      return INIT_FAILED;
   }
   
   // MA indicators
   gFastMAHandle = iMA(_Symbol, _Period, 9, 0, MODE_EMA, PRICE_CLOSE);
   gSlowMAHandle = iMA(_Symbol, _Period, 21, 0, MODE_EMA, PRICE_CLOSE);
   
   if(gFastMAHandle == INVALID_HANDLE || gSlowMAHandle == INVALID_HANDLE)
   {
      Print("FAIL: MA handles creation failed");
      return INIT_FAILED;
   }
   
   Print("SUCCESS: TimeFiltered EA ready - Clean CTrade integration active (default filling)");
   Print("RR Logic: SL=", InpStopLossPips, " pips; TP=", (InpStopLossPips > 0 && InpTakeProfitPips == 0 ? InpStopLossPips * 2 : InpTakeProfitPips), " pips (1:2 auto if TP=0)");
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| OnDeinit                                                         |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(gFastMAHandle != INVALID_HANDLE) IndicatorRelease(gFastMAHandle);
   if(gSlowMAHandle != INVALID_HANDLE) IndicatorRelease(gSlowMAHandle);
   gTradeMgr.Deinit();
   gSV.ClearAll();
}

//+------------------------------------------------------------------+
//| Signal Detection: EMA Crossover                                  |
//+------------------------------------------------------------------+
bool EntrySignalDetected()
{
   double fast[2], slow[2];
   if(CopyBuffer(gFastMAHandle, 0, 0, 2, fast) != 2 || CopyBuffer(gSlowMAHandle, 0, 0, 2, slow) != 2)
      return false;
   
   bool crossover = (fast[1] <= slow[1] && fast[0] > slow[0]);
   if(!crossover) return false;
   
   datetime barTime = iTime(_Symbol, _Period, 0);
   if(barTime == gLastSignalBar) return false;
   gLastSignalBar = barTime;
   return true;
}

//+------------------------------------------------------------------+
//| Position Check                                                   |
//+------------------------------------------------------------------+
bool HasOpenPosition()
{
   int total = PositionsTotal();
   for(int i = 0; i < total; i++)
   {
      if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
         return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| OnTick                                                           |
//+------------------------------------------------------------------+
void OnTick()
{
   if(InpDrawSessions)
      gSV.RefreshSessions(InpLookbackDays);
   
   if(!IsTradingAllowed(gCTX))
   {
      Comment("TimeFiltered EA: Trading OFF");
      return;
   }
   
   Comment("TimeFiltered EA: Trading ON | Positions: ", PositionsTotal());
   
   if(!HasOpenPosition() && EntrySignalDetected())
   {
      Print("=== SIGNAL: EMA Crossover Detected ===");
      gTradeMgr.ExecuteBuy(InpLotSize, 0, 0, "Buy");
   }
}
//+------------------------------------------------------------------+