//+------------------------------------------------------------------+
//|                                             CNavigatorDialog.mqh |
//|                                               Christian Benjamin |
//|                          https://www.mql5.com/en/users/lynnchris |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Christian Benjamin"
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.3"
#property strict

#ifndef CNAVIGATORDIALOG_MQH
#define CNAVIGATORDIALOG_MQH

//--- Standard MetaTrader 5 Controls library
#include <Controls\Dialog.mqh>   // base class for resizable dialogs
#include <Controls\Edit.mqh>     // text input fields
#include <Controls\Button.mqh>   // clickable buttons
#include <Controls\Label.mqh>    // static text labels
#include <Controls\ListView.mqh> // scrollable list for bookmarks

//--- Our own bookmark persistence class (handles CSV file I/O)
#include "BookmarkStorage.mqh"

//+------------------------------------------------------------------+
//| History Navigator dialog                                         |
//| Inherits from CAppDialog, giving it standard window behaviour    |
//| (drag, close button, etc.)                                       |
//+------------------------------------------------------------------+
class CNavigatorDialog : public CAppDialog
  {
private:
   //--- UI Controls – grouped by function

   //--- Date/time input section
   CLabel            m_lblTitle;
   CLabel            m_lblDay;
   CLabel            m_lblMonth;
   CLabel            m_lblYear;
   CLabel            m_lblHour;
   CLabel            m_lblMinute;
   CLabel            m_lblStatus;

   CEdit             m_editDay;
   CEdit             m_editMonth;
   CEdit             m_editYear;
   CEdit             m_editHour;
   CEdit             m_editMinute;

   CButton           m_btnGo;
   CButton           m_btnToday;

   //--- Bookmark management section
   CLabel            m_lblBookmarkGroup;
   CLabel            m_lblBookmarkName;
   CLabel            m_lblBookmarkNotes;

   CEdit             m_editBookmarkName;
   CEdit             m_editBookmarkNotes;

   CButton           m_btnSaveBookmark;

   CListView         m_listBookmarks;

   CButton           m_btnGoToBookmark;
   CButton           m_btnRenameBookmark;
   CButton           m_btnDeleteBookmark;

   //--- Layout constants
   int               m_margin;
   int               m_rowHeight;
   int               m_spacing;

   int               m_labelWidth;
   int               m_editWidth;
   int               m_buttonWidth;

   int               m_clientWidth;
   int               m_clientHeight;

   //--- Coordinates of the bookmark list (stored to recreate it later)
   int               m_listX1, m_listY1, m_listX2, m_listY2;
   int               m_listHeight;

   //--- State & data
   CBookmarkStorage  m_bookmarkStorage;


   int               m_selectedIndex;

   datetime          m_lastNavigatedTime;

   //--- Event map – connects UI events to member functions
                     EVENT_MAP_BEGIN(CNavigatorDialog)
                     ON_EVENT(ON_CLICK, m_btnGo,             OnGoClick)
                     ON_EVENT(ON_CLICK, m_btnToday,          OnTodayClick)
                     ON_EVENT(ON_CLICK, m_btnSaveBookmark,   OnSaveBookmark)
                     ON_EVENT(ON_CLICK, m_btnGoToBookmark,   OnGoToBookmark)
                     ON_EVENT(ON_CLICK, m_btnDeleteBookmark, OnDeleteBookmark)
                     ON_EVENT(ON_CHANGE, m_listBookmarks,    OnListSelect)
                     EVENT_MAP_END(CAppDialog)

public:
                     CNavigatorDialog(void);
                    ~CNavigatorDialog(void);

   //--- Override the base Create to set up our custom controls
   virtual bool      Create(const long chart,
                            const string name,
                            const int subwin,
                            const int x1,
                            const int y1,
                            const int x2,
                            const int y2) override;

   //--- Public button handlers (called by the event map)
   void              OnGoClick(void);
   void              OnTodayClick(void);
   void              OnSaveBookmark(void);
   void              OnGoToBookmark(void);
   void              OnDeleteBookmark(void);
   void              OnListSelect(void);

private:
   bool              CreateControls(void);

   void              UpdateStatus(string text, color clr = clrBlack);
   void              SetStatusReady(void);

   bool              ValidateInput(int &day, int &month, int &year,
                                   int &hour, int &minute);
   bool              IsValidDate(int day, int month, int year);

   void              NavigateToDate(void);
   void              NavigateToDateTime(datetime target);
   void              ReturnToToday(void);

   int               FindBarShift(datetime target, bool exact = false);
   void              CenterChartOnBar(int barIndex);

   void              RefreshBookmarkList(void);
   void              LoadBookmarks(void);

   void              ClearSelection(void);
   bool              CaptureSelectedBookmark(void);
   bool              SelectBookmarkIndex(const int index);

   string            BookmarkDisplay(const SBookmark &bm) const;
  };

