using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;
using System.Drawing.Imaging;
using System.IO;

namespace RandomLSB
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private Bitmap OriginalImage = null, EncodedImage = null, MarkedImage = null;

       

        private void btnEncode_Click(object sender, EventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            Application.DoEvents();
            EncodedImage =(Bitmap) OriginalImage.Clone();
            MarkedImage =(Bitmap) OriginalImage.Clone();
            EncodeMessageInImage(EncodedImage, MarkedImage, txtPassword.Text, txtMessage.Text);
            radioButton3.Checked = true;
            PicImage.Image = MarkedImage;
            this.Cursor = Cursors.Default;
        }
        private void EncodeMessageInImage(Bitmap EncImg,Bitmap MarkImg,string password,string message)
        {
            Random rnd = new Random(NumericPassword(password));
            HashSet<string> used_positions = new HashSet<string>();
            byte[] bytes = BitConverter.GetBytes(message.Length);
            for(int i=0;i<bytes.Length;i++)
            {
                EncodeByte(EncImg, MarkImg, rnd, bytes[i], used_positions);
            }

            char[] chars = message.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                EncodeByte(EncImg, MarkImg, rnd,(byte)chars[i], used_positions);
            }

        }
        private void EncodeByte(Bitmap EncImg,Bitmap MarkImg,Random rnd,byte value,HashSet<string> used_positions)
        {
            for(int i=0; i<8;i++)
            {   
                int row, col, pix;
                PickPosition(EncImg, rnd, used_positions,out row,out col,out pix);
                Color clr = EncImg.GetPixel(row, col);
                byte r = clr.R;
                byte g = clr.G;
                byte b = clr.B;
                int bit = 0;
                if ((value & 1) == 1)
                    bit = 1;
                switch(pix)
                {
                    case 0:
                        r = (byte)((r & 0xFE) | bit);
                        break;
                    case 1:
                        g = (byte)((g & 0xFE) | bit);
                        break;
                    case 2:
                        b = (byte)((b & 0xFE) | bit);
                        break;

                }
                clr = Color.FromArgb(clr.A, r, g, b);
                EncImg.SetPixel(row, col, clr);
                MarkImg.SetPixel(row, col, Color.Red);
                value >>= 1;
            }
        }
        private void PickPosition(Bitmap EncImg,Random rnd, HashSet<string> used_positions,out int row,out int col,out int pix )
        {
            while(true)
            {
                row = rnd.Next(0, EncImg.Width);
                col = rnd.Next(0, EncImg.Height);
                pix = rnd.Next(0, 3);
                string key = row.ToString() + "/" + col.ToString() + "/" + pix.ToString();
                if(!used_positions.Contains(key))
                {
                    used_positions.Add(key);
                    return;
                }
            }
        }

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            PicImage.Image = OriginalImage;

        }

        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            PicImage.Image = EncodedImage;
        }

        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            PicImage.Image = MarkedImage;
        }

        private void savePictureToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog s = new SaveFileDialog();
            s.Filter="Image Files| *.png;*.bmp;";
            if(s.ShowDialog()==DialogResult.OK)
            {
                FileInfo f = new FileInfo(s.FileName);
                switch(f.Extension)
                {
                    case ".png":
                        EncodedImage.Save(s.FileName, ImageFormat.Png);
                        break;
                    case ".bmp":
                        EncodedImage.Save(s.FileName, ImageFormat.Bmp);
                        break;
                }
               
            }
        }

        private void btnDecode_Click(object sender, EventArgs e)
        {
            txtMessage.Text = string.Empty;
            Application.DoEvents();
          txtMessage.Text =DecodeMessageImage(OriginalImage, txtPassword.Text);
            
        }
        private string DecodeMessageImage(Bitmap bm,string password)
        {
            Random rnd = new Random(NumericPassword(password));
            HashSet<string> used_positions = new HashSet<string>();

            int len =0;
            byte[] bytes = BitConverter.GetBytes(len);
            for(int i=0;i<bytes.Length;i++)
            {
                bytes[i] = DecodeByte(bm, rnd, used_positions);
            }
            len = BitConverter.ToInt32(bytes, 0);
            char[] chars = new char[len];
            for (int i = 0; i < len; i++)
            {
                chars[i] =(char)DecodeByte(bm, rnd, used_positions);
            }
            return new string(chars);
        }
        private byte DecodeByte(Bitmap bm,Random rnd, HashSet<string> used_positions)
        {
            byte value = 0;
            byte value_mask = 1;
            for(int i=0;i<8;i++)
            {
                int row, col, pix;
                PickPosition(bm, rnd, used_positions, out row, out col, out pix);
                byte color_value = 0;
                switch(pix)
                {
                    case 0:
                        color_value = bm.GetPixel(row, col).R;
                        break;
                    case 1:
                        color_value = bm.GetPixel(row, col).G;
                        break;
                    case 2:
                        color_value = bm.GetPixel(row, col).B;
                        break;
                }
                if ((color_value & 1) == 1)
                    value = (byte)(value | value_mask);
                value_mask <<= 1;

            }
            return value;
        }
        private int NumericPassword(string password)
        {
            using (var md5 = MD5.Create())
            {
                return BitConverter.ToInt32(md5.ComputeHash(Encoding.UTF8.GetBytes(password)),0);
            }
        }
        private void openPictureToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog op = new OpenFileDialog();
            if(op.ShowDialog()==DialogResult.OK)
            {
                OriginalImage = new Bitmap(op.FileName);
                EncodedImage = null;
                MarkedImage = null;
                radioButton1.Checked = true;
                PicImage.Image = OriginalImage;
            }
        }
    }
}
