#include "CLogger.mqh"
#include "ConsoleLogHandler.mqh"
#include "FileLogHandler.mqh"

// Input parameters
input int      MagicNumber = 654321;         // EA Magic Number
input double   LotSize     = 0.01;           // Fixed lot size
input int      StopLossPips = 50;            // Stop Loss in pips
input int      TakeProfitPips = 100;         // Take Profit in pips
input LogLevel ConsoleLogLevel = LOG_LEVEL_INFO; // Minimum level for console output
input LogLevel FileLogLevel = LOG_LEVEL_DEBUG;   // Minimum level for file output

// Global logger pointer (optional, can use CLogger::Instance() directly)
CLogger *g_logger = NULL;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Get the logger instance
   g_logger = CLogger::Instance();
   if(CheckPointer(g_logger) == POINTER_INVALID)
     {
      Print("Critical Error: Failed to get Logger instance!");
      return(INIT_FAILED);
     }

//--- Set EA information for context in logs
   g_logger.SetExpertInfo(MagicNumber, MQL5InfoString(MQL5_PROGRAM_NAME));

//--- Configure Handlers ---
   // 1. Console Handler
   ConsoleLogHandler *console_handler = new ConsoleLogHandler(ConsoleLogLevel);
   if(CheckPointer(console_handler) != POINTER_INVALID)
     {
      // Optionally customize format
      // console_handler.SetFormat("[{level}] {message}"); 
      if(!g_logger.AddHandler(console_handler))
        {
         Print("Warning: Failed to add ConsoleLogHandler.");
         delete console_handler; // Clean up if not added
        }
     }
   else
     {
      Print("Warning: Failed to create ConsoleLogHandler.");
     }

   // 2. File Handler
   string log_prefix = MQL5InfoString(MQL5_PROGRAM_NAME) + "_" + IntegerToString(MagicNumber);
   FileLogHandler *file_handler = new FileLogHandler("MQL5/Logs/EA_Logs", // Directory relative to MQL5/Files
                                                   log_prefix,          // File name prefix
                                                   FileLogLevel,        // Minimum level to log to file
                                                   "[{time}] {level} ({origin}): {message}", // Format
                                                   2048, // Max file size in KB (e.g., 2MB)
                                                   10);  // Max number of log files to keep
   if(CheckPointer(file_handler) != POINTER_INVALID)
     {
      if(!g_logger.AddHandler(file_handler))
        {
         Print("Warning: Failed to add FileLogHandler.");
         delete file_handler; // Clean up if not added
        }
     }
   else
     {
      Print("Warning: Failed to create FileLogHandler.");
     }

//--- Log initialization message
   g_logger.Info(__FUNCTION__, "Expert Advisor initialized successfully.");
   g_logger.Debug(__FUNCTION__, StringFormat("Settings: Lots=%.2f, SL=%d, TP=%d, ConsoleLevel=%s, FileLevel=%s",
                                           LotSize, StopLossPips, TakeProfitPips, 
                                           EnumToString(ConsoleLogLevel),
                                           EnumToString(FileLogLevel)));

//--- succeed
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Log deinitialization
   if(CheckPointer(g_logger) != POINTER_INVALID)
     {
      string reason_str = "Unknown reason";
      switch(reason)
        {
         case REASON_ACCOUNT: reason_str = "Account change"; break;
         case REASON_CHARTCHANGE: reason_str = "Chart symbol or period change"; break;
         case REASON_CHARTCLOSE: reason_str = "Chart closed"; break;
         case REASON_PARAMETERS: reason_str = "Input parameters changed"; break;
         case REASON_RECOMPILE: reason_str = "Recompiled"; break;
         case REASON_REMOVE: reason_str = "EA removed from chart"; break;
         case REASON_TEMPLATE: reason_str = "Template applied"; break;
         case REASON_CLOSE: reason_str = "Terminal closed"; break;
        }
      g_logger.Info(__FUNCTION__, "Expert Advisor shutting down. Reason: " + reason_str + " (" + IntegerToString(reason) + ")");
      
      // Release the logger instance (this calls Shutdown() on all handlers)
      CLogger::Release();
      g_logger = NULL; // Set pointer to NULL after release
     }
   else
     {
      Print("Logger instance was already invalid during Deinit.");
     }
