//+------------------------------------------------------------------+
//|                                  CNavigatorDialogAnnotations.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   "2.00"
#property strict

#ifndef CNAVIGATORDIALOGANNOTATIONS_MQH
#define CNAVIGATORDIALOGANNOTATIONS_MQH

//--- Standard controls
#include <Controls\Dialog.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Button.mqh>
#include <Controls\Label.mqh>
#include <Controls\ListView.mqh>

//--- Storage class
#include "BookmarkStorageAnnotations.mqh"

//+------------------------------------------------------------------+
//| History Navigator dialog with bookmark annotations               |
//+------------------------------------------------------------------+
class CNavigatorDialogAnnotations : public CAppDialog
  {
private:
   //--- Controls
   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;

   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
   int               m_margin;
   int               m_rowHeight;
   int               m_spacing;
   int               m_labelWidth;
   int               m_editWidth;
   int               m_buttonWidth;
   int               m_clientWidth;
   int               m_clientHeight;
   int               m_listX1,m_listY1,m_listX2,m_listY2;
   int               m_listHeight;

   //--- State
   CBookmarkStorageAnnotations m_bookmarkStorage;
   int               m_selectedIndex;
   datetime          m_lastNavigatedTime;

   //--- Timer-based refresh control
   bool              m_bNeedRefresh;
   uint              m_lastChartChangeTick;
   int               m_stableBarCount;
   bool              m_pendingNavigation;
   datetime          m_pendingNavigationTime;
   ENUM_TIMEFRAMES   m_pendingNavigationTimeframe;
   string            m_pendingSymbol;
   ulong             m_requestTick;
   bool              m_waitingSwitch;
   string            m_annotationPrefix;
   uint              m_annotationSerial;
   string            m_ownedObjects[];
   bool              m_annotationsIncomplete;
   ulong             m_refreshStarted;

   //--- Event map
                     EVENT_MAP_BEGIN(CNavigatorDialogAnnotations)
                     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:
                     CNavigatorDialogAnnotations(void);
                    ~CNavigatorDialogAnnotations(void);

   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;

   void              OnGoClick(void);
   void              OnTodayClick(void);
   void              OnSaveBookmark(void);
   void              OnGoToBookmark(void);
   void              OnDeleteBookmark(void);
   void              OnListSelect(void);

   //+------------------------------------------------------------------+
   //| Handles chart changes for annotation refresh                     |
   //+------------------------------------------------------------------+
   void              ChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   void              OnTimer(void);
   void              RestorePendingNavigation(void);

   void              CancelPendingNavigation(void);
private:
   bool              PutPending(const string suffix,const double value);
   bool              ReadTargetBar(const datetime target,MqlRates &bar,int &shift);
   bool              TrackObject(const string name);
   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);
   bool              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;

   //--- Annotation methods
   string            GetAnnotationName(const uint serial,string suffix) const;
   void              CreateAnnotationForBookmark(const SBookmark &bm);
   void              RemoveAllAnnotations(void);
   void              RefreshAllAnnotations(void);
   bool              IsChartStable(void);
   void              RequestDeferredRefresh(void);
   string            PendingNavigationKey(const string suffix) const;
   bool              QueuePendingNavigation(const SBookmark &bookmark);
   void              ClearPendingNavigation(const string symbol="");
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CNavigatorDialogAnnotations::CNavigatorDialogAnnotations(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),
     m_bNeedRefresh(false),
     m_lastChartChangeTick(0),
     m_stableBarCount(-1),
     m_pendingNavigation(false),
     m_pendingNavigationTime(0),
     m_pendingNavigationTimeframe(PERIOD_CURRENT),
     m_pendingSymbol(""),
     m_requestTick(0),
     m_waitingSwitch(false),
     m_annotationSerial(0),
     m_annotationsIncomplete(false),
     m_refreshStarted(0)
  {
   m_annotationPrefix=StringFormat("HN2_%I64X_%I64X_",ChartID(),GetMicrosecondCount());
  }

//+------------------------------------------------------------------+
//| Destructor – remove all annotations                              |
//+------------------------------------------------------------------+
CNavigatorDialogAnnotations::~CNavigatorDialogAnnotations(void)
  {
   RemoveAllAnnotations();
  }

