//+------------------------------------------------------------------+
//|                                                 TicksCollect.mq4 |
//|                                   Copyright 2021, Incognito Team |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, Incognito team"
#property description "Indicator for collect ticks from broker"
#property link      "https://inco-group.info/"
#define VERSION "1.1"
#define FLUSH_EVERY_TICKS 50
#define PATH_SEPARATOR "\\"
#define OUT_FILE_EXTENSION  "csv"

#property version VERSION
#property strict
#property indicator_separate_window


struct TickStruct
{
   datetime time;
   double   bid;
   double   ask;

   TickStruct(datetime dtTime = 0, double dBid = 0.0, double dAsk = 0.0)
   {
      time = dtTime;
      bid = dBid;
      ask = dAsk;
   }
};

input string     i_string1                      = "Ticks collecting params";// =========================
input string     i_storeFolder                  = "tickshistory";

TickStruct g_nonWritedTicks[];

bool g_activate;
int g_newTicksCnt; // Count of ticks for store to file

//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
//| Custom indicator initialization function                                                                                                                                                          |
//+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
int OnInit()
{
   if (!IsTicksBufferReady())
      return INIT_FAILED;

   g_activate = true;
   return INIT_SUCCEEDED;
}

//+---------------------------------------------------------------------------------------------------------+
//|Custom indicator deinitialization function                                                               |
//+---------------------------------------------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   FlushTicksFile(true);
}

//+---------------------------------------------------------------------------------------------------------+
//|Check ticks buffer ready                                                                                 |
//+---------------------------------------------------------------------------------------------------------+
bool IsTicksBufferReady()
{
   g_newTicksCnt = 0;

   if (ArrayResize(g_nonWritedTicks, FLUSH_EVERY_TICKS) != FLUSH_EVERY_TICKS)
   {
      Alert(WindowExpertName(), ": unable to allocate memory for ticks buffer. Indicator is off.");
      return false;
   }

   return true;
}

//+---------------------------------------------------------------------------------------------------------+
//| Open tick file                                                                                          |
//+---------------------------------------------------------------------------------------------------------+
bool OpenTicksFile(int &hTicksFile)
{
   string fileFolder = StringTrimRight(i_storeFolder) + PATH_SEPARATOR + Symbol() + PATH_SEPARATOR + IntegerToString(Year()),
          fileName =  StringFormat("%d-%d.%s", Month(), Day(), OUT_FILE_EXTENSION),
          filePath = fileFolder + PATH_SEPARATOR + fileName;

   FolderCreate(fileFolder);

   hTicksFile = FileOpen(filePath, FILE_READ | FILE_WRITE | FILE_SHARE_READ | FILE_SHARE_WRITE);
   if (hTicksFile >= 0)
      return true;

   Alert(WindowExpertName(), ": unable to open the file ", filePath, ". Indicator is off.");
   return false;
}

//+---------------------------------------------------------------------------------------------------------+
//| Flush ticks to file                                                                                     |
//+---------------------------------------------------------------------------------------------------------+
bool FlushTicksFile(bool isDeinit = false)
{
   TickStruct tick;

   g_nonWritedTicks[g_newTicksCnt].time = TimeCurrent();
   g_nonWritedTicks[g_newTicksCnt].bid = Bid;
   g_nonWritedTicks[g_newTicksCnt].ask = Ask;
   g_newTicksCnt++;

   if (g_newTicksCnt < FLUSH_EVERY_TICKS && !isDeinit)
      return true;

   // Store ticks to file
   int hTicksFile = -1;
   if (!OpenTicksFile(hTicksFile))
      return false;

   FileSeek(hTicksFile, 0, SEEK_END);
   for (int i = 0; i < g_newTicksCnt; i++)
   {
      tick = g_nonWritedTicks[i];
      if (FileWrite(hTicksFile, TimeToStr(tick.time, TIME_DATE|TIME_SECONDS), DoubleToStr(tick.bid, Digits), DoubleToStr(tick.ask, Digits)))
         continue;

      Alert(WindowExpertName(), ": uunable to save the data to file. Error N", GetLastError(), ". Indicator is off.");
      return false;
   }

   // Close file, reset ticks counter
   FileClose(hTicksFile);
   g_newTicksCnt = 0;

   return true;
}

//+---------------------------------------------------------------------------------------------------------+
//| OnCalculate indicator event                                                                            |
//+---------------------------------------------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
{
   if (!g_activate)
      return rates_total;

   if (!FlushTicksFile())
      g_activate = false;

   return rates_total;
}
