//Determining the color of the current pixel on account of the given position: inside start/end pixels or outside them
        public Bitmap addColorToPixel(pixelInfo curPixelInfo, Bitmap curBitmap, List<Color> allColors, Encrypting curEncrypting)
        {
            bool noChange = false;
            int curIndex = curPixelInfo.i;
            if (curPixelInfo.startI > -1 && curIndex >= curPixelInfo.startI && curIndex <= curPixelInfo.endI)
            {
                noChange = true;
            }

            Color curColor = new Color();
            if (!noChange)
            {
                //The current pixel is randomly assigned
                curIndex = curPixelInfo.random.Next(0, allColors.Count - 1);

                while (IO.twoColorsAreEqual(allColors[curIndex], curEncrypting.curHashing.startColor)) //StartColor determines where the encrypted chunk starts/ends and thus cannot be used outside it
                {
                    curIndex = curPixelInfo.random.Next(0, allColors.Count - 1);
                }

                curColor = allColors[curIndex];
            }
            else
            {
                //This is part of the "encrypted chunk" and thus the pixels, as calculated so far, have to be added
                curPixelInfo.encryptedAdded = true;
                curIndex = curIndex - curPixelInfo.startI;
                curColor = curEncrypting.encryptedString[curIndex];

                if (curEncrypting.curHashing.startOffset.startOffsetCount == -1)
                {
                    //There is no offset; thus the color in the "offset position" cannot be curEncrypting.curHashing.startOffset.afterOffset
                    if (twoColorsAreEqual(curColor, curEncrypting.curHashing.startOffset.afterOffset) && !curEncrypting.curHashing.startOffset.replaceAfter.Value.IsEmpty)
                    {
                        if (curEncrypting.encryptingNumber > 0 && curIndex == curEncrypting.encryptingNumber + 1)
                        {
                            curColor = curEncrypting.curHashing.startOffset.replaceAfter.Value;
                        }
                    }
                }
            }

            curBitmap.SetPixel(curPixelInfo.x, curPixelInfo.y, curColor);

            return curBitmap;
        }
        //btnEncrypt Click Event starting the encryption
        private void btnEncrypt_Click(object sender, EventArgs e)
        {
            clearEncryptCmbBxs();
            string curText = WrittenByOthers.RemoveDiacritics(txtBxEncrypt.Text, true); //Diacritics are not being accounted for because of the limited number of available colors
            Encrypting curEncrypting = new Encrypting((Color)cmbBxEncryptStart.SelectedItem, curText, curIO, (int)numUpDwnEncrypt.Value);

            if (!curEncrypting.errors)
            {
                //After encrypting the input string, the corresponding image has to be created, together with all the random pixels to hide the true information and further security means.
                curEncrypting = curIO.createImageFile(500, 500, curEncrypting);

                if (curEncrypting.errors)
                {
                    displayError("There was an error while encrypting the text.", curEncrypting.errorMessage, false);
                }
                else
                {
                    if (curEncrypting.modifiedText != curEncrypting.origText && curEncrypting.ignoredChars.Count > 0)
                    {
                        cmbBxIgnored.DataSource = curEncrypting.ignoredChars;
                    }
                    if (curText != txtBxEncrypt.Text)
                    {
                        IEnumerable<char> tempArray = txtBxEncrypt.Text.ToCharArray().Except(curText.ToCharArray());
                        List<string> curSource = tempArray.Select(x => x + " -> " + WrittenByOthers.RemoveDiacritics(x.ToString(), true)).ToList();
                        cmbBxCorrected.DataSource = curSource;
                    }

                    txtBxEncrypt.Text = curEncrypting.modifiedText;
                }
            }
            else
            {
                displayError("There was an error while encrypting the text.", curEncrypting.errorMessage, 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;
            }
        }
        //Method defining the pixels "surrounding" the encrypted string. These pixels are very important to allow the decryptor to undoubtedly locate the bunch of pixels to be decrypted.
        private Encrypting setBoundaryColors(Encrypting curEncrypting)
        {
            Hashing curHashing = curEncrypting.curHashing;
            bool analyseOffset = curEncrypting.encryptingNumber > 0 && curEncrypting.encryptedString.Count - curEncrypting.encryptingNumber > 1;
            if (analyseOffset)
            {
                //startColor at the "encryptingNumber position"
                curEncrypting.encryptedString.Insert(curEncrypting.encryptingNumber, curHashing.startColor);
                curEncrypting.curHashing.startOffset.startOffsetCount = curEncrypting.encryptingNumber + 1;
                curEncrypting.encryptedString.Insert(curEncrypting.encryptingNumber + 1, curHashing.startOffset.afterOffset);
            }
            else
            {
                //startColor as first pixel
                curEncrypting.encryptedString.Insert(0, curHashing.startColor);
            }

            //Method encrypting the current length in the required position
            addLength(curEncrypting, curHashing, analyseOffset);

            //startColor as last pixel
            curEncrypting.encryptedString.Add(curHashing.startColor);

            return curEncrypting;
        }
        //Method called by the one above to actually create the image
        private Bitmap createImage(int width, int height, Encrypting curEncrypting)
        {
            Hashing curHashing = curEncrypting.curHashing;
            List<Color> allColors = curHashing.charsVsColors.Values.ToList(); //Colors being considered in the current encryption: neither their number nor their order is kept constant

            curEncrypting = setBoundaryColors(curEncrypting); //Including the additional pixels surrounding the ones including the encrypted information

            //Accounting for the fact that the default size (500*500) is not enough to store all the required information
            int totSize = width * height;
            if (totSize < curHashing.curLength)
            {
                double ratio = Math.Ceiling(Math.Sqrt(Math.Ceiling((double)allColors.Count / curHashing.curLength)));
                height = Convert.ToInt32(Math.Ceiling(height * ratio));
                width = Convert.ToInt32(Math.Ceiling(width * ratio));
            }

            Bitmap outBitmap = new Bitmap(width, height);

            pixelInfo curPixel = new pixelInfo();

            //Determining the start/end pixels (where the encrypted string + additional info will be located)
            curPixel.startI = calculateStartI(width, height, curEncrypting);
            if (curEncrypting.encryptedString != null) curPixel.endI = curPixel.startI + curEncrypting.encryptedString.Count - 1;

            //Main loop populating the bitmap with all the pixels: ones containing encrypted information and all the other ones
            curPixel.i = -1;
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    curPixel.i = curPixel.i + 1;
                    curPixel.x = x;
                    curPixel.y = y;

                    outBitmap = addColorToPixel(curPixel, outBitmap, allColors, curEncrypting);
                }
            }

            return outBitmap;
        }
        //Adding pixels with information regarding the length of the string
        private Encrypting addLength(Encrypting curEncrypting, Hashing curHashing, bool analyseOffset)
        {
            string remString = curHashing.curLength.ToString();
            int curIndex = 0;
            if (analyseOffset) curIndex = curEncrypting.encryptingNumber + 1; //AfterOffset has also to be accounted

            //Loop creating one new pixel per digit defining the current length
            while(remString.Trim().Length > 0)
            {
                int curLength = remString.Length;
                curIndex = curIndex + 1;
                char curChar = Convert.ToChar(remString.Substring(0, 1));
                curEncrypting.encryptedString.Insert(curIndex, curHashing.charsVsColors[curChar]);

                if (curLength <= 1) break;
                remString = remString.Substring(1, remString.Length - 1);
            }

            if (analyseOffset)
            {
                //The default population of encryptedString already avoids any pixel before the start one to have startColor. This loop confirms this for all the pixels between
                //the formal start and the actual start (offset)
                if (curEncrypting.encryptingNumber > 0 && curEncrypting.encryptedString.Count >= curEncrypting.encryptingNumber)
                {
                    for (int i = 0; i < curEncrypting.encryptingNumber; i++)
                    {
                        if (twoColorsAreEqual(curEncrypting.encryptedString[i], curHashing.startColor))
                        {
                            //The start point is after this; thus the color of this pixel cannot be the startColor, it will be replaced with the replaceColor
                            curEncrypting.encryptedString[i] = curHashing.replacePair.Value;
                        }
                    }
                }
            }

            return curEncrypting;
        }
        //------------------------------- ENCRYPTING
        //Method performing the last actions in the encryption process: the ones required to create the output image with the encrypted pixels and the other irrelevant, surrounding ones
        public Encrypting createImageFile(int width, int height, Encrypting curEncrypting)
        {
            try
            {
                if (File.Exists(Environment.CurrentDirectory + @"\encrypted_image.png")) File.Delete(Environment.CurrentDirectory + @"\encrypted_image.png");

                Bitmap curBitmap = createImage(width, height, curEncrypting);
                if (curBitmap != null) curBitmap.Save(Environment.CurrentDirectory + @"\encrypted_image.png", System.Drawing.Imaging.ImageFormat.Png);

                if (File.Exists(Environment.CurrentDirectory + @"\encrypted_image.png"))
                {
                    Process curProcess = new Process();
                    curProcess.StartInfo.FileName = "explorer.exe";
                    curProcess.StartInfo.Arguments = Environment.CurrentDirectory + @"\encrypted_image.png";

                    curProcess.Start(); //Opening the picture
                }
                else curEncrypting.errors = true;
            }
            catch
            {
                curEncrypting.errors = true;
            }

            return curEncrypting;
        }
        //Method determining the start position from which the encrypted string will be added to the image
        public int calculateStartI(int width, int height, Encrypting curEncrypting)
        {
            if (curEncrypting == null || curEncrypting.encryptedString == null) return -1;

            Hashing curHashing = curEncrypting.curHashing;
            int availableSize = width * height - curEncrypting.encryptedString.Count;
            int startI = availableSize;
            if (startI > 0)
            {
                int tempStart = curHashing.startColor.R + curHashing.startColor.G + curHashing.startColor.B;
                if (tempStart <= availableSize)
                {
                    startI = tempStart;
                }
                else
                {
                    tempStart = curHashing.startColor.R + curHashing.startColor.G;
                    if (tempStart <= availableSize)
                    {
                        startI = tempStart;
                    }
                    else
                    {
                        if (curHashing.startColor.R <= availableSize)
                        {
                            startI = curHashing.startColor.R;
                        }
                        else
                        {
                            startI = availableSize - 1;
                        }
                    }
                }
            }

            return startI;
        }