context: using iTrading.Core.Data;
  
  namespace iTrading.Core.Kernel
  {
      using System;
      using System.Collections;
      using System.Globalization;
      using System.IO;
      using System.Timers;
      using iTrading.Core.Data;
  
      internal class MarketDataBuf
      {
          private byte[] buf = new byte[0x8000];
          private int bytesInBuf;
          private int bytesInBufRead;
          private int bytesRead;
          private int bytesWritten;
          private ArrayList headers;
          private const int maxBytesPerEvent = 11;
          private MarketDataEventArgs readCurrent;
          private bool recording;
          private bool replaying;
          private DateTime replayTime;
          private Symbol symbol;
          private DateTime time;
          private DateTime timeLastWrite;
          private int totalBytes2Read;
  
          internal MarketDataBuf(Symbol symbol, DateTime time)
          {
              this.time = time;
              this.symbol = symbol;
              this.Init();
          }
  
          internal void Cancel()
          {
              if (this.recording)
              {
                  this.recording = false;
                  this.symbol.MarketData.MarketDataItem -= new MarketDataItemEventHandler(this.MarketData);
                  this.Write();
              }
          }
  
          internal void CancelReplay()
          {
              this.symbol.connection.replayTimer.Elapsed -= new ElapsedEventHandler(this.ReplayTimerElapsed);
          }
  
          internal static bool Dump(Connection connection, Symbol symbol, DateTime from, DateTime to, string path)
          {
              if (from.Date > to.Date)
              {
                  throw new TMException(ErrorCode.Panic, "'from' date has to be smaller/equal 'to' date");
              }
              StreamWriter writer = null;
              try
              {
                  writer = new StreamWriter(path);
              }
              catch (Exception)
              {
                  throw new TMException(ErrorCode.Panic, "Unable to create file '" + path + "'");
              }
              CultureInfo provider = new CultureInfo("en-US");
              for (DateTime time = from.Date; time <= to.Date; time = time.AddDays(1.0))
              {
                  MarketDataBuf buf = new MarketDataBuf(symbol, time);
                  buf.Reset();
                  buf.MoveNext();
                  while (buf.readCurrent != null)
                  {
                      writer.WriteLine(string.Concat(new object[] { "", (int) buf.readCurrent.MarketDataType.Id, ";", buf.readCurrent.Time.ToString("yyyyMMddHHmmssff"), ";", buf.readCurrent.Price.ToString(provider), ";", buf.readCurrent.Volume }));
                      buf.MoveNext();
                  }
              }
              writer.Close();
              return true;
          }
  
          private void Init()
          {
              this.buf = new byte[0x8000];
              this.bytesInBuf = -1;
              this.bytesInBufRead = 0;
              this.bytesRead = 0;
              this.bytesWritten = 0;
              this.headers = new ArrayList();
              this.timeLastWrite = DateTime.Now;
              this.totalBytes2Read = 0;
              this.readCurrent = null;
              this.recording = false;
              this.replaying = false;
              this.replayTime = this.time;
              if (!Directory.Exists(Globals.InstallDir + @"\db\data"))
              {
                  Directory.CreateDirectory(Globals.InstallDir + @"\db\data");
              }
              string path = Globals.InstallDir + @"\db\data\" + this.symbol.FullName;
              if (!Directory.Exists(path))
              {
                  Directory.CreateDirectory(path);
              }
              this.ReadHeaders();
          }
  
          internal static void Load(Connection connection, Symbol symbol, string path)
          {
              CultureInfo provider = new CultureInfo("en-US");
              StreamReader reader = null;
              try
              {
                  reader = new StreamReader(path);
              }
              catch (Exception)
              {
                  throw new TMException(ErrorCode.Panic, "Unable to open file '" + path + "'");
              }
              DateTime minDate = Globals.MinDate;
              int num = 0;
              MarketDataBuf buf = null;
              string str = null;
              while ((str = reader.ReadLine()) != null)
              {
                  string[] strArray = str.Split(new char[] { ';' });
                  if (strArray.Length != 4)
                  {
                      reader.Close();
                      throw new TMException(ErrorCode.Panic, string.Concat(new object[] { "Line ", num, " in file '", path, "' has wrong number of fields (", strArray.Length, "): ", str }));
                  }
                  MarketDataType marketDataType = connection.MarketDataTypes[(MarketDataTypeId) Convert.ToInt32(strArray[0])];
                  if (marketDataType == null)
                  {
                      reader.Close();
                      throw new TMException(ErrorCode.Panic, string.Concat(new object[] { "Line ", num, " in file '", path, "' holds invalid market data type ", Convert.ToInt32(strArray[0]), ": ", str }));
                  }
                  DateTime time = Globals.MinDate;
                  try
                  {
                      int millisecond = 0;
                      if (strArray[1].Length != 14)
                      {
                          if (strArray[1].Length != 15)
                          {
                              if (strArray[1].Length != 0x10)
                              {
                                  if (strArray[1].Length != 0x11)
                                  {
                                      reader.Close();
                                      throw new TMException(ErrorCode.Panic, string.Concat(new object[] { "Line ", num, " in file '", path, "' holds invalid time stamp value (milli seconds): ", str }));
                                  }
                                  millisecond = Convert.ToInt32(strArray[1].Substring(14, 3));
                              }
                              else
                              {
                                  millisecond = Convert.ToInt32(strArray[1].Substring(14, 2)) * 10;
                              }
                          }
                          else
                          {
                              millisecond = Convert.ToInt32(strArray[1].Substring(14, 1)) * 100;
                          }
                      }
                      time = new DateTime(Convert.ToInt32(strArray[1].Substring(0, 4)), Convert.ToInt32(strArray[1].Substring(4, 2)), Convert.ToInt32(strArray[1].Substring(6, 2)), Convert.ToInt32(strArray[1].Substring(8, 2)), Convert.ToInt32(strArray[1].Substring(10, 2)), Convert.ToInt32(strArray[1].Substring(12, 2)), millisecond);
                  }
                  catch (Exception)
                  {
                      reader.Close();
                      throw new TMException(ErrorCode.Panic, string.Concat(new object[] { "Line ", num, " in file '", path, "' holds invalid time stamp '", strArray[1], "': ", str }));
                  }
                  if (time < minDate)
                  {
                      reader.Close();
                      throw new TMException(ErrorCode.Panic, string.Concat(new object[] { "Line ", num, " in file '", path, "': time stamp '", strArray[2], "' is smaller than previous one: ", str }));
                  }
                  double price = 0.0;
                  try
                  {
                      price = Convert.ToDouble(strArray[2], provider);
                  }
                  catch (Exception)
                  {
                      reader.Close();
                      throw new TMException(ErrorCode.Panic, string.Concat(new object[] { "Line ", num, " in file '", path, "' holds invalid price value '", strArray[3], "': ", str }));
                  }
                  int volume = 0;
                  try
                  {
                      volume = Convert.ToInt32(strArray[3], provider);
                  }
                  catch (Exception)
                  {
                      connection.ProcessEventArgs(new iTrading.Core.Kernel.ITradingErrorEventArgs(connection, ErrorCode.Panic, "", string.Concat(new object[] { "Line ", num, " in file '", path, "' holds invalid volume '", strArray[4], "': ", str })));
                      goto Label_04A4;
                  }
                  if (buf == null)
                  {
                      buf = new MarketDataBuf(symbol, time);
                  }
                  else if (buf.Date != time.Date)
                  {
                      buf.Write();
                      buf = new MarketDataBuf(symbol, time);
                  }
                  if (!buf.MarketDataNow(new MarketDataEventArgs(symbol.MarketData, ErrorCode.NoError, "", symbol, marketDataType, price, volume, time)))
                  {
                      reader.Close();
                      return;
                  }
              Label_04A4:
                  num++;
              }
              reader.Close();
              if (buf != null)
              {
                  buf.Write();
              }
          }
  
          internal void MarketData(object sender, MarketDataEventArgs e)
          {
              if (!this.MarketDataNow(e))
              {
                  this.Cancel();
              }
          }
  
          internal bool MarketDataNow(MarketDataEventArgs e)
          {
              lock (this)
              {
                  byte[] buffer;
                  IntPtr ptr;
                  if ((e.Error != ErrorCode.NoError) || (e.MarketDataType.Id == MarketDataTypeId.Unknown))
                  {
                      return true;
                  }
                  this.MoreBytes(11);
                  DataFileHeader header = (DataFileHeader) this.headers[(int) e.MarketDataType.Id];
                  int num = ++this.bytesInBuf;
                  int num2 = ++this.bytesInBuf;
                  int num3 = (int) Math.Round((double) (e.Time.Subtract(header.timeLast).TotalSeconds / Globals.recorderIntervalSeconds), 0);
                  int volume = num3;
                  if (num3 < 0)
                  {
                      this.symbol.connection.ProcessEventArgs(new iTrading.Core.Kernel.ITradingErrorEventArgs(this.symbol.connection, ErrorCode.Panic, "", string.Concat(new object[] { "MarketData (", e.MarketDataType.Id, ") for '", this.symbol.FullName, "' at ", e.Time, ": time is smaller than last recorded time stamp (", header.timeLast, ")" })));
                      return false;
                  }
                  (buffer = this.buf)[(int) (ptr = (IntPtr) num2)] = (byte) (buffer[(int) ptr] | ((byte) (((int) e.MarketDataType.Id) << 5)));
                  if (volume != 0)
                  {
                      if (volume > 0xff)
                      {
                          if (volume > 0xffff)
                          {
                              if (volume > 0xffffff)
                              {
                                  this.symbol.connection.ProcessEventArgs(new iTrading.Core.Kernel.ITradingErrorEventArgs(this.symbol.connection, ErrorCode.Panic, "", string.Concat(new object[] { "MarketData (", e.MarketDataType.Id, ") for '", this.symbol.FullName, "' at ", e.Time, " exceeds time gap maximum" })));
                                  return false;
                              }
                              (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 3);
                              this.buf[++this.bytesInBuf] = (byte) (volume / 0x10000);
                              volume -= (this.buf[this.bytesInBuf] * 0x100) * 0x100;
                              this.buf[++this.bytesInBuf] = (byte) (volume / 0x100);
                              volume -= this.buf[this.bytesInBuf] * 0x100;
                              this.buf[++this.bytesInBuf] = (byte) volume;
                          }
                          else
                          {
                              (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 2);
                              this.buf[++this.bytesInBuf] = (byte) (volume / 0x100);
                              volume -= this.buf[this.bytesInBuf] * 0x100;
                              this.buf[++this.bytesInBuf] = (byte) volume;
                          }
                      }
                      else
                      {
                          (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 1);
                          this.buf[++this.bytesInBuf] = (byte) volume;
                      }
                  }
                  header.timeLast = header.timeLast.AddSeconds(num3 * Globals.recorderIntervalSeconds);
                  double num5 = this.symbol.Round2TickSize(e.Price);
                  if (header.close != double.MinValue)
                  {
                      header.high = Math.Max(header.high, num5);
                      header.low = Math.Min(header.low, num5);
                      volume = (int) Math.Round((double) ((num5 - header.close) / this.symbol.TickSize), 0);
                      if (volume != 0)
                      {
                          if ((-16 >= volume) || (volume >= 0x10))
                          {
                              if ((-128 >= volume) || (volume >= 0x80))
                              {
                                  if ((-16384 >= volume) || (volume >= 0x4000))
                                  {
                                      this.symbol.connection.ProcessEventArgs(new iTrading.Core.Kernel.ITradingErrorEventArgs(this.symbol.connection, ErrorCode.Panic, "", string.Concat(new object[] { "MarketData (", e.MarketDataType.Id, ") for '", this.symbol.FullName, "' at ", e.Time, " exceeds max price gap (", e.Price, ")" })));
                                      return false;
                                  }
                                  volume += 0x4000;
                                  (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 12);
                                  this.buf[++this.bytesInBuf] = (byte) (volume / 0x100);
                                  volume -= this.buf[this.bytesInBuf] * 0x100;
                                  this.buf[++this.bytesInBuf] = (byte) volume;
                              }
                              else
                              {
                                  volume += 0x80;
                                  (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 8);
                                  this.buf[++this.bytesInBuf] = (byte) volume;
                              }
                          }
                          else
                          {
                              volume += 0x10;
                              (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 4);
                              (buffer = this.buf)[(int) (ptr = (IntPtr) num2)] = (byte) (buffer[(int) ptr] | ((byte) volume));
                          }
                      }
                  }
                  else
                  {
                      header.high = num5;
                      header.low = num5;
                      header.open = num5;
                  }
                  header.close = num5;
                  if (e.Volume != 0)
                  {
                      if (e.Volume <= 0xff)
                      {
                          volume = e.Volume;
                          (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 0x10);
                          this.buf[++this.bytesInBuf] = (byte) volume;
                      }
                      else if (((((int) (((double) e.Volume) / 50.0)) * 50) == e.Volume) && ((e.Volume / 50) <= 0xff))
                      {
                          volume = e.Volume / 50;
                          (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 0x20);
                          this.buf[++this.bytesInBuf] = (byte) volume;
                      }
                      else if (((((int) (((double) e.Volume) / 100.0)) * 100) == e.Volume) && ((e.Volume / 100) <= 0xff))
                      {
                          volume = e.Volume / 100;
                          (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 0x30);
                          this.buf[++this.bytesInBuf] = (byte) volume;
                      }
                      else if (((((int) (((double) e.Volume) / 500.0)) * 500) == e.Volume) && ((e.Volume / 500) <= 0xff))
                      {
                          volume = e.Volume / 500;
                          (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 0x40);
                          this.buf[++this.bytesInBuf] = (byte) volume;
                      }
                      else if (((((int) (((double) e.Volume) / 1000.0)) * 0x3e8) == e.Volume) && ((e.Volume / 0x3e8) <= 0xff))
                      {
                          volume = e.Volume / 0x3e8;
                          (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 80);
                          this.buf[++this.bytesInBuf] = (byte) volume;
                      }
                      else if (e.Volume <= 0xffff)
                      {
                          volume = e.Volume;
                          (buffer = this.buf)[(int) (ptr = (IntPtr) num)] = (byte) (buffer[(int) ptr] | 0x60);
  
gt:                         this.buf[++this.bytesInBuf] = (byte) (volume / 0x100);