//+------------------------------------------------------------------+
//| Constructor – initialises layout constants and state             |
//+------------------------------------------------------------------+
CNavigatorDialog::CNavigatorDialog(void)
   :
   m_margin(10),
   m_rowHeight(24),
   m_spacing(6),
   m_labelWidth(55),
   m_editWidth(50),
   m_buttonWidth(150),
   m_clientWidth(0),
   m_clientHeight(0),
   m_listX1(0),
   m_listY1(0),
   m_listX2(0),
   m_listY2(0),
   m_listHeight(55),
   m_selectedIndex(-1),
   m_lastNavigatedTime(0)
  {
  }

//+------------------------------------------------------------------+
//| Destructor – nothing to free (all controls are child objects)    |
//+------------------------------------------------------------------+
CNavigatorDialog::~CNavigatorDialog(void)
  {
  }

//+------------------------------------------------------------------+
//| Creates the dialog window and all its child controls             |
//+------------------------------------------------------------------+
bool CNavigatorDialog::Create(const long chart,
                              const string name,
                              const int subwin,
                              const int x1,
                              const int y1,
                              const int x2,
                              const int y2)
  {
//--- First, let the base class create the window frame
   if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2))
      return(false);

//--- Store working area sizes for positioning controls
   m_clientWidth  = x2 - x1;
   m_clientHeight = y2 - y1;

//--- Build all child controls (labels, edits, buttons, list)
   if(!CreateControls())
      return(false);

//--- Pre‑fill the date/time fields with the current system time
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   m_editDay.Text(IntegerToString(dt.day));
   m_editMonth.Text(IntegerToString(dt.mon));
   m_editYear.Text(IntegerToString(dt.year));
   m_editHour.Text(IntegerToString(dt.hour));
   m_editMinute.Text(IntegerToString(dt.min));

//--- Load existing bookmarks from disk into memory and show them
   LoadBookmarks();
   SetStatusReady();

   return(true);
  }