//+------------------------------------------------------------------+
//| Delayed refresh and navigation hand-off helpers                  |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::RequestDeferredRefresh(void)
  {
   if(!m_bNeedRefresh)
      m_refreshStarted=GetTickCount64();
   m_bNeedRefresh=true;
   m_lastChartChangeTick=GetTickCount();
   m_stableBarCount=-1;
  }

//+------------------------------------------------------------------+
//| Builds a terminal-global key for pending navigation              |
//+------------------------------------------------------------------+
string CNavigatorDialogAnnotations::PendingNavigationKey(const string suffix) const
  {
   return("HN2.P."+StringFormat("%I64d",ChartID())+"."+suffix);
  }

//+------------------------------------------------------------------+
//| Stores navigation state before the chart is changed              |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::QueuePendingNavigation(const SBookmark &bookmark)
  {
   ClearPendingNavigation();
   int n=StringLen(bookmark.symbol);
   if(bookmark.dateTime<=0||bookmark.dateTime>32535215999||n<1||n>255||!HNValidTimeframe(bookmark.timeframe))
      return(false);
   bool ok=PutPending("Version",2)&&PutPending("Time",(double)bookmark.dateTime)&&
           PutPending("Timeframe",(double)bookmark.timeframe)&&
           PutPending("Tick",(double)GetTickCount64())&&PutPending("Length",n);
//--- UTF-16 code units are exact small integers in terminal global doubles.
   for(int i=0;i<n&&ok;i++)
      ok=PutPending("S"+IntegerToString(i),(double)StringGetCharacter(bookmark.symbol,i));
   if(!ok||!PutPending("Flag",1))
     {
      ClearPendingNavigation();
      return(false);
     }
   m_waitingSwitch=true;
   m_requestTick=GetTickCount64();
   return(true);
  }

//+------------------------------------------------------------------+
//| Clears persisted navigation state                                |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::ClearPendingNavigation(const string symbol)
  {
   string prefix=PendingNavigationKey("");
   GlobalVariableDel(PendingNavigationKey("Flag"));
   for(int i=GlobalVariablesTotal()-1;i>=0;i--)
     {
      string name=GlobalVariableName(i);
      if(StringFind(name,prefix)==0&&!GlobalVariableDel(name))
         Print("Failed to clear pending key: ",name);
     }
   m_waitingSwitch=false;
  }

//+------------------------------------------------------------------+
//| Restores navigation state after EA reinitialization              |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::RestorePendingNavigation(void)
  {
//--- Ordinary attachment, restart, recompilation and input changes never replay.
   if(_UninitReason!=REASON_CHARTCHANGE)
     {
      ClearPendingNavigation();
      return;
     }
   double flag,version,stamp,tf,tick,len;
   bool ok=GlobalVariableGet(PendingNavigationKey("Flag"),flag)&&flag==1&&
           GlobalVariableGet(PendingNavigationKey("Version"),version)&&version==2&&
           GlobalVariableGet(PendingNavigationKey("Time"),stamp)&&stamp>0&&
           stamp<=32535215999&&MathFloor(stamp)==stamp&&
           GlobalVariableGet(PendingNavigationKey("Timeframe"),tf)&&tf==(double)Period()&&
           GlobalVariableGet(PendingNavigationKey("Tick"),tick)&&tick>=0&&
           tick<=(double)GetTickCount64()&&(double)GetTickCount64()-tick<=60000&&
           GlobalVariableGet(PendingNavigationKey("Length"),len)&&len>=1&&len<=255&&
           MathFloor(len)==len;
   string symbol="";
   if(ok)
      for(int i=0;i<(int)len;i++)
        {
         double code;
         if(!GlobalVariableGet(PendingNavigationKey("S"+IntegerToString(i)),code)||code<1||code>65535||
            MathFloor(code)!=code)
           {
            ok=false;
            break;
           }
         symbol+=ShortToString((ushort)code);
        }
   if(!ok||symbol!=Symbol())
     {
      ClearPendingNavigation();
      return;
     }
   m_pendingNavigationTime=(datetime)stamp;
   m_pendingNavigationTimeframe=(ENUM_TIMEFRAMES)(int)tf;
   m_pendingSymbol=symbol;
   m_requestTick=(ulong)tick;
   m_pendingNavigation=true;
   RequestDeferredRefresh();
  }