//--- Print to standard log just in case logger failed
   Print(MQL5InfoString(MQL5_PROGRAM_NAME) + ": Deinitialized. Reason code: " + IntegerToString(reason));
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Ensure logger is valid
   if(CheckPointer(g_logger) == POINTER_INVALID)
     {
      // Attempt to re-initialize logger if it became invalid unexpectedly
      // This is defensive coding, ideally it shouldn't happen if OnInit succeeded.
      Print("Error: Logger instance invalid in OnTick! Attempting re-init...");
      if(OnInit() != INIT_SUCCEEDED)
        {
         Print("Critical Error: Failed to re-initialize logger in OnTick. Stopping EA.");
         ExpertRemove(); // Stop the EA
         return;
        }
     }

//--- Log tick arrival
   MqlTick latest_tick;
   if(SymbolInfoTick(_Symbol, latest_tick))
     {
      g_logger.Debug(__FUNCTION__, StringFormat("New Tick: Time=%s, Bid=%.5f, Ask=%.5f, Volume=%d",
                                             TimeToString(latest_tick.time, TIME_DATE|TIME_SECONDS),
                                             latest_tick.bid, latest_tick.ask, (int)latest_tick.volume_real));
     }
   else
     {
      g_logger.Warn(__FUNCTION__, "Failed to get latest tick info. Error: " + IntegerToString(GetLastError()));
     }

//--- Example Logic: Check for a simple crossover
   // Note: Use more robust indicator handling in a real EA
   double ma_fast[], ma_slow[];
   int copied_fast = CopyBuffer(iMA(_Symbol, _Period, 10, 0, MODE_SMA, PRICE_CLOSE), 0, 0, 3, ma_fast);
   int copied_slow = CopyBuffer(iMA(_Symbol, _Period, 50, 0, MODE_SMA, PRICE_CLOSE), 0, 0, 3, ma_slow);
   
   if(copied_fast < 3 || copied_slow < 3)
     {
      g_logger.Warn(__FUNCTION__, "Failed to copy enough indicator data.");
      return; // Not enough data yet
     }
     
   // ArraySetAsSeries might be needed depending on how you access indices
   // ArraySetAsSeries(ma_fast, true);
   // ArraySetAsSeries(ma_slow, true);
   
   bool cross_up = ma_fast[1] > ma_slow[1] && ma_fast[2] <= ma_slow[2];
   bool cross_down = ma_fast[1] < ma_slow[1] && ma_fast[2] >= ma_slow[2];

   if(cross_up)
     {
      g_logger.Info(__FUNCTION__, "MA Cross Up detected. Potential Buy Signal.");
      // --- Add trading logic here ---
      // Example: SendBuyOrder();
     }
   else if(cross_down)
     {
      g_logger.Info(__FUNCTION__, "MA Cross Down detected. Potential Sell Signal.");
      // --- Add trading logic here ---
      // Example: SendSellOrder();
     }
     
   // Log account info periodically
   static datetime last_account_log = 0;
   if(TimeCurrent() - last_account_log >= 3600) // Log every hour
     {
      g_logger.Info(__FUNCTION__, StringFormat("Account Update: Balance=%.2f, Equity=%.2f, Margin=%.2f, FreeMargin=%.2f",
                                            AccountInfoDouble(ACCOUNT_BALANCE),
                                            AccountInfoDouble(ACCOUNT_EQUITY),
                                            AccountInfoDouble(ACCOUNT_MARGIN),
                                            AccountInfoDouble(ACCOUNT_MARGIN_FREE)));
      last_account_log = TimeCurrent();
     }
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Ensure logger is valid
   if(CheckPointer(g_logger) == POINTER_INVALID) return;
   
//--- Log chart events
   string event_name = "Unknown Chart Event";
   switch(id)
     {
      case CHARTEVENT_KEYDOWN: event_name = "KeyDown"; break;
      case CHARTEVENT_MOUSE_MOVE: event_name = "MouseMove"; break;
      // Add other CHARTEVENT cases as needed
      case CHARTEVENT_OBJECT_CLICK: event_name = "ObjectClick"; break;
      case CHARTEVENT_CUSTOM+1: event_name = "CustomEvent_1"; break; // Example custom event
     }
     
   g_logger.Debug(__FUNCTION__, StringFormat("Chart Event: ID=%d (%s), lparam=%d, dparam=%.5f, sparam='%s'",
                                           id, event_name, lparam, dparam, sparam));
  }
//+------------------------------------------------------------------+
