/// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="startTime"></param>
        public PatternsEngineWrapper(DateTime startTime, string name)
        {
            this.Name               = name;
            this.StartTime          = startTime;
            this.CurrentTime        = startTime;
            this.EngineWorkerHandle = new SafeNativeMethods.EngineCriticalHandle((IntPtr)0);

            // Trace and counters
            PatternsProcessorWrapper.Source.TraceEvent(TraceEventType.Verbose, 1200, "{0} instanciation at {1:s}", this, startTime);

            // Initialize performance counters
            PerformanceCounterHelper.InitializePerformanceCounters();
        }
        /// <summary>
        /// Add the given FHR & UP signal to the engine and detect the events & contractions
        /// </summary>
        /// <param name="hrs">FHR1 signal in 4Hz</param>
        /// <param name="ups">UP signal in 1Hz</param>
        /// <param name="startIndex">Index to start processing as per the 1Hz signal</param>
        /// <param name="length">Number of seconds of the buffer to process as per the 1Hz signal</param>
        /// <returns></returns>
        public IEnumerable <DetectedObject> Process(byte[] hrs, byte[] ups, int startIndex, int length)
        {
            try
            {
                var results = new List <DetectedObject>();
                if (length <= 0)
                {
                    return(results);
                }

                // Some validation
                if (hrs == null)
                {
                    throw new ArgumentNullException("hrs");
                }
                if (ups == null)
                {
                    throw new ArgumentNullException("ups");
                }

                // We want UP in 1Hz and HR in 4Hz and an equal number of seconds of UP and HR!
                if (hrs.GetLength(0) != 4 * ups.GetLength(0))
                {
                    throw new InvalidOperationException("Mismatch between FHR and UP signal lengths");
                }

                if ((startIndex < 0) || (ups.GetLength(0) < startIndex + length))
                {
                    throw new InvalidOperationException("Out of bound parameters");
                }

                lock (this.LockObject)
                {
                    // Start the engine if necessary
                    if (!this.IsEngineInitialized)
                    {
                        this.EngineWorkerHandle = SafeNativeMethods.EngineInitialize();
                    }

                    // Make sure the engine was successfully started
                    if (!this.IsEngineInitialized)
                    {
                        throw new InvalidOperationException("Unable to initialize the calculation engine");
                    }

                    // Process the data AS IS
                    int buffer_size = 0;

                    // Simulate a limited seconds feed of 1 day of tracing at once maximum
                    int time_frame = 86400;
                    int position   = startIndex;
                    int end        = startIndex + length;
                    while (position < end)
                    {
                        int block_size = Math.Min(time_frame, end - position);

                        buffer_size = SafeNativeMethods.EngineProcessUP(this.EngineWorkerHandle, ups, position, block_size);
                        buffer_size = SafeNativeMethods.EngineProcessHR(this.EngineWorkerHandle, hrs, 4 * position, 4 * block_size);

                        position += time_frame;
                    }

                    // Read the results
                    if (buffer_size > 0)
                    {
                        var data = new StringBuilder(buffer_size);

                        bool moreData = SafeNativeMethods.EngineReadResults(this.EngineWorkerHandle, data, buffer_size);
                        Debug.Assert(!moreData);

                        string[] lines = data.ToString().Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);


                        foreach (string line in lines)
                        {
                            PatternsProcessorWrapper.Source.TraceEvent(TraceEventType.Error, 1299, "PAW debug: PatternsEngineWrapper::Process {0}", line);


                            var artifact = line.ToDetectionArtifact(this.StartTime);
                            if (artifact != null)
                            {
                                results.Add(artifact);
                            }
                        }
                    }

                    // Count Artifacts
                    PerformanceCounterHelper.AddArtifactsFound(results.Count);

                    // Count Seconds
                    PerformanceCounterHelper.AddSecondsOfTracingProcessed(length);

                    // Remember current time possition
                    this.CurrentTime = this.CurrentTime.AddSeconds(length);

                    return(results);
                }
            }
            catch (Exception ex)
            {
                PatternsProcessorWrapper.Source.TraceEvent(TraceEventType.Error, 1299, "{0} [Process Method]\n:{0}", ex);
                throw;
            }
        }