//+------------------------------------------------------------------+
//| Create the dialog window                                         |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::Create(const long chart,const string name,const int subwin,const int x1,
      const int y1,const int x2,const int y2)
  {
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);

   m_clientWidth=x2 - x1;
   m_clientHeight=y2 - y1;

   if(!CreateControls())
      return(false);

   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));

   LoadBookmarks();
//--- OnInit does not necessarily receive CHARTEVENT_CHART_CHANGE.  Request
//--- the delayed refresh explicitly, so annotations are rebuilt after a
//--- timeframe/symbol reinitialization as soon as chart data is ready.
   RequestDeferredRefresh();
   if(m_lblStatus.Text()=="")
      SetStatusReady();

   return(true);
  }

//+------------------------------------------------------------------+
//| Create all UI controls                                           |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::CreateControls(void)
  {
   int x1,y1,x2,y2;
   int currentY=m_margin;

//--- Title
   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
   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
   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
   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
   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
   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
   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 button
   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
   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
   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
   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);
  }

//+------------------------------------------------------------------+
//| Handlers                                                         |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::OnGoClick(void)
  {
   CancelPendingNavigation();
   NavigateToDate();
  }

//+------------------------------------------------------------------+
//| Handles the Return To Today button click                         |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::OnTodayClick(void)
  {
   ReturnToToday();
  }

//+------------------------------------------------------------------+
//| Saves the current position as a bookmark                         |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::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(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;
     }
   SBookmark bm;
   bm.name=name;
   bm.symbol=symbol;
   bm.timeframe=tf;
   bm.dateTime=dt;
   bm.notes=m_editBookmarkNotes.Text();

   if(!m_bookmarkStorage.AddBookmark(bm))
     {
      UpdateStatus("Failed to save bookmark.",clrRed);
      return;
     }
   RefreshBookmarkList();
   RefreshAllAnnotations();

   int newIndex=m_bookmarkStorage.Count() - 1;
   if(newIndex>=0)
      SelectBookmarkIndex(newIndex);

   UpdateStatus("Bookmark saved.",clrGreen);
   m_editBookmarkName.Text("");
   m_editBookmarkNotes.Text("");

   if(newIndex>=0)
      SelectBookmarkIndex(newIndex);
  }

//+------------------------------------------------------------------+
//| Checks whether a bookmark is selected                            |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::CaptureSelectedBookmark(void)
  {
   if(m_selectedIndex < 0||m_selectedIndex>=m_bookmarkStorage.Count())
      return(false);
   return(true);
  }

//+------------------------------------------------------------------+
//| Selects a bookmark by its storage index                          |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::SelectBookmarkIndex(const int index)
  {
   if(index < 0||index>=m_bookmarkStorage.Count())
     {
      m_selectedIndex=-1;
      return(false);
     }
   m_selectedIndex=index;
   m_listBookmarks.Select(index);

   SBookmark bm=m_bookmarkStorage.GetBookmark(index);
   m_editBookmarkName.Text(bm.name);
   m_editBookmarkNotes.Text(bm.notes);

   PrintFormat("HistoryNavigatorAnnotations: selected index=%d name='%s'",index,bm.name);
   return(true);
  }

//+------------------------------------------------------------------+
//| Navigates to the selected bookmark                               |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::OnGoToBookmark(void)
  {
   if(!CaptureSelectedBookmark())
     {
      UpdateStatus("Please select a bookmark first.",clrRed);
      return;
     }
   CancelPendingNavigation();
   SBookmark bm=m_bookmarkStorage.GetBookmark(m_selectedIndex);

   if(bm.name==""||bm.symbol==""||bm.dateTime<=0)
     {
      UpdateStatus("Invalid bookmark selected.",clrRed);
      return;
     }
   PrintFormat("HistoryNavigatorAnnotations: opening bookmark '%s' -> %s %s %s",bm.name,bm.symbol,
               EnumToString(bm.timeframe),TimeToString(bm.dateTime,TIME_DATE | TIME_MINUTES));

   if(bm.symbol!=Symbol()||bm.timeframe!=(ENUM_TIMEFRAMES)Period())
     {
      if(!SymbolSelect(bm.symbol,true))
        {
         UpdateStatus("Saved symbol unavailable on this broker.",clrRed);
         return;
        }
      if(!QueuePendingNavigation(bm))
        {
         UpdateStatus("Failed to prepare bookmark navigation.",clrRed);
         return;
        }
      if(!ChartSetSymbolPeriod(0,bm.symbol,bm.timeframe))
        {
         ClearPendingNavigation(bm.symbol);
         UpdateStatus("Failed to switch symbol or timeframe.",clrRed);
         return;
        }
      UpdateStatus("Loading bookmark chart...",clrBlue);
      return;
     }
   NavigateToDateTime(bm.dateTime);
  }