//+------------------------------------------------------------------+
//| Lays out all controls with precise x/y coordinates.              |
//+------------------------------------------------------------------+
bool CNavigatorDialog::CreateControls(void)
  {
   int x1, y1, x2, y2;
   int currentY = m_margin;

//--- TITLE (centered, blue, larger font)
   x1 = m_margin;
   y1 = currentY;
   x2 = m_clientWidth - m_margin;
   y2 = y1 + m_rowHeight;

   if(!m_lblTitle.Create(m_chart_id, m_name + "Title", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblTitle.Text("History Navigator");
   m_lblTitle.FontSize(10);
   m_lblTitle.Color(clrBlue);
   if(!Add(GetPointer(m_lblTitle)))
      return(false);

   currentY += m_rowHeight + m_spacing;

//--- DAY input: label + edit
   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + m_labelWidth;
   y2 = y1 + m_rowHeight;
   if(!m_lblDay.Create(m_chart_id, m_name + "LblDay", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblDay.Text("Day:");
   if(!Add(GetPointer(m_lblDay)))
      return(false);

   x1 = x2 + 5;
   y1 = currentY;
   x2 = x1 + m_editWidth;
   y2 = y1 + m_rowHeight;
   if(!m_editDay.Create(m_chart_id, m_name + "EditDay", m_subwin, x1, y1, x2, y2))
      return(false);
   if(!Add(GetPointer(m_editDay)))
      return(false);

   currentY += m_rowHeight + m_spacing;

//--- MONTH (same pattern)
   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + m_labelWidth;
   y2 = y1 + m_rowHeight;
   if(!m_lblMonth.Create(m_chart_id, m_name + "LblMonth", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblMonth.Text("Month:");
   if(!Add(GetPointer(m_lblMonth)))
      return(false);

   x1 = x2 + 5;
   y1 = currentY;
   x2 = x1 + m_editWidth;
   y2 = y1 + m_rowHeight;
   if(!m_editMonth.Create(m_chart_id, m_name + "EditMonth", m_subwin, x1, y1, x2, y2))
      return(false);
   if(!Add(GetPointer(m_editMonth)))
      return(false);

   currentY += m_rowHeight + m_spacing;

//--- YEAR (slightly wider edit because 4 digits)
   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + m_labelWidth;
   y2 = y1 + m_rowHeight;
   if(!m_lblYear.Create(m_chart_id, m_name + "LblYear", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblYear.Text("Year:");
   if(!Add(GetPointer(m_lblYear)))
      return(false);

   x1 = x2 + 5;
   y1 = currentY;
   x2 = x1 + m_editWidth + 10;
   y2 = y1 + m_rowHeight;
   if(!m_editYear.Create(m_chart_id, m_name + "EditYear", m_subwin, x1, y1, x2, y2))
      return(false);
   if(!Add(GetPointer(m_editYear)))
      return(false);

   currentY += m_rowHeight + m_spacing;

//--- HOUR
   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + m_labelWidth;
   y2 = y1 + m_rowHeight;
   if(!m_lblHour.Create(m_chart_id, m_name + "LblHour", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblHour.Text("Hour:");
   if(!Add(GetPointer(m_lblHour)))
      return(false);

   x1 = x2 + 5;
   y1 = currentY;
   x2 = x1 + m_editWidth;
   y2 = y1 + m_rowHeight;
   if(!m_editHour.Create(m_chart_id, m_name + "EditHour", m_subwin, x1, y1, x2, y2))
      return(false);
   if(!Add(GetPointer(m_editHour)))
      return(false);

   currentY += m_rowHeight + m_spacing;

//--- MINUTE
   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + m_labelWidth;
   y2 = y1 + m_rowHeight;
   if(!m_lblMinute.Create(m_chart_id, m_name + "LblMinute", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblMinute.Text("Minute:");
   if(!Add(GetPointer(m_lblMinute)))
      return(false);

   x1 = x2 + 5;
   y1 = currentY;
   x2 = x1 + m_editWidth;
   y2 = y1 + m_rowHeight;
   if(!m_editMinute.Create(m_chart_id, m_name + "EditMinute", m_subwin, x1, y1, x2, y2))
      return(false);
   if(!Add(GetPointer(m_editMinute)))
      return(false);

   currentY += m_rowHeight + m_spacing + 5;

//--- NAVIGATION BUTTONS (full width)
   int buttonHeight = m_rowHeight + 4;

   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + m_buttonWidth;
   y2 = y1 + buttonHeight;
   if(!m_btnGo.Create(m_chart_id, m_name + "BtnGo", m_subwin, x1, y1, x2, y2))
      return(false);
   m_btnGo.Text("GO TO DATE");
   if(!Add(GetPointer(m_btnGo)))
      return(false);

   currentY += buttonHeight + m_spacing;

   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + m_buttonWidth;
   y2 = y1 + buttonHeight;
   if(!m_btnToday.Create(m_chart_id, m_name + "BtnToday", m_subwin, x1, y1, x2, y2))
      return(false);
   m_btnToday.Text("RETURN TO TODAY");
   if(!Add(GetPointer(m_btnToday)))
      return(false);

   currentY += buttonHeight + m_spacing + 10;

//--- BOOKMARK SECTION HEADER (grey, smaller font)
   x1 = m_margin;
   y1 = currentY;
   x2 = m_clientWidth - m_margin;
   y2 = y1 + m_rowHeight;
   if(!m_lblBookmarkGroup.Create(m_chart_id, m_name + "BmGroup", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblBookmarkGroup.Text("Bookmarks");
   m_lblBookmarkGroup.FontSize(9);
   m_lblBookmarkGroup.Color(clrGray);
   if(!Add(GetPointer(m_lblBookmarkGroup)))
      return(false);

   currentY += m_rowHeight + 4;

//--- BOOKMARK NAME (label + edit)
   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + 45;
   y2 = y1 + m_rowHeight;
   if(!m_lblBookmarkName.Create(m_chart_id, m_name + "BmNameLbl", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblBookmarkName.Text("Name:");
   if(!Add(GetPointer(m_lblBookmarkName)))
      return(false);

   x1 = x2 + 5;
   y1 = currentY;
   x2 = m_clientWidth - m_margin;
   y2 = y1 + m_rowHeight;
   if(!m_editBookmarkName.Create(m_chart_id, m_name + "BmName", m_subwin, x1, y1, x2, y2))
      return(false);
   if(!Add(GetPointer(m_editBookmarkName)))
      return(false);

   currentY += m_rowHeight + 4;

//--- BOOKMARK NOTES
   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + 45;
   y2 = y1 + m_rowHeight;
   if(!m_lblBookmarkNotes.Create(m_chart_id, m_name + "BmNotesLbl", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblBookmarkNotes.Text("Notes:");
   if(!Add(GetPointer(m_lblBookmarkNotes)))
      return(false);

   x1 = x2 + 5;
   y1 = currentY;
   x2 = m_clientWidth - m_margin;
   y2 = y1 + m_rowHeight;
   if(!m_editBookmarkNotes.Create(m_chart_id, m_name + "BmNotes", m_subwin, x1, y1, x2, y2))
      return(false);
   if(!Add(GetPointer(m_editBookmarkNotes)))
      return(false);

   currentY += m_rowHeight + 4;

//--- SAVE BOOKMARK button (right‑aligned)
   x1 = m_clientWidth - m_margin - 100;
   y1 = currentY;
   x2 = m_clientWidth - m_margin;
   y2 = y1 + buttonHeight;
   if(!m_btnSaveBookmark.Create(m_chart_id, m_name + "BtnSave", m_subwin, x1, y1, x2, y2))
      return(false);
   m_btnSaveBookmark.Text("Save");
   if(!Add(GetPointer(m_btnSaveBookmark)))
      return(false);

   currentY += buttonHeight + 4;

//--- BOOKMARK LIST (only ~55px tall; shows ~2 rows)
   m_listHeight = 55;
   m_listX1 = m_margin;
   m_listY1 = currentY;
   m_listX2 = m_clientWidth - m_margin;
   m_listY2 = currentY + m_listHeight;
   if(!m_listBookmarks.Create(m_chart_id, m_name + "BmList", m_subwin,
                              m_listX1, m_listY1, m_listX2, m_listY2))
      return(false);
   if(!Add(GetPointer(m_listBookmarks)))
      return(false);

   currentY += m_listHeight + 3;

//--- ACTION BUTTONS (Go To and Delete) – side‑by‑side
   int btnW = (m_clientWidth - 2 * m_margin - 6) / 2;

   x1 = m_margin;
   y1 = currentY;
   x2 = x1 + btnW;
   y2 = y1 + buttonHeight;
   if(!m_btnGoToBookmark.Create(m_chart_id, m_name + "BtnGoTo", m_subwin, x1, y1, x2, y2))
      return(false);
   m_btnGoToBookmark.Text("Go To");
   if(!Add(GetPointer(m_btnGoToBookmark)))
      return(false);

   x1 = x2 + 6;
   y1 = currentY;
   x2 = x1 + btnW;
   y2 = y1 + buttonHeight;
   if(!m_btnDeleteBookmark.Create(m_chart_id, m_name + "BtnDelete", m_subwin, x1, y1, x2, y2))
      return(false);
   m_btnDeleteBookmark.Text("Delete");
   if(!Add(GetPointer(m_btnDeleteBookmark)))
      return(false);

   currentY += buttonHeight + 2;

//--- STATUS BAR (at the very bottom)
   x1 = m_margin;
   y1 = currentY;
   x2 = m_clientWidth - m_margin;
   y2 = y1 + m_rowHeight;
   if(!m_lblStatus.Create(m_chart_id, m_name + "Status", m_subwin, x1, y1, x2, y2))
      return(false);
   m_lblStatus.Text("Ready");
   m_lblStatus.Color(clrBlack);
   if(!Add(GetPointer(m_lblStatus)))
      return(false);

   return(true);
  }

//+------------------------------------------------------------------+
//| Handles the GO TO DATE button click – triggers navigation        |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnGoClick(void)
  {
   NavigateToDate();
  }

//+------------------------------------------------------------------+
//| Handles the RETURN TO TODAY button – scrolls to the latest bar   |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnTodayClick(void)
  {
   ReturnToToday();
  }

//+------------------------------------------------------------------+
//| Saves the current chart position as a new bookmark.              |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnSaveBookmark(void)
  {
   string name = m_editBookmarkName.Text();
   StringTrimLeft(name);
   StringTrimRight(name);

   if(name == "")
     {
      UpdateStatus("Please enter a bookmark name.", clrRed);
      return;
     }

   string symbol = Symbol();
   ENUM_TIMEFRAMES tf = (ENUM_TIMEFRAMES)Period();

   datetime dt = m_lastNavigatedTime;

//--- If the user hasn't navigated yet, use the bar in the middle of the screen
   if(dt <= 0)
     {
      int visibleBars = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
      int firstVisible = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);

      if(visibleBars < 1)
         visibleBars = 50;

      int centerShift = firstVisible + visibleBars / 2;
      dt = iTime(symbol, tf, centerShift);
     }

   if(dt <= 0)
     {
      UpdateStatus("Unable to determine current chart time.", clrRed);
      return;
     }

//--- Build the bookmark structure
   SBookmark bm;
   bm.name       = name;
   bm.symbol     = symbol;
   bm.timeframe  = tf;
   bm.dateTime   = dt;
   bm.notes      = m_editBookmarkNotes.Text();

//--- Debug output to the Experts log
   PrintFormat("HistoryNavigator: saving bookmark '%s' -> %s %s %s",
               bm.name, bm.symbol, EnumToString(bm.timeframe),
               TimeToString(bm.dateTime, TIME_DATE | TIME_MINUTES));

//--- Ask the storage class to add it and write to file
   if(!m_bookmarkStorage.AddBookmark(bm))
     {
      UpdateStatus("Failed to save bookmark.", clrRed);
      return;
     }

//--- The newly added bookmark is at the end of the storage array
   int newIndex = m_bookmarkStorage.Count() - 1;

   RefreshBookmarkList();

//--- Auto‑select the newly created bookmark so the user sees it
   if(newIndex >= 0)
      SelectBookmarkIndex(newIndex);

   UpdateStatus("Bookmark saved.", clrGreen);

//--- Clear the input fields but keep the selection
   m_editBookmarkName.Text("");
   m_editBookmarkNotes.Text("");

   if(newIndex >= 0)
      SelectBookmarkIndex(newIndex);
  }

//+------------------------------------------------------------------+
//| Verifies that a bookmark is currently selected.                  |
//+------------------------------------------------------------------+
bool CNavigatorDialog::CaptureSelectedBookmark(void)
  {
   if(m_selectedIndex < 0 || m_selectedIndex >= m_bookmarkStorage.Count())
      return(false);
   return(true);
  }

//+------------------------------------------------------------------+
//| Programmatically selects a bookmark by its storage index.        |
//+------------------------------------------------------------------+
bool CNavigatorDialog::SelectBookmarkIndex(const int index)
  {
   if(index < 0 || index >= m_bookmarkStorage.Count())
     {
      m_selectedIndex = -1;
      return(false);
     }

   m_selectedIndex = index;

//--- Tell the list control to highlight that row
   m_listBookmarks.Select(index);

//--- Fill the edit fields with the bookmark's details
   SBookmark bm = m_bookmarkStorage.GetBookmark(index);
   m_editBookmarkName.Text(bm.name);
   m_editBookmarkNotes.Text(bm.notes);

   PrintFormat("HistoryNavigator: selected index=%d name='%s'",
               index, bm.name);

   return(true);
  }

//+------------------------------------------------------------------+
//| Navigates to the location stored in the selected bookmark.       |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnGoToBookmark(void)
  {
   if(!CaptureSelectedBookmark())
     {
      UpdateStatus("Please select a bookmark first.", clrRed);
      return;
     }

   SBookmark bm = m_bookmarkStorage.GetBookmark(m_selectedIndex);

   if(bm.name == "" || bm.symbol == "" || bm.dateTime <= 0)
     {
      UpdateStatus("Invalid bookmark selected.", clrRed);
      return;
     }

   PrintFormat("HistoryNavigator: opening bookmark '%s' -> %s %s %s",
               bm.name, bm.symbol, EnumToString(bm.timeframe),
               TimeToString(bm.dateTime, TIME_DATE | TIME_MINUTES));

//--- If the symbol or timeframe differ, switch the chart
   if(!ChartSetSymbolPeriod(0, bm.symbol, bm.timeframe))
     {
      UpdateStatus("Failed to switch symbol/timeframe.", clrRed);
      return;
     }

//--- Wait a moment for MT5 to load the new history data
   Sleep(100);

//--- Now navigate to the exact datetime within that new context
   NavigateToDateTime(bm.dateTime);

   UpdateStatus("Navigated to: " + bm.name, clrGreen);
  }

//+------------------------------------------------------------------+
//| Deletes the selected bookmark after user confirmation.           |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnDeleteBookmark(void)
  {
   if(!CaptureSelectedBookmark())
     {
      UpdateStatus("Please select a bookmark first.", clrRed);
      return;
     }

   int index = m_selectedIndex;
   SBookmark bm = m_bookmarkStorage.GetBookmark(index);

   if(bm.name == "")
     {
      UpdateStatus("Invalid bookmark selected.", clrRed);
      return;
     }

//--- Ask the user to confirm
   string message = "Delete bookmark '" + bm.name + "'?";
   int answer = MessageBox(message, "Confirm Delete", MB_YESNO);

   if(answer != IDYES)
      return;

   PrintFormat("HistoryNavigator: deleting selected index=%d name='%s'",
               index, bm.name);

   if(!m_bookmarkStorage.DeleteBookmark(index))
     {
      UpdateStatus("Failed to delete bookmark.", clrRed);
      return;
     }

//--- Clear stale selection before rebuilding the list
   m_selectedIndex = -1;

   RefreshBookmarkList();

//--- Select the nearest remaining row (if any)
   int remaining = m_bookmarkStorage.Count();

   if(remaining > 0)
     {
      int nextIndex = index;
      if(nextIndex >= remaining)
         nextIndex = remaining - 1;
      SelectBookmarkIndex(nextIndex);
     }
   else
     {
      ClearSelection();
     }

   UpdateStatus("Bookmark deleted.", clrGreen);
  }

//+------------------------------------------------------------------+
//| Called when the user clicks on a different row in the list.      |
//+------------------------------------------------------------------+
void CNavigatorDialog::OnListSelect(void)
  {
   int index = (int)m_listBookmarks.Value();

   if(index < 0 || index >= m_bookmarkStorage.Count())
     {
      m_selectedIndex = -1;
      return;
     }

   m_selectedIndex = index;

   SBookmark bm = m_bookmarkStorage.GetBookmark(index);
   m_editBookmarkName.Text(bm.name);
   m_editBookmarkNotes.Text(bm.notes);

   PrintFormat("HistoryNavigator: list selection index=%d name='%s'",
               index, bm.name);
  }

//+------------------------------------------------------------------+
//| Reads the date/time input fields, validates them, and calls      |
//+------------------------------------------------------------------+
void CNavigatorDialog::NavigateToDate(void)
  {
   int day, month, year, hour, minute;

   if(!ValidateInput(day, month, year, hour, minute))
      return;

   MqlDateTime dt;
   dt.year  = year;
   dt.mon   = month;
   dt.day   = day;
   dt.hour  = hour;
   dt.min   = minute;
   dt.sec   = 0;

   datetime target = StructToTime(dt);

   NavigateToDateTime(target);
  }

//+------------------------------------------------------------------+
//| Core navigation routine:                                         |
//+------------------------------------------------------------------+
void CNavigatorDialog::NavigateToDateTime(datetime target)
  {
   string symbol = Symbol();
   ENUM_TIMEFRAMES tf = (ENUM_TIMEFRAMES)Period();

   int totalBars = Bars(symbol, tf);

   if(totalBars <= 0)
     {
      UpdateStatus("No history data available.", clrRed);
      return;
     }

   datetime first = iTime(symbol, tf, totalBars - 1);
   datetime last  = iTime(symbol, tf, 0);

   if(first <= 0 || last <= 0)
     {
      UpdateStatus("Unable to read chart history.", clrRed);
      return;
     }

   if(target < first || target > last)
     {
      UpdateStatus("Outside chart history.", clrRed);
      return;
     }

   UpdateStatus("Searching...", clrBlue);

   int idx = FindBarShift(target, false);

   if(idx < 0)
     {
      UpdateStatus("Bar not found.", clrRed);
      return;
     }

   datetime actual = iTime(symbol, tf, idx);

   m_lastNavigatedTime = target;

   CenterChartOnBar(idx);

   PrintFormat("HistoryNavigator: target=%s actual=%s bar=%d symbol=%s timeframe=%s",
               TimeToString(target, TIME_DATE | TIME_MINUTES),
               TimeToString(actual, TIME_DATE | TIME_MINUTES),
               idx, symbol, EnumToString(tf));

   MqlDateTime fdt;
   TimeToStruct(actual, fdt);

   UpdateStatus(StringFormat("Found: %02d.%02d.%04d %02d:%02d",
                             fdt.day, fdt.mon, fdt.year, fdt.hour, fdt.min),
                clrGreen);
  }

//+------------------------------------------------------------------+
//| Returns the chart view to the current market (latest bar).       |
//+------------------------------------------------------------------+
void CNavigatorDialog::ReturnToToday(void)
  {
   ChartSetInteger(0, CHART_AUTOSCROLL, true);
   ChartSetInteger(0, CHART_SHIFT, true);

   ChartNavigate(0, CHART_END, 0);

   ChartRedraw();

   m_lastNavigatedTime = 0;

   UpdateStatus("Viewing current market", clrBlack);
  }

//+------------------------------------------------------------------+
//| Wrapper for iBarShift – uses the current symbol/timeframe.       |
//+------------------------------------------------------------------+
int CNavigatorDialog::FindBarShift(datetime target, bool exact)
  {
   int shift = iBarShift(Symbol(), Period(), target, exact);
   if(shift < 0)
      return(-1);
   return(shift);
  }

//+------------------------------------------------------------------+
//| Centres the chart so that the specified bar index is in the      |
//+------------------------------------------------------------------+
void CNavigatorDialog::CenterChartOnBar(int barIndex)
  {
   string symbol = Symbol();
   ENUM_TIMEFRAMES tf = (ENUM_TIMEFRAMES)Period();

   int total = Bars(symbol, tf);

   if(total <= 0 || barIndex < 0 || barIndex >= total)
      return;

//--- Disable autoscroll so we can set a fixed position
   ChartSetInteger(0, CHART_AUTOSCROLL, false);
   ChartSetInteger(0, CHART_SHIFT, false);

   int visible = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);

   if(visible < 1)
      visible = 50;

//--- shift = number of bars from the oldest to the leftmost visible.
//--- We want barIndex to be in the middle, so we start ~visible/2 bars earlier.
   int shift = (total - 1) - barIndex - (visible / 2);

   if(shift < 0)
      shift = 0;

   ChartNavigate(0, CHART_BEGIN, shift);

   ChartRedraw();

   Sleep(50);
  }

//+------------------------------------------------------------------+
//| Validates each numeric input field and checks calendar validity  |
//+------------------------------------------------------------------+
bool CNavigatorDialog::ValidateInput(int &day, int &month, int &year,
                                     int &hour, int &minute)
  {
   day    = (int)StringToInteger(m_editDay.Text());
   month  = (int)StringToInteger(m_editMonth.Text());
   year   = (int)StringToInteger(m_editYear.Text());
   hour   = (int)StringToInteger(m_editHour.Text());
   minute = (int)StringToInteger(m_editMinute.Text());

   if(day < 1 || day > 31)
     {
      UpdateStatus("Invalid day (1-31)", clrRed);
      return(false);
     }

   if(month < 1 || month > 12)
     {
      UpdateStatus("Invalid month (1-12)", clrRed);
      return(false);
     }

   if(year < 1970 || year > 3000)
     {
      UpdateStatus("Invalid year (1970-3000)", clrRed);
      return(false);
     }

   if(hour < 0 || hour > 23)
     {
      UpdateStatus("Invalid hour (0-23)", clrRed);
      return(false);
     }

   if(minute < 0 || minute > 59)
     {
      UpdateStatus("Invalid minute (0-59)", clrRed);
      return(false);
     }

   if(!IsValidDate(day, month, year))
     {
      UpdateStatus("Invalid calendar date.", clrRed);
      return(false);
     }

   return(true);
  }

//+------------------------------------------------------------------+
//| Checks whether a given day/month/year actually exists.           |
//+------------------------------------------------------------------+
bool CNavigatorDialog::IsValidDate(int day, int month, int year)
  {
   if(month < 1 || month > 12)
      return(false);

   int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

   bool leap = ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0));
   if(month == 2 && leap)
      days[1] = 29;

   return(day <= days[month - 1]);
  }

//+------------------------------------------------------------------+
//| Updates the status label with a coloured message.                |
//+------------------------------------------------------------------+
void CNavigatorDialog::UpdateStatus(string text, color clr = clrBlack)
  {
   m_lblStatus.Text(text);
   m_lblStatus.Color(clr);
  }

//+------------------------------------------------------------------+
//| Resets status to "Ready" in black.                               |
//+------------------------------------------------------------------+
void CNavigatorDialog::SetStatusReady(void)
  {
   UpdateStatus("Ready", clrBlack);
  }

//+------------------------------------------------------------------+
//| Formats one bookmark for display in the list.                    |
//+------------------------------------------------------------------+
string CNavigatorDialog::BookmarkDisplay(const SBookmark &bm) const
  {
   return(bm.name + " | " +
          bm.symbol + " | " +
          EnumToString(bm.timeframe) + " | " +
          TimeToString(bm.dateTime, TIME_DATE | TIME_MINUTES));
  }

//+------------------------------------------------------------------+
//| Completely rebuilds the bookmark list from the storage array.    |
//+------------------------------------------------------------------+
void CNavigatorDialog::RefreshBookmarkList(void)
  {
//--- Destroy the old control to reset its internal state
   m_listBookmarks.Destroy();

   if(!m_listBookmarks.Create(m_chart_id, m_name + "BmList", m_subwin,
                              m_listX1, m_listY1, m_listX2, m_listY2))
     {
      Print("HistoryNavigator: failed to recreate bookmark list.");
      m_selectedIndex = -1;
      return;
     }

   if(!Add(GetPointer(m_listBookmarks)))
     {
      Print("HistoryNavigator: failed to add recreated bookmark list.");
      m_selectedIndex = -1;
      return;
     }

   int total = m_bookmarkStorage.Count();

   for(int i = 0; i < total; i++)
     {
      SBookmark bm = m_bookmarkStorage.GetBookmark(i);
      m_listBookmarks.AddItem(BookmarkDisplay(bm));
     }

//--- Reset selection – the caller will restore it if needed
   m_selectedIndex = -1;
  }

//+------------------------------------------------------------------+
//| Loads bookmarks from the CSV file and refreshes the list.        |
//+------------------------------------------------------------------+
void CNavigatorDialog::LoadBookmarks(void)
  {
   if(!m_bookmarkStorage.Load())
     {
      UpdateStatus("Failed to load bookmarks.", clrRed);
      return;
     }

   RefreshBookmarkList();

   PrintFormat("HistoryNavigator: %d bookmark(s) available.",
               m_bookmarkStorage.Count());
  }

//+------------------------------------------------------------------+
//| Clears the selection completely.                                 |
//+------------------------------------------------------------------+
void CNavigatorDialog::ClearSelection(void)
  {
   m_selectedIndex = -1;
   m_listBookmarks.Select(-1);
   m_editBookmarkName.Text("");
   m_editBookmarkNotes.Text("");
  }

#endif // CNAVIGATORDIALOG_MQH
//+------------------------------------------------------------------+