//btnDecrypt Click Event starting the decryption
        private void btnDecrypt_Click(object sender, EventArgs e)
        {
            txtBxDecrypt.Text = "";
            OpenFileDialog curDialog = new OpenFileDialog();

            if (curDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                using (Bitmap curBitmap = curIO.fileValidImage(curDialog.FileName))
                {
                    if (curBitmap != null)
                    {
                        Decrypting curDecrypting = new Decrypting((Color)cmbBxDecryptStart.SelectedItem, curBitmap, curIO, (int)numUpDwnDecrypt.Value);
                        if (!curDecrypting.errors)
                        {
                            txtBxDecrypt.Text = curDecrypting.decryptedString;
                        }
                        else
                        {
                            displayError("Either the input parameters or the image are wrong.", curDecrypting.errorMessage, false);
                        }
                    }
                    else displayError("Please, selected a valid image file.", null, false);
                }
            }
        }
        public Point startPixelOrig; //Stores the "fake start point", relevant while analysing the next pixels storing length information

        #endregion Fields

        #region Constructors

        //---
        //Starting actions on account of the given process (encryption/decription)
        public Hashing(Color temp_startColor, Encrypting curEncrypting, Decrypting curDecrypting, IO temp_curIO)
        {
            startColor = temp_startColor;
            curIO = temp_curIO;
            curColors = new List<Color>(curIO.allKnownColors);
            startOffset = new startOffsetInfo();

            if (curEncrypting != null)
            {
                if (curEncrypting.origText == null || curEncrypting.origText.Length < 1)
                {
                    errors = true;
                    return;
                }
                curLength = curEncrypting.origText.Length + 2 + (curEncrypting.origText.Length + 2).ToString().Length; //Length considered while decrypting
                encryptingNumber = curEncrypting.encryptingNumber;
            }
            else
            {
                if (curDecrypting.origBitmap == null)
                {
                    errorMessage = "There was a problem while accessing the image file.";
                    errors = true;
                    return;
                }

                encryptingNumber = curDecrypting.encryptingNumber;
                pixelsToIgnore = new List<Point>();

                //Preliminary validity check by looking for the startColor (in the right position) and determining the length (and the offset, if applicable)
                curIO.getBoundaryPixels(curDecrypting, this);

                if (curLength <= 0)
                {
                    errors = true;
                    return;
                }
            }

            calculateStartStep(); //Calculating curStep

            buildCharsVsColors(); //Building the hash table (dictionary with all the accounted characters and their equivalent color) which will be used in the current encryption/decryption process

            if (curDecrypting != null)
            {
                //For decryption. Confirming that the length value, calculated above, matches the information in the pixels enconding the actual length.
                //The reason for not having this analysis before is the fact that the charsVsColors is required
                if (!curIO.lengthIsOK(curDecrypting, this)) this.errors = true;
            }
        }
        //There is an offset and thus the actual start of the encrypted chunk is certain; but this determination is not evident as far as it is stored as an integer (= distance).
        //This integer has to be converted into actual pixel/point position; this is what this function does: returning the actual start point by bringing the offset into account
        private Point newStartFromOffset(Point curStart, int offSet, Decrypting curDecrypting)
        {
            Point newStart = new Point();

            int curCount = 0;
            bool firstTime = true;
            for (int x = curDecrypting.origBitmap.Width - 1; x >= 0; x--)
            {
                for (int y = curDecrypting.origBitmap.Height - 1; y >= 0; y--)
                {
                    if (firstTime)
                    {
                        x = curStart.X;
                        y = curStart.Y;
                        firstTime = false;
                    }

                    curCount = curCount + 1;

                    if (curCount > offSet)
                    {
                        newStart.X = x;
                        newStart.Y = y;
                        break;
                    }
                }

                if (curCount >= offSet) break;
            }

            return newStart;
        }
        //Last check performed while hashing (for decryption): the previously-calculated length has to match the value encrypted in some of the pixels of the image in order
        //to confirm that this is a right image and to go ahead with the decryption
        public bool lengthIsOK(Decrypting curDecrypting, Hashing curHashing)
        {
            bool isOK = true;

            curHashing.pixelsLength = new pixelsLengthInfo();

            int count = -1;
            int calcLength = 0;
            string lengthString = "";

            bool firstTime = true;
            bool foundStart = false;

            int startVal = 1;
            if (curHashing.startOffset.startOffsetCount > -1) startVal = startVal + 1; //"After pixel" of the offSet has also to be skipped

            for (int x = 0; x < curDecrypting.origBitmap.Width; x++)
            {
                for (int y = 0; y < curDecrypting.origBitmap.Height; y++)
                {
                    if (firstTime)
                    {
                        x = curHashing.startPixelOrig.X;
                        y = curHashing.startPixelOrig.Y;
                        firstTime = false;
                    }

                    count = count + 1;

                    if (count >= startVal) //to skip startColor and, eventually, offset after pixel
                    {
                        Color curColor = curDecrypting.origBitmap.GetPixel(x, y);
                        KeyValuePair<char, Color> tempPair = curHashing.charsVsColors.FirstOrDefault(x2 => twoColorsAreEqual(x2.Value, curColor));
                        if (Char.IsDigit(tempPair.Key))
                        {
                            curHashing.pixelsToIgnore.Add(new Point(x, y));
                            lengthString = lengthString + tempPair.Key.ToString();
                            if (!foundStart)
                            {
                                curHashing.pixelsLength.startPixel = new Point(x, y);
                                foundStart = true;
                            }
                        }
                        else
                        {
                            return false;
                        }

                        if (int.TryParse(lengthString, out calcLength))
                        {
                            if (calcLength == curHashing.curLength)
                            {
                                curHashing.pixelsLength.length = calcLength;
                                curHashing.pixelsLength.endPixel = new Point(x, y);
                                return true;
                            }
                        }
                        else return false;
                    }
                }
            }

            return isOK;
        }
        //--------------------
        //------------------------------- DECRYPTING
        //Method call soon in the hashing (for decrypting) process: it does not only extract valuable information (start pixel, length and, eventually, offset),
        //but also avoids any wrong image to be analysed further.
        public void getBoundaryPixels(Decrypting curDecrypting, Hashing curHashing)
        {
            Point tempLast = new Point(-1, -1);
            int tempLength = -1;
            int lastLength = 0;
            Color nextPixel = new Color();
            bool storeNext = false;
            curHashing.curLength = -1;

            for (int x = 0; x < curDecrypting.origBitmap.Width; x++)
            {
                for (int y = 0; y < curDecrypting.origBitmap.Height; y++)
                {
                    if (tempLength >= 0) tempLength = tempLength + 1;

                    Color curColor = curDecrypting.origBitmap.GetPixel(x, y);
                    if (storeNext)
                    {
                        //This is the "after pixel" which confirms that an offset actually exists
                        if (!twoColorsAreEqual(curColor, curHashing.startColor))  nextPixel = curColor;
                        storeNext = false;
                    }

                    //StartColor determines where the "relevant chunk" starts/ends, what is done by this condition
                    if (twoColorsAreEqual(curColor, curHashing.startColor))
                    {
                        if (tempLength < 0)
                        {
                            curHashing.startPixel = new Point(x, y);
                            curHashing.startPixelOrig = new Point(x, y);
                            storeNext = true;
                            tempLength = 0;
                        }
                        else
                        {
                            tempLast = new Point(x, y);
                            lastLength = tempLength;
                        }
                    }
                }
            }

            if (tempLast != new Point(-1, -1) && lastLength > 0)
            {
                //The collected information seems right

                curHashing.endPixel = new Point(tempLast.X, tempLast.Y);
                curHashing.curLength = lastLength + 1;

                int afterIndex = curHashing.curIO.allKnownColors.IndexOf(curHashing.startColor) + curDecrypting.encryptingNumber;
                if (afterIndex > curHashing.curIO.allKnownColors.Count - 1) afterIndex = curDecrypting.encryptingNumber;
                Color afterOffset = curHashing.curIO.allKnownColors[afterIndex];

                bool analyseOffset = !nextPixel.IsEmpty && twoColorsAreEqual(afterOffset, nextPixel);

                if (analyseOffset)
                {
                    //There is an offset and thus some variables have to be updated
                    curHashing.curLength = curHashing.curLength - 1; //afterOffset is not included withing the length value
                    curHashing.startOffset.afterOffset = afterOffset;
                    curHashing.startOffset.startOffsetCount = curDecrypting.encryptingNumber;
                    curHashing.startOffset.startPoint = new Point(curHashing.startPixel.X, curHashing.startPixel.Y);
                    curHashing.curLength = curHashing.curLength + curHashing.startOffset.startOffsetCount;
                    curHashing.startPixel = newStartFromOffset(curHashing.startPixel, curHashing.startOffset.startOffsetCount, curDecrypting);
                }

                lengthCorrection(curHashing);
            }
        }
        //Final stage of the decryption process: application of all the previous calculations to the image to extract the encrypted message
        public Decrypting decryptImage(Decrypting curDecrypting)
        {
            Bitmap curBitmap = curDecrypting.origBitmap;

            curDecrypting.decryptedString = "";

            Hashing curHashing = curDecrypting.curHashing;
            bool origFound = false;
            bool firstTime = true;
            bool decryptColor = false;

            for (int x = 0; x < curDecrypting.origBitmap.Width; x++) //The analysis starts from the startPixel, already calculated while creating the Hash table
            {
                for (int y = 0; y < curDecrypting.origBitmap.Height; y++)
                {
                    bool analyseStart = false;
                    bool goAhead = false;
                    if (firstTime)
                    {
                        x = curDecrypting.curHashing.startPixel.X;
                        y = curDecrypting.curHashing.startPixel.Y;
                    }
                    Color curColor = curDecrypting.origBitmap.GetPixel(x, y);

                    if (firstTime)
                    {
                        if (curHashing.startOffset.startOffsetCount == -1) //No offset
                        {
                            analyseStart = true;
                        }
                        else decryptColor = true;

                        firstTime = false;
                    }
                    else if (curHashing.startOffset.startOffsetCount != -1) //Offset
                    {
                        if (!origFound && twoColorsAreEqual(curColor, curHashing.startColor))
                        {
                            analyseStart = true;
                        }
                    }

                    if (analyseStart)
                    {
                        //No offset and thus the restrictions in the conversions start already here
                        if (!twoColorsAreEqual(curDecrypting.origBitmap.GetPixel(x, y), curHashing.startColor))
                        {
                            curDecrypting.errors = true;
                            return curDecrypting;
                        }
                        else
                        {
                            decryptColor = false;
                        }
                    }

                    if (decryptColor)
                    {
                        //The original start point hasn't either be found or was found and all the surrounding, irrelevant information has already been skipped
                        if (x == curHashing.endPixel.X && y == curHashing.endPixel.Y)
                        {
                            return curDecrypting;
                        }
                        goAhead = true;
                    }
                    else
                    {
                        if (!origFound)
                        {
                            if (x == curHashing.pixelsLength.endPixel.X && y == curHashing.pixelsLength.endPixel.Y)
                            {
                                origFound = true;
                                decryptColor = true; //from the next pixel onwards, all the information should be decrypted
                            }
                        }
                        else
                        {
                            curDecrypting.errors = true;
                            return curDecrypting;
                        }
                    }

                    if (goAhead)
                    {
                        KeyValuePair<char, Color> tempPair = curHashing.charsVsColors.FirstOrDefault(x2 => twoColorsAreEqual(x2.Value, curColor));
                        if (tempPair.Value.IsEmpty && !twoColorsAreEqual(curColor, curHashing.startColor))
                        {
                            bool isOK = false;
                            if (curDecrypting.encryptingNumber > 0)
                            {
                                if (!curHashing.replacePair.Value.IsEmpty && twoColorsAreEqual(curColor, curHashing.replacePair.Value))
                                {
                                    tempPair = new KeyValuePair<char, Color>(curHashing.replacePair.Key, curColor);
                                    isOK = true;
                                }
                                else if (!curHashing.startOffset.replaceAfter.Value.IsEmpty && twoColorsAreEqual(curColor, curHashing.startOffset.replaceAfter.Value))
                                {
                                    tempPair = new KeyValuePair<char, Color>(curHashing.startOffset.replaceAfter.Key, curColor);
                                    isOK = true;
                                }
                            }
                            if (!isOK)
                            {
                                curDecrypting.errors = true;
                                return curDecrypting;
                            }
                        }

                        curDecrypting.decryptedString = curDecrypting.decryptedString + tempPair.Key.ToString();
                    }
                }
            }

            return curDecrypting;
        }