//+------------------------------------------------------------------+
//| Deletes the selected bookmark                                    |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::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;
     }
   string message="Delete bookmark '" + bm.name + "'?";
   int answer=MessageBox(message,"Confirm Delete",MB_YESNO);
   if(answer!=IDYES)
      return;

   if(!m_bookmarkStorage.DeleteBookmark(index))
     {
      UpdateStatus("Failed to delete bookmark.",clrRed);
      return;
     }
   m_selectedIndex=-1;
   RefreshBookmarkList();
   RefreshAllAnnotations();

   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);
  }

//+------------------------------------------------------------------+
//| Updates controls for the selected bookmark                       |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::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("HistoryNavigatorAnnotations: list selection index=%d name='%s'",index,bm.name);
  }

//+------------------------------------------------------------------+
//| Chart event – sets a flag for delayed refresh                    |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::ChartEvent(const int id,const long &lparam,const double &dparam,
      const string &sparam)
  {
   CAppDialog::ChartEvent(id,lparam,dparam,sparam);

   if(id==CHARTEVENT_CHART_CHANGE)
     {
      RequestDeferredRefresh();
     }
  }

//+------------------------------------------------------------------+
//| Timer handler – performs delayed refresh when chart is stable    |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::OnTimer(void)
  {
   if(m_waitingSwitch)
     {
      if(GetTickCount64()-m_requestTick>60000)
        {
         CancelPendingNavigation();
         UpdateStatus("Chart switch timed out.",clrRed);
        }
      return;
     }
   if(m_pendingNavigation)
     {
      if(Symbol()!=m_pendingSymbol||Period()!=m_pendingNavigationTimeframe||
         GetTickCount64()-m_requestTick>60000)
        {
         CancelPendingNavigation();
         UpdateStatus("Navigation expired or chart context changed.",clrRed);
         return;
        }
      MqlRates bar;
      int shift;
      if(!ReadTargetBar(m_pendingNavigationTime,bar,shift))
         return;
      if(!IsChartStable())
         return;
      datetime target=m_pendingNavigationTime;
      CancelPendingNavigation();
      NavigateToDateTime(target);
     }
   if(!m_bNeedRefresh)
      return;
   if(GetTickCount64()-m_refreshStarted>60000)
     {
      m_bNeedRefresh=false;
      Print("Annotation refresh timed out; retry Go To or change the chart to refresh.");
      return;
     }
   if(!IsChartStable())
      return;
   RefreshAllAnnotations();
   m_bNeedRefresh=m_annotationsIncomplete;
  }

//+------------------------------------------------------------------+
//| Checks if the chart is stable enough for annotation operations   |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::IsChartStable(void)
  {
//--- Check if chart has visible bars
   int visibleBars=(int)ChartGetInteger(0,CHART_VISIBLE_BARS);
   if(visibleBars<=0)
      return(false);

//--- Check if symbol and period are valid
   if(Symbol()==""||Period()==0)
      return(false);

//--- Equal bar counts are a debounce only, not a synchronization guarantee.
   if(!SeriesInfoInteger(Symbol(),Period(),SERIES_SYNCHRONIZED))
      return(false);
//--- Check if there are bars available
   int bars=Bars(Symbol(),Period());
   if(bars<=0)
      return(false);

//--- Check if bar count has stabilised (not changing rapidly)
   if(m_stableBarCount==-1)
     {
      m_stableBarCount=bars;
      return(false);
     }
//--- If bar count changed, update and wait
   if(m_stableBarCount!=bars)
     {
      m_stableBarCount=bars;
      return(false);
     }
//--- Use a local monotonic clock. TimeCurrent() can remain unchanged until
//--- the next market tick, which would otherwise block the refresh forever.
   if((uint)(GetTickCount()-m_lastChartChangeTick) < 1000)
      return(false);

   return(true);
  }

//+------------------------------------------------------------------+
//| Navigation methods                                               |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::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);
  }

