/// <summary>
        ///     Prepare. This method will be invoked after end of configuration and before the first GetValues call
        /// </summary>
        public override void Prepare()
        {
            try
            {
                if (!initializeWasInvoked)
                {
                    throw new Exception("PrepareForComputation method in SmartWrapper cannot be invoked before the Initialize method has been invoked");
                }

                Validate();

                if (validationErrorMessages.Count > 0)
                {
                    var errorMessage = "";
                    foreach (string str in validationErrorMessages)
                    {
                        errorMessage += "Error: " + str + ". ";
                    }

                    throw new Exception(errorMessage);
                }

                /*foreach (CacheOutputLink cacheOutputLink in _cacheOutputLinks)
                {
                    cacheOutputLink.UpdateBuffer();
                }*/

                // start the link monitor
                //LinkMonitor.Instance.Start(this.cacheOutputLinks, this.traceFile);

                // MUST BE DONE AFTER LNKS ADDED
                prefetchHelper = new PrefetchManager(traceFile, statistics, client, scenarioId, dataOutputLinks, TimeHorizon, enablePrefetching, webServiceManager);

                //PrefetchMonitor.Instance.Start(this.dataOutputLinks, this.traceFile);
                //PrefetchMonitor.Instance.setTimeHorizon(this.TimeHorizon);

                prepareForCompotationWasInvoked = true;
            }
            catch (Exception e)
            {
                var message = "Exception in LinkableComponent. ";
                message += "ComponentID: " + ComponentID + "\n";
                throw new Exception(message, e);
            }
        }
        /**
         * Returns the ValueSet that corresponds to requestTime.
         */
        public IValueSet GetValues(ITime requestedTime, IQuantity quantity, IElementSet elementSet, PrefetchManager prefetchManager, ILink link)
        {
            try
            {
                // generate the key of the buffer entry we're looking for
                var key = ValueSetEntry.CreateKey(webServiceManager.FindServiceIdForQuantity(quantity.ID), quantity.ID, elementSet.ID, requestedTime, scenarioId);

                traceFile.Append("GetValues: " + key);

                var mapValueSet = client.getMap<string, ValueSetEntry>("valueSet");
                var mapElementSet = client.getMap<string, ElementSetEntry>("elementSet");
                var queueValueSetRequest = client.getQueue<ValueSetRequestEntry>("valueSetRequest");

                // record this request
                statistics.Add("GetValuesCount", 1);

                // measure our wait time
                var waitStopwatch = Stopwatch.StartNew();

                // see if the requested values are in the cache
                var valueSetEntry = mapValueSet.get(key);

                // if the value set does not exist then request it
                if (valueSetEntry == null)
                {
                    traceFile.Append("Requesting Value Set: " + key);

                    // insert the element set if necessary
                    if (elementSetsPut.Contains(elementSet.ID) == false)
                    {
                        elementSetsPut.Add(elementSet.ID);
                        mapElementSet.put(elementSet.ID, new ElementSetEntry(elementSet));
                    }

                    while (valueSetEntry == null)
                    {
                        // if we're prefetching, we may have already requested this
                        // value set in a previous prefetch that hasn't been fulfilled
                        // yet in which case we do not want to issue the request again.
                        if (prefetchManager.timeIsFetched(link, (TimeStamp)requestedTime) == false)
                        {
                            // insert the request into the queue
                            var insertRequestStopwatch = Stopwatch.StartNew();

                            // create the request entry
                            var valueSetRequestEntry = new ValueSetRequestEntry(webServiceManager.FindServiceIdForQuantity(quantity.ID), quantity.ID, elementSet.ID, Utils.ITimeToDateTime(requestedTime), scenarioId);

                            // BLOCKING
                            queueValueSetRequest.put(valueSetRequestEntry);

                            statistics.Add("RequestInsertTimeMS", insertRequestStopwatch.ElapsedMilliseconds);
                            traceFile.Append("RequestInsertTime:" + string.Format("{0:0.0}", insertRequestStopwatch.ElapsedMilliseconds) + "ms");
                        }

                        // PERFORMANCE STUDY: start delay at 100 and double each time
                        // we check and it's not available

                        // poll for the value set
                        var delay = 1000;
                        while (true)
                        {
                            // we know that the value set is not in the cache since
                            // we just checked that above, so wait immediately after
                            // making the request
                            Thread.Sleep(delay);

                            valueSetEntry = mapValueSet.get(key);
                            if (valueSetEntry != null)
                            {
                                break;
                            }
                            //delay *= 2;
                            traceFile.Append(string.Format("Waiting ({0}) For Value Set: ({1})", delay, key));

                            if (delay > 20000)
                            {
                                statistics.Add("RequestRetry", 1);
                                break;
                            }
                        }
                    }
                }

                statistics.Add("CacheWaitTimeMS", waitStopwatch.ElapsedMilliseconds);
                traceFile.Append("WaitTime:" + string.Format("{0:0.0}", waitStopwatch.ElapsedMilliseconds) + "ms");

                return new ScalarSet(valueSetEntry.Values());
            }
            catch (Exception e)
            {
                traceFile.Exception(e);
                return null;
            }
        }
        /// <summary>
        ///     Retrieves a value from the buffer that applies to the time passes as argument.
        ///     During this process the buffer will do temporal operations,
        ///     such as extrapolations, interpolations, or aggregation
        /// </summary>
        /// <param name="time">The time for which the values should apply</param>
        /// <returns>The values</returns>
        public virtual IValueSet GetValue(ITime time, PrefetchManager prefetchHelper)
        {
            // retrieve the values from the cache
            var values = cacheManager.GetValues(time, link.SourceQuantity, link.SourceElementSet, prefetchHelper, link);

            // mark this value as already fetched
            prefetchHelper.addFetchedTime(link, (TimeStamp)time);

            // perform any new prefetching based on this request
            prefetchHelper.Update(link);

            if (_linearDataOperation != null)
            {
                values = _linearDataOperation.PerformDataOperation(values);
            }

            return ConvertUnit(values);
        }