/// <summary>
 /// Analyzes the input file and writes the output to the AppConf.OutputFileName file.
 /// First checks if the output file already exists and if it is ok to overwrite it.
 /// </summary>
 /// <returns><c>true</c> if the analysis succeeded.</returns>
 public bool Analyze()
 {
     if (File.Exists(AppConf.OutputFileName) && !AppConf.OverWriteOutputFile)
     {
         ApplicationError.Add("Output file already exists", DefaultErrorMessageId);
         return(false);
     }
     return(PrepareEDFFiles() && _neuroLoopGain.Analyze());
 }
        /// <summary>
        /// Prepares the EDF files.
        /// Opens the input file, creates a new ouput file and sets the header information.
        /// </summary>
        /// <returns></returns>
        private bool PrepareEDFFiles()
        {
            if (AppConf.CopyInputSignal)
            {
                _neuroLoopGain.NumOutputSignals = 15;
            }
            else
            {
                _neuroLoopGain.NumOutputSignals = 14;
            }


            double[] sFrecs = new double[_neuroLoopGain.NumOutputSignals];

            // Calculate best value for output EDF datablock duration according to analysis parameters

            try
            {
                //OPEN INPUT FILENAME
                _neuroLoopGain.InputEDFFile        = new EdfFile(InputEDFFileName, true, false, false, true);
                _neuroLoopGain.InputSignalSelected = InputSignalSelected;

                int numInputSignals = _neuroLoopGain.InputEDFFile.FileInfo.NrSignals;
                _neuroLoopGain.InputBufferOffsets = new int[numInputSignals];
                for (int k = 0; k < numInputSignals; k++)
                {
                    _neuroLoopGain.InputBufferOffsets[k] = _neuroLoopGain.InputEDFFile.SignalInfo[k].BufferOffset;
                }

                //OUTPUT EDF
                EdfFile outputEDF = new EdfFile(AppConf.OutputFileName, false, false, false, false);
                //EdfFile outputEDF = new EdfFile(AppConf.OutputFileName, false, true, false, false);

                EdfSignalInfoBase edfSignalInfo = _neuroLoopGain.InputEDFFile.SignalInfo[InputSignalSelected];

                outputEDF.CreateNewFile(_neuroLoopGain.NumOutputSignals);

                //PreCondition checked before: (1 / AppConf.SmoothTime) is between 1 and sFrecs[0]
                double doubleValue = MathEx.RoundNearest(1 / AppConf.SmoothTime);
                Range.EnsureRange(doubleValue, 0, _neuroLoopGain.InputSampleFrequency);

                for (int k = 0; k < _neuroLoopGain.NumOutputSignals; k++)
                {
                    sFrecs[k] = doubleValue;
                }
                if (AppConf.CopyInputSignal)
                {
                    sFrecs[0] = _neuroLoopGain.InputSampleFrequency;
                }

                //TODO: check this
                List <EdfDataBlockSizeCalculatorResult> results = new List <EdfDataBlockSizeCalculatorResult>();
                double dataBlockDuration = EdfDataBlockSizeCalculator.Calculate(sFrecs, MaxEdFblockSize, results);
                if (!MathEx.SameValue(dataBlockDuration, -1))
                {
                    // Look for minimun possible error
                    double bestError = results[0].MaxRelativeError;
                    int    bestIdx   = 0;
                    for (int k = 1; k < results.Count; k++)
                    {
                        if (results[k].MaxRelativeError < bestError)
                        {
                            bestError = results[k].MaxRelativeError;
                            bestIdx   = k;
                        }
                        else if (MathEx.SameValue(results[k].MaxRelativeError, bestError) && (results[k].Duration > results[bestIdx].Duration) && (results[k].Duration <= MaxBlockDuration))
                        {
                            // Try to get block durations as much as possible to avoid excesive disk reads
                            bestIdx = k;
                        }
                    }
                    if (!MathEx.SameValue(bestError, 0))
                    {
                        //TODO: error??, not valid SUSSsmoothingTime parameter (we're gonna miss some samples)
                        ApplicationError.Add("not valid SmoothTime parameter (we're gonna miss some samples)", DefaultErrorMessageId);
                        ErrorLogger.WriteErrorLog("not valid SmoothTime parameter (we're gonna miss some samples). Error = " + bestError.ToString());
                        outputEDF.FileInfo.SampleRecDuration = results[bestIdx].Duration;
                    }
                    else
                    {
                        outputEDF.FileInfo.SampleRecDuration = results[bestIdx].Duration; // Duration in seconds of block record
                        AppConf.SmoothTime = 1.0 / sFrecs[1];                             // Reset the value of SmoothTime to the one actually used by the system
                    }
                }
                else
                {
                    ApplicationError.Add("failed to assign a data block duration for output EDF", DefaultErrorMessageId);
                    ErrorLogger.WriteErrorLog("failed to assign a data block duration for output EDF");
                }

                _neuroLoopGain.OutputBufferOffsets    = new int[15];
                _neuroLoopGain.OutputBufferOffsets[0] = 0;

                for (int k = 0; k < _neuroLoopGain.NumOutputSignals; k++)
                {
                    outputEDF.SignalInfo[k].PreFilter = edfSignalInfo.PreFilter;
                    double nsamples = outputEDF.FileInfo.SampleRecDuration * sFrecs[k];
                    if (Math.Truncate(nsamples) < nsamples)
                    {
                        //TODO: Error?? the number of samples in a data block should be an integer number
                        ApplicationError.Add("not valid SmoothTime parameter: we are going miss some samples", DefaultErrorMessageId);
                        ErrorLogger.WriteErrorLog("not valid SmoothTime parameter: we are going miss some samples");

                        /* It should effectively be an integer number, but if we admit some error to happen
                         * (bestError != 0), then we can work with the truncated value => we're gonna miss some
                         * sample during the process
                         */
                        nsamples = Math.Truncate(nsamples);
                    }
                    outputEDF.SignalInfo[k].NrSamples = (int)nsamples;
                    //if (k == 0)
                    if ((k == 0) && AppConf.CopyInputSignal)
                    {
                        outputEDF.SignalInfo[k].DigiMax  = edfSignalInfo.DigiMax;
                        outputEDF.SignalInfo[k].DigiMin  = edfSignalInfo.DigiMin;
                        outputEDF.SignalInfo[k].PhysiMax = edfSignalInfo.PhysiMax;
                        outputEDF.SignalInfo[k].PhysiMin = edfSignalInfo.PhysiMin;
                        outputEDF.SignalInfo[k].PhysiDim = edfSignalInfo.PhysiDim;
                    }
                    else
                    {
                        outputEDF.SignalInfo[k].DigiMax = short.MaxValue;
                        outputEDF.SignalInfo[k].DigiMin = -short.MaxValue;
                        //if (k < 9)
                        if (k < _neuroLoopGain.NumOutputSignals - 6)
                        {
                            outputEDF.SignalInfo[k].PhysiDim = "Filtered";
                            outputEDF.SignalInfo[k].PhysiMax = short.MaxValue;
                            outputEDF.SignalInfo[k].PhysiMin = -short.MaxValue;
                        }
                        else
                        //if ((k >= 9) && (k < 12))
                        if ((k >= _neuroLoopGain.NumOutputSignals - 6) && (k < _neuroLoopGain.NumOutputSignals - 3))
                        {
                            outputEDF.SignalInfo[k].PhysiMax = short.MaxValue;
                            outputEDF.SignalInfo[k].PhysiMin = -short.MaxValue;
                        }
                        else
                        {
                            outputEDF.SignalInfo[k].PhysiDim = "%";
                            outputEDF.SignalInfo[k].PhysiMax = short.MaxValue / AppConf.MicGain;
                            outputEDF.SignalInfo[k].PhysiMin = -short.MaxValue / AppConf.MicGain;
                        }
                        if (k > 0)
                        {
                            if (AppConf.CopyInputSignal)
                            {
                                _neuroLoopGain.OutputBufferOffsets[k] = _neuroLoopGain.OutputBufferOffsets[k - 1] + outputEDF.SignalInfo[k - 1].NrSamples;
                            }
                            else
                            {
                                _neuroLoopGain.OutputBufferOffsets[k + 1] = _neuroLoopGain.OutputBufferOffsets[k] + outputEDF.SignalInfo[k].NrSamples;
                            }
                        }
                    }

                    outputEDF.SignalInfo[k].Reserved = edfSignalInfo.Reserved;
                    //if ((k > 0) && (k < 9))
                    if ((k >= _neuroLoopGain.NumOutputSignals - 14) && (k < _neuroLoopGain.NumOutputSignals - 6))
                    {
                        if (AppConf.CopyInputSignal)
                        {
                            outputEDF.SignalInfo[k].SignalLabel = _signalOutputLabels[k] + " " + edfSignalInfo.PhysiDim + "**2/x";
                        }
                        else
                        {
                            outputEDF.SignalInfo[k].SignalLabel = _signalOutputLabels[k + 1] + " " + edfSignalInfo.PhysiDim + "**2/x";
                        }
                    }
                    else
                    if (AppConf.CopyInputSignal)
                    {
                        outputEDF.SignalInfo[k].SignalLabel = _signalOutputLabels[k];
                    }
                    else
                    {
                        outputEDF.SignalInfo[k].SignalLabel = _signalOutputLabels[k + 1];
                    }
                    outputEDF.SignalInfo[k].TransducerType    = edfSignalInfo.TransducerType;
                    outputEDF.SignalInfo[k].ThousandSeparator = edfSignalInfo.ThousandSeparator;
                }

                // Info for MC analysis
                outputEDF.FileInfo.Recording = string.Format(CultureInfo.InvariantCulture,
                                                             "Startdate {0} NeuroLoop-gain analysis at {1:#.#}Hz of {2} in {3}",
                                                             _neuroLoopGain.InputEDFFile.FileInfo.StartDate.HasValue ? _neuroLoopGain.InputEDFFile.FileInfo.StartDate.Value.ToString("dd-MMM-yyyy", CultureInfo.InvariantCulture) : "X",
                                                             sFrecs[1],
                                                             _neuroLoopGain.InputEDFFile.SignalInfo[InputSignalSelected].SignalLabel, Path.GetFileName(InputEDFFileName));

                //Copy header info from the original signal
                outputEDF.FileInfo.Patient   = _neuroLoopGain.InputEDFFile.FileInfo.Patient;
                outputEDF.FileInfo.StartDate = _neuroLoopGain.InputEDFFile.FileInfo.StartDate;
                outputEDF.FileInfo.StartTime = _neuroLoopGain.InputEDFFile.FileInfo.StartTime;

                outputEDF.CommitChanges();

                _neuroLoopGain.MCsignalsBlockSamples = outputEDF.SignalInfo[1].NrSamples;
                _neuroLoopGain.OutputEDFFile         = outputEDF;
            }

            catch (Exception e)
            {
                ApplicationError.Add("Error opening input and/or ouput files: " + e.Message, DefaultErrorMessageId);
                ErrorLogger.WriteErrorLog("Error opening input and/or ouput files: " + e.Message);
            }

            return(!ApplicationError.Signaled);
        }