//+------------------------------------------------------------------+
//| Navigates to a specified historical time                         |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::NavigateToDateTime(datetime target)
  {
   if(target<=0||target>TimeCurrent())
     {
      UpdateStatus("Invalid or future bookmark time.",clrRed);
      return;
     }
   MqlRates bar;
   int idx;
   if(!ReadTargetBar(target,bar,idx))
     {
      if(!m_pendingNavigation||m_pendingNavigationTime!=target)
        {
         CancelPendingNavigation();
         m_pendingNavigation=true;
         m_pendingNavigationTime=target;
         m_pendingNavigationTimeframe=Period();
         m_pendingSymbol=Symbol();
         m_requestTick=GetTickCount64();
        }
      RequestDeferredRefresh();
      UpdateStatus("Waiting for target history (60s limit).",clrBlue);
      return;
     }
   if(!CenterChartOnBar(idx))
     {
      UpdateStatus("Chart positioning failed. See Experts log.",clrRed);
      return;
     }
   m_lastNavigatedTime=target;
   UpdateStatus("Found: "+TimeToString(bar.time,TIME_DATE|TIME_MINUTES),clrGreen);
   RequestDeferredRefresh();
  }

//+------------------------------------------------------------------+
//| Returns the chart to the latest market data                      |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::ReturnToToday(void)
  {
   CancelPendingNavigation();
   ChartSetInteger(0,CHART_AUTOSCROLL,true);
   ChartSetInteger(0,CHART_SHIFT,true);
   ChartNavigate(0,CHART_END,0);
   ChartRedraw();
   m_lastNavigatedTime=0;
   UpdateStatus("Viewing current market",clrBlack);
  }

//+------------------------------------------------------------------+
//| Finds the bar shift for a target time                            |
//+------------------------------------------------------------------+
int CNavigatorDialogAnnotations::FindBarShift(datetime target,bool exact)
  {
   int shift=iBarShift(Symbol(),Period(),target,exact);
   if(shift < 0)
      return(-1);
   return(shift);
  }

//+------------------------------------------------------------------+
//| Centres the chart on a historical bar                            |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::CenterChartOnBar(int barIndex)
  {
   int total=Bars(Symbol(),Period());
   if(total<=0||barIndex<0||barIndex>=total)
      return(false);
   if(!ChartSetInteger(0,CHART_AUTOSCROLL,false)||!ChartSetInteger(0,CHART_SHIFT,false))
      return(false);
   ChartRedraw();
   int visible=(int)ChartGetInteger(0,CHART_VISIBLE_BARS);
   if(visible<1)
      return(false);
//--- Series 0 is newest. CHART_BEGIN's origin is the oldest bar.
   int offset=(total-1)-barIndex-visible/2;
   if(offset<0)
      offset=0;
   if(!ChartNavigate(0,CHART_BEGIN,offset))
      return(false);
   ChartRedraw();
//--- Verify the observed viewport and correct using its series coordinates.
   int desired=(int)MathMin(total-1,barIndex+visible/2);
   int first=(int)ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR);
   if(first!=desired&&!ChartNavigate(0,CHART_CURRENT_POS,first-desired))
      return(false);
   ChartRedraw();
   first=(int)ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR);
   bool shown=(barIndex<=first&&barIndex>=first-visible+1);
   if(!shown)
      PrintFormat("Centering failed: target=%d first=%d visible=%d",barIndex,first,visible);
//--- Near either history boundary the terminal may clamp the desired center.
   return(shown);
  }

//+------------------------------------------------------------------+
//| Validates the entered date and time                              |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::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 calendar date is valid                          |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::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 navigator status message                             |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::UpdateStatus(string text,color clr=clrBlack)
  {
   m_lblStatus.Text(text);
   m_lblStatus.Color(clr);
  }

//+------------------------------------------------------------------+
//| Displays the ready status message                                |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::SetStatusReady(void)
  {
   UpdateStatus("Ready",clrBlack);
  }

//+------------------------------------------------------------------+
//| Builds text for a bookmark list entry                            |
//+------------------------------------------------------------------+
string CNavigatorDialogAnnotations::BookmarkDisplay(const SBookmark &bm) const
  {
   return(bm.name + " | " + bm.symbol + " | " + EnumToString(bm.timeframe) + " | " + TimeToString(bm.dateTime,
          TIME_DATE|TIME_MINUTES));
  }

