// Main method - compute ROP from changes into block height in time, using a rig state (rotary drilling, slide drilling, or
        // oscillate slide drilling) to determine if the bit depth needs to be calculated.  Currently this uses a temporary single state
        // (enumeration = 2, which should be drilling)
        public Data ComputeBitDepth(Data Data)
        {
            // Find the Rig State data column
            DataValues blockHeight = utility.FindDataColumn( Data, "Block_Height");
            DataValues rigStates = utility.FindDataColumn(Data, "Rig_State");

            // Create output 
            DataValues bitDepth = new DataValues();
            bitDepth.Name = "Bit_Depth";
            bitDepth.Units = blockHeight.Units;
            bitDepth.Raw = false;
            double oldBitDepth = 0;
            double oldBlockHeight = 0;

            // Create data array storage for output
            bitDepth.DataColumn = new List<DataValue>();

            /// do the algorithm
            for (int i = 0; i < blockHeight.DataColumn.Count; i++)
            {
                // Get calculation values
                DateTime t = Convert.ToDateTime(blockHeight.DataColumn[i].Timestamp);
                double val = Convert.ToDouble(blockHeight.DataColumn[i].Value);
                string rigState = RigStateBitDepth[Convert.ToInt32(rigStates.DataColumn[i].Value)];
                double bd = 0;

                if (i == 0)
                {
                    // Trap first time through algorithm - need at least two values to compute ROP
                    bd = 0;
                    oldBitDepth = 0;
                    oldBlockHeight = val;
                }
                else if (val == -999.25)
                {
                    // Trap schlumberger null value
                    bd = val;
                }
                else
                {
                    // Compute ROP
                    if (rigState == "Drilling" || rigState == "Reaming" || rigState == "TripIn" || rigState == "TripOut")
                    {
                        bd = oldBitDepth + (oldBlockHeight - val);
                        oldBitDepth = bd;
                    }
                    else
                    {
                        bd = oldBitDepth;
                    }
                    oldBlockHeight = val;
                }

                /// Put you output back into Data
                bitDepth.DataColumn.Add(new DataValue(t, bd.ToString()));
            }
            Data.DataList.Add(bitDepth);
            return (Data);
        }
        // Main method - compute dummy stub.  
        public Data ComputeDummy(Data Data)
        {
            /// Get your needed input data here

                // Example - get rig state and blovk height
                DataValues rigStates = util.FindDataColumn(Data, "Rig_State");
                DataValues blockHeights = util.FindDataColumn(Data, "Block_Height");

            // Define you data output channels here
                // Create an output data column to store results back into Data
                DataValues Output = new DataValues();
                Output.Name = "Your output data column name";
                Output.Units = "Your output data units";

                // Create data array storage for output
                Output.DataColumn = new List<DataValue>();


            // Put you calculation logic here

                // Example do dummy calculation - remember to cast values to the proper type (string, double, int, ....)
                double dummy = 0;
                foreach (DataValue dv in blockHeights.DataColumn)
                {
                    DateTime t = Convert.ToDateTime(dv.Timestamp);
                    double val = Convert.ToDouble(dv.Value);

                    dummy = dummy + val;

                    /// Store result back into Data
                    Output.DataColumn.Add(new DataValue(t, dummy.ToString()));
                }

                // or

                for (int i = 0; i < blockHeights.DataColumn.Count; i++)
                {
                    DateTime t = Convert.ToDateTime(blockHeights.DataColumn[i].Timestamp);
                    double val = Convert.ToDouble(blockHeights.DataColumn[i].Value);
                    string rigState = RigStateDummy[Convert.ToInt32(rigStates.DataColumn[i].Value)];

                    if (rigState == "Drilling")
                    {
                        dummy = dummy + val;
                    }
                    else
                    {
                        dummy = dummy - val;
                    }

                    /// Put you output back into Data
                    Output.DataColumn.Add(new DataValue(t, dummy.ToString()));
                }

            // Return the updated Data to the master routines
            Data.DataList.Add(Output);
            return (Data);
        }
        // This method checks for the existence of a column in Data.  It is used primarily in checking/versioning the trace name, as multiple traces with the same name
        // will cause identity issues betwee traces of the same name
        public bool FindIfDataColumnExists(Data Data, string name)
        {
            bool retVal = false;

            foreach (DataValues dv in Data.DataList)
            {
                if (dv.Name.ToLower() == name.ToLower())
                {
                    retVal = true;
                    break;
                }
            }

            return (retVal);
        }
        // These methods interact with Data to return information about data traces or columns
        // These are effectively the memory management for Data

        // This method returns an data column (header and values) based on the input column name
        public DataValues FindDataColumn(Data Data, string name)
        {
            DataValues Column = null;

            // For each data column find a match of the of column name and requested column name
            // if there is a match, break out of the search and return the data column
            foreach (DataValues dv in Data.DataList)
            {
                if (dv.Name.ToLower() == name.ToLower())
                {
                    Column = dv;
                    break;
                }
            }
            return (Column);
        }
        // This method find the integer index associated with a requested column name
        public int FindDataColumnIndex(Data Data, string name)
        {
            // index counter to be output
            int i = 0;

            // Loop through the columns in Data, and if there is a match of column name and reqwuested column name,
            // then return the index of the column array.  This is quite useful for locating the DateTime column.
            // It also eliminates a fixed position for the DateTime column in the input file
            foreach (DataValues dv in Data.DataList)
            {
                if (dv.Name.ToLower() == name.ToLower())
                {
                    break;
                }
                i++;
            }
            return (i);
        }
        // Main method - compute bit depth from changes into block height in time, using a rig state (rotary drilling, slide drilling, or
        // oscillate slide drilling) to determine if the ROP needs to be calculated.  Currently this uses a temporary single state
        // (enumeration = 2, which should be drilling)
        public Data ComputeRop(Data Data)
        {
            // Find the Rig State data column
            DataValues rigStates = util.FindDataColumn(Data, "Rig_State");

            // If we have rig state proceed
            if (rigStates != null)
            {
                // Find the block height column, if it exists proceed
                DataValues blockHeights = util.FindDataColumn(Data, "Block_Height");
                if (blockHeights != null)
                {
                    // Computation algorithm
                    // If the rig state is "2" then compute ROP from the current and previous block heights and timestamps
                    // We keep the previous block height and timestamp in startXXX variables and the current sample in endXXX 

                    // Create output space
                    double outRop = 0;

                    // load the first values of block height and its' timestamp into the startXXX varaible for initialization
                    double startBlockHeight = Convert.ToDouble(blockHeights.DataColumn[0].Value);
                    DateTime startTime = blockHeights.DataColumn[0].Timestamp;

                    //

                    // Create an output data column to store results back into Data
                    DataValues DataValues = new DataValues();
                    DataValues.Name = "Computed_ROP Approach 1";
                    DataValues.Units = "ft/hr";
                    DataValues.DataColumn = new List<DataValue>();
                    DataValues.DataColumn.Add(new DataValue(startTime, outRop.ToString()));

                    // Main computation Loop
                    // For each data array index, extract the values of rig state, block height, and the timestamp
                    // If the state is Drilling, then use current and previous value of block height and timestamp to compute ROP
                    for (int i = 1; i < rigStates.DataColumn.Count; i++)
                    {
                        // Extract the values of rig state, block height, and the timestamp
                        string rigState = string.Empty;
                        try
                        {
                            rigState = RigStateROP[Convert.ToInt32( rigStates.DataColumn[i].Value)];
                        }
                        catch
                        {
                            rigState = string.Empty;
                        }
                        double endBlockHeight = Convert.ToDouble(blockHeights.DataColumn[i].Value);
                        DateTime endTime = blockHeights.DataColumn[i].Timestamp;

                        // If we are "drilling, compute ROP, else ROP = 0
                        if (rigState == "Drilling")
                        {
                            /// Trap schlumberger null
                            if (endBlockHeight == -999.25)
                            {
                                outRop = 0;
                            }
                            else
                            {
                                // Compute the difference in time in seconds, and convert the number to a double
                                int diff = (endTime - startTime).Seconds;
                                double div = Convert.ToDouble(Convert.ToDouble(diff));

                                // Error trap for two samples with the same timestamp, compute ROP
                                if (div != 0)
                                {
                                    outRop = 3600 * (endBlockHeight - startBlockHeight) / div;
                                }
                            }

                            // Trap to catch bad rig state values
                            //if (outRop < 0)
                            //{
                            //    outRop = 0;
                            //}
                        }
                        else
                        {
                            // Wrong rig state for ROP calc
                            outRop = 0;
                        }

                        //Final trap throw out ROP < 0
                        if (outRop < 0)
                        {
                            outRop = 0;
                        }

                        // Take the calculated ROP and append the value to the Data column values
                        DataValues.DataColumn.Add(new DataValue(endTime, outRop.ToString()));
                        startTime = endTime;
                        startBlockHeight = endBlockHeight;
                    }

                    // Add the newly created ROP column to Data
                    DataValues.Raw = false;
                    if (util.FindIfDataColumnExists(Data, DataValues.Name))
                    {
                        DataValues.Name = util.GenerateVersonedName(Data, DataValues.Name);
                    }
                    Data.DataList.Add(DataValues);
                }
                else
                {
                    /// no block height found
                }
            }
            else
            {
                /// no rig state found
            }

            return (Data);
        }
        // This method checks to see if a trace of the same name already exisits in Data (case: when the same caluculation is run twice, the output will already
        // exist in Data, so we change the name so that the traces are distinguishable.
        public string GenerateVersonedName(Data Data, string name)
        {
            string retVal = string.Empty;
            string newName = string.Empty;
            int i = 1;

            bool tryAgain = true;
            while(tryAgain) 
            {
                newName = name + " version " + i.ToString();
                tryAgain = FindIfDataColumnExists(Data, newName);
                i++;
            }
            retVal = newName;
            return (retVal);
        }
        // This region contains the code for reading, writing and manipulating the Data
        #region CSV file IO

        private void ReadInputCSV(string filename)
        {
            // Opens the input CSV data file and stores it in a temporary string, then parses the string into
            // data columns, by first reading the file header, then loads the data into Data the application
            // memory space.  Data contains each trace,  which is data vector, array, or column.  Each
            // sample in a data column consists of a data value, timestamp pair.  Column name and units are
            // stored in the data column header.

            // Read the filename from the DataInputFile textbox, open the file and read it into a string
            string rawData = File.ReadAllText(filename);

            // Remove carriage return issues and break the raw data string into  lines
            rawData = rawData.Replace('\n', '\r');
            string[] lines = rawData.Split(new char[] { '\r' }, StringSplitOptions.RemoveEmptyEntries);

            // Determine the number of rows and columns in the inoput data
            int rows = lines.Length;
            int columns = lines[0].Split(',').Length;

            // Transfer the string based values into Data, the master app database
            // Create Data root and root stub for the data columns (arrays)
            if (columns > 0)
            {
                Data = new Data();
                List<DataValues> DataValues = new List<DataValues>();
                Data.DataList = DataValues;
            }

            // Read Column Names and allocate space for each column of the data array

            // Assumes that there are two header lines - the first for names , the second for units
            string[] line = lines[0].Split(',');
            string[] unitLine = lines[1].Split(',');

            // Read the first two lines, then for each column read the data column name and units, 
            // then create and link each data column in Data 
            for (int i = 0; i < columns; i++)
            {
                DataValues dv = new DataValues();
                dv.Name = line[i].ToString();
                dv.Units = unitLine[i].ToString();

                dv.DataColumn = new List<DataValue>();
                Data.DataList.Add(dv);
            }

            // Now that the columns names are stored in Data, find the DateTime column
            // We need to find this column as each data value has a timestamp associated with it.
            //
            // A more effiecent way of doing this is to find a data samples index in the Data array,
            // then lookup the index in the timestamp array to find the associated timestamp for the sample.
            // However, we intend to have some form of graphic display, so having a value, timestamp pair
            // will be more efficient for plots and charts
            int tsIndex = util.FindDataColumnIndex(Data, "DateTime");
            
            // Now read the remaining lines into the data columns
            // Start at the third line in the data file
            for (int i = 2; i < rows; i++)
            {
                line = lines[i].Split(',');

                // For each data row read the input columns and create a data value
                for (int j = 0; j < columns; j++)
                {
                    DataValue dv = new DataValue();
                    dv.Timestamp = Convert.ToDateTime(line[tsIndex].ToString());
                    dv.Value = line[j].ToString();
                    Data.DataList[j].DataColumn.Add(dv);
                }
            }
        }
 private void ComputeBitDepthClick(object sender, EventArgs e)
 {
     // Bit Depth Approach 1 calculation
     try
     {
         ComputeBitDepthClass calc = new ComputeBitDepthClass();
         Data = calc.ComputeBitDepth(Data);
         AddButtons("calculated");
     }
     catch
     {
     }
 }