private void RenameAndHandleNewImageInfo(ReceivedImageInfo info)
        {
            // Format current (Unix) UTC time so we can use it when renaming file.
            DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);

            dateTime = dateTime.AddSeconds(info.triggerInfo.utcTime);
            string formattedTime = dateTime.ToString("yyyyMMdd_hhmmss");

            string originalImageFilename = Path.GetFileName(info.originalFilePath);
            string newFileName           = String.Format("{0}_{1}_{2}_{3}.{4}", InstrumentID, formattedTime, info.imageType, info.imageNumber, info.imageExtension);
            string newFilePath           = Path.Combine(CurrentDataFileDirectory, newFileName);

            // Rename image.  If there's already an image with the same name then delete it first.
            if (File.Exists(newFilePath))
            {
                File.Delete(newFilePath);
            }
            File.Move(info.originalFilePath, newFilePath);

            HandleData(info.triggerInfo.utcTime, info.triggerInfo.sysTime, new List <object>()
            {
                newFileName, info.triggerInfo.sysTime, info.camImageNumber
            });
        }
        void CameraHandler_NewImageDownloaded(string originalFilePath)
        {
            string imageType;      // e.g. IMG or CAM1
            int    camImageNumber; // e.g. 1 or 1852
            string imageExtension; // e.g. JPG or CR2
            bool   parseSuccessful = ParseImageFilePath(originalFilePath, out imageType, out camImageNumber, out imageExtension);

            if (!parseSuccessful)
            {
                SendText(String.Format("Could not parse image \"{0}\"", Path.GetFileName(originalFilePath)));
                return;
            }

            lastReceivedSysTime = SysTime;

            // Detect if camera numbering rolled over from 9999 to 1.
            // Can't just check if new image number is less than last since don't always receive in order.
            // For example could go 9998 -> 1 -> 9999 -> 2 -> 3... and that would only be one rollover.
            if (firstImageReceived)
            {
                if (lastCamImageNumber > 9000 && camImageNumber < 1000)
                {
                    numberOfTimesNumberingRolledOver++;
                }
                else if (lastCamImageNumber < 1000 && camImageNumber > 9000)
                {
                    // Rare case of rolled over, but then got out of order, so need to undo rollover, which should then
                    // shortly be re-done in either the next image or the one after that.
                    numberOfTimesNumberingRolledOver--;
                }
            }

            // Save this so we know for next time.
            lastCamImageNumber = camImageNumber;

            // Convert image number into OUR numbering scheme (i.e. doesn't wrap at 9999)
            int imageNumber = camImageNumber + (numberOfTimesNumberingRolledOver * 9999);

            // Find matching number in trigger info so we know what time the picture was taken at.
            // Iterate backwards through list so we can safetly delete elements.
            // If we haven't received first image yet then we want to use the first triggerInfo and clear the entire list.
            TriggerImageInfo matchingTrigger = null;

            for (int i = triggerImageInfo.Count - 1; i >= 0; i--)
            {
                TriggerImageInfo trigger = triggerImageInfo[i];
                if (!firstImageReceived || (trigger.expectedImageNumber == imageNumber))
                {
                    matchingTrigger = trigger;
                    triggerImageInfo.RemoveAt(i);
                }
            }

            if (matchingTrigger == null)
            {
                SendText(String.Format("Could find matching trigger info for image number {0}", imageNumber));
                DontKnowWhatNumberToExpect();
                return;
            }

            // We have valid information for our newly downloaded image.
            ReceivedImageInfo receivedInfo = new ReceivedImageInfo(originalFilePath, matchingTrigger, imageType, imageNumber, camImageNumber, imageExtension);

            // Set a flag to determine we want to handle data (rename and send) right now or if we need to
            // wait because it's out of order.
            bool handleNewImageRightNow = true;

            if (!firstImageReceived)
            {
                firstImageReceived         = true;
                nextExpectedReceivedNumber = imageNumber + 1;
                nextExpectedTriggerNumber  = imageNumber + 1;
            }
            else // we know what the next number should be, so verify it.
            {
                if (matchingTrigger.expectedImageNumber < nextExpectedReceivedNumber)
                {
                    SendText(String.Format("Received image number {0} is lower than expected {1}. This should never happen.", imageNumber, nextExpectedReceivedNumber));
                    DontKnowWhatNumberToExpect();
                    // return; handle it anyways... it will just be out of order in the log, but still correct.
                }
                else if (matchingTrigger.expectedImageNumber > nextExpectedReceivedNumber)
                {
                    // Received image out of order.  This is normal.  Just buffer it to write it out later.
                    handleNewImageRightNow = false;
                    bufferedImageInfo.Add(receivedInfo);
                }
                else
                {
                    // We received the one we were expecting... so now we should start expecting the next one.s
                    nextExpectedReceivedNumber++;
                }
            }

            if (handleNewImageRightNow)
            {
                RenameAndHandleNewImageInfo(receivedInfo);

                // Handle any other buffered elements that immediately follow this one.
                bool handledBufferImage = true;
                while (handledBufferImage)
                {
                    handledBufferImage = false; // Assume we won't find any matching buffer elements.
                    for (int i = bufferedImageInfo.Count - 1; i >= 0; i--)
                    {
                        ReceivedImageInfo info = bufferedImageInfo[i];
                        if (info.triggerInfo.expectedImageNumber == nextExpectedReceivedNumber)
                        {
                            //SendText("Flushing " + info.imageNumber);
                            RenameAndHandleNewImageInfo(info);
                            nextExpectedReceivedNumber++;
                            bufferedImageInfo.RemoveAt(i);
                            handledBufferImage = true; // so keep looking for next one.
                            break;                     // only try to match one each time through loop.
                        }
                    }
                }
            }
            else
            {
                // Make sure we haven't buffered up too many images.  This is a sign that the image we're expecting is
                // never going to come.  In that case just handle everything we have and start expecting the next one.
                if (bufferedImageInfo.Count > 5)
                {
                    List <ReceivedImageInfo> sortedBufferedImageInfo = bufferedImageInfo.OrderBy(o => o.triggerInfo.expectedImageNumber).ToList();
                    foreach (ReceivedImageInfo info in sortedBufferedImageInfo)
                    {
                        //SendText("Fail Flushing " + info.imageNumber);
                        RenameAndHandleNewImageInfo(info);
                    }
                    DontKnowWhatNumberToExpect();
                    bufferedImageInfo.Clear(); // since we just handled them all.
                }
            }
        }