//+------------------------------------------------------------------+
//| Rebuilds the bookmark list control                               |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::RefreshBookmarkList(void)
  {
   m_listBookmarks.Destroy();

   if(!m_listBookmarks.Create(m_chart_id,m_name+"BmList",m_subwin,m_listX1,m_listY1,m_listX2,m_listY2))
     {
      Print("HistoryNavigatorAnnotations: failed to recreate bookmark list.");
      m_selectedIndex=-1;
      return;
     }
   if(!Add(GetPointer(m_listBookmarks)))
     {
      Print("HistoryNavigatorAnnotations: 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));
     }
   m_selectedIndex=-1;
  }

//+------------------------------------------------------------------+
//| Loads bookmarks into the navigator                               |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::LoadBookmarks(void)
  {
   if(!m_bookmarkStorage.Load())
     {
      UpdateStatus("Failed to load bookmarks.",clrRed);
      return;
     }
   RefreshBookmarkList();
   RefreshAllAnnotations();

   PrintFormat("HistoryNavigatorAnnotations: %d bookmark(s) available.",m_bookmarkStorage.Count());
  }

//+------------------------------------------------------------------+
//| Clears the current bookmark selection                            |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::ClearSelection(void)
  {
   m_selectedIndex=-1;
   m_listBookmarks.Select(-1);
   m_editBookmarkName.Text("");
   m_editBookmarkNotes.Text("");
  }
//--- ====================================================================
//--- Annotation methods
//--- ====================================================================

//+------------------------------------------------------------------+
//| Builds an annotation object name                                 |
//+------------------------------------------------------------------+
string CNavigatorDialogAnnotations::GetAnnotationName(const uint serial,string suffix) const
  {
   return(m_annotationPrefix+IntegerToString(serial)+"_"+suffix);
  }

//+------------------------------------------------------------------+
//| Create vertical line and text label for a bookmark               |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::CreateAnnotationForBookmark(const SBookmark &bm)
  {
   if(bm.symbol!=Symbol()||bm.timeframe!=Period())
      return;
//--- Keep the chart caption compact; the complete name remains in storage.
   string label=bm.name;
   if(StringLen(label)>60)
      label=StringSubstr(label,0,57)+"...";
   MqlRates bar;
   int shift;
   if(!ReadTargetBar(bm.dateTime,bar,shift))
     {
      m_annotationsIncomplete=true;
      return;
     }
   string lineName,textName;
//--- Identity is per allocation, independent of user names and duplicate records.
   do
     {
      m_annotationSerial++;
      lineName=GetAnnotationName(m_annotationSerial,"L");
      textName=GetAnnotationName(m_annotationSerial,"T");
     }
   while(ObjectFind(0,lineName)>=0||ObjectFind(0,textName)>=0);
   ResetLastError();
   if(!ObjectCreate(0,lineName,OBJ_VLINE,0,bm.dateTime,0))
     {
      m_annotationsIncomplete=true;
      Print("Line creation failed: ",GetLastError());
      return;
     }
   if(!TrackObject(lineName))
     {
      ObjectDelete(0,lineName);
      m_annotationsIncomplete=true;
      return;
     }
   if(ObjectFind(0,lineName)<0)
     {
      m_annotationsIncomplete=true;
      Print("Queued line creation did not complete.");
      return;
     }
   bool ok=ObjectSetInteger(0,lineName,OBJPROP_COLOR,clrBlue)&&
           ObjectSetInteger(0,lineName,OBJPROP_STYLE,STYLE_SOLID)&&
           ObjectSetInteger(0,lineName,OBJPROP_WIDTH,1)&&
           ObjectSetInteger(0,lineName,OBJPROP_SELECTABLE,false);
   if(!ObjectCreate(0,textName,OBJ_TEXT,0,bm.dateTime,bar.high))
     {
      m_annotationsIncomplete=true;
      Print("Label creation failed: ",GetLastError());
      return;
     }
   if(!TrackObject(textName))
     {
      ObjectDelete(0,textName);
      m_annotationsIncomplete=true;
      return;
     }
   if(ObjectFind(0,textName)<0)
     {
      m_annotationsIncomplete=true;
      Print("Queued label creation did not complete.");
      return;
     }
   ok=ObjectSetString(0,textName,OBJPROP_TEXT,label)&&ok;
   ok=ObjectSetInteger(0,textName,OBJPROP_COLOR,clrBlue)&&ok;
   ok=ObjectSetInteger(0,textName,OBJPROP_FONTSIZE,10)&&ok;
   ok=ObjectSetString(0,textName,OBJPROP_FONT,"Arial")&&ok;
   ok=ObjectSetInteger(0,textName,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER)&&ok;
   ok=ObjectSetInteger(0,textName,OBJPROP_SELECTABLE,false)&&ok;
//--- Synchronous reads verify key properties after queued setters execute.
   ok=(ObjectGetString(0,textName,OBJPROP_TEXT)==label)&&ok;
   ok=(ObjectGetInteger(0,lineName,OBJPROP_COLOR)==clrBlue)&&ok;
   if(!ok)
     {
      m_annotationsIncomplete=true;
      Print("Annotation properties failed: ",GetLastError());
     }
  }

//+------------------------------------------------------------------+
//| Records an allocated annotation name for later cleanup           |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::TrackObject(const string name)
  {
   int n=ArraySize(m_ownedObjects);
   if(ArrayResize(m_ownedObjects,n+1)!=n+1)
      return(false);
   m_ownedObjects[n]=name;
   return(true);
  }

//+------------------------------------------------------------------+
//| Remove all annotations created by this EA                        |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::RemoveAllAnnotations(void)
  {
//--- No prefix sweep: only names allocated and recorded by this instance.
   int kept=0;
   for(int i=0;i<ArraySize(m_ownedObjects);i++)
     {
      string name=m_ownedObjects[i];
      if(ObjectFind(0,name)<0)
         continue;
      if(!ObjectDelete(0,name)||ObjectFind(0,name)>=0)
        {
         Print("Could not remove annotation: ",name," error=",GetLastError());
         m_ownedObjects[kept++]=name;
        }
     }
   ArrayResize(m_ownedObjects,kept);
  }

//+------------------------------------------------------------------+
//| Refresh all annotations: clear and recreate for current chart    |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::RefreshAllAnnotations(void)
  {
   m_annotationsIncomplete=false;
   if(!IsChartStable())
     {
      m_annotationsIncomplete=true;
      RequestDeferredRefresh();
      return;
     }
   RemoveAllAnnotations();
   if(ArraySize(m_ownedObjects)>0)
     {
      m_annotationsIncomplete=true;
      return;
     }
   for(int i=0;i<m_bookmarkStorage.Count();i++)
     {
      SBookmark bm=m_bookmarkStorage.GetBookmark(i);
      CreateAnnotationForBookmark(bm);
     }
  }

//+------------------------------------------------------------------+
//| Writes one value to a temporary terminal Global Variable         |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::PutPending(const string suffix,const double value)
  {
   string key=PendingNavigationKey(suffix);
   return(GlobalVariableTemp(key)&&GlobalVariableSet(key,value)!=0);
  }

//+------------------------------------------------------------------+
//| Cancels pending navigation and clears its stored state           |
//+------------------------------------------------------------------+
void CNavigatorDialogAnnotations::CancelPendingNavigation(void)
  {
   m_pendingNavigation=false;
   ClearPendingNavigation();
  }

//+------------------------------------------------------------------+
//| Checks synchronization and resolves the requested historical bar |
//+------------------------------------------------------------------+
bool CNavigatorDialogAnnotations::ReadTargetBar(const datetime target,MqlRates &bar,int &shift)
  {
   if(target<=0||target>TimeCurrent())
      return(false);
   MqlRates rates[1];
//--- Date-based request initiates loading if this historical region is absent.
   if(CopyRates(Symbol(),Period(),target,1,rates)!=1)
      return(false);
   if(!SeriesInfoInteger(Symbol(),Period(),SERIES_SYNCHRONIZED))
      return(false);
   int total=Bars(Symbol(),Period());
   if(total<=0||target<iTime(Symbol(),Period(),total-1))
      return(false);
   shift=iBarShift(Symbol(),Period(),target,false);
   if(shift<0||iTime(Symbol(),Period(),shift)!=rates[0].time)
      return(false);
//--- Gaps map to the nearest preceding available bar, not an invented candle.
   if(rates[0].time<=0||rates[0].time>target||!MathIsValidNumber(rates[0].high))
      return(false);
   bar=rates[0];
   return(true);
  }
#endif // CNAVIGATORDIALOGANNOTATIONS_MQH
//+------------------------------------------------------------------+