﻿using System;
using System.IO;
using System.Security.Cryptography;

namespace StreamingConnectClient.Sources
{
    public static class Crypter
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="password"></param>
        /// <returns>(Decryptor, Encryptor)</returns>
        public static Tuple<ICryptoTransform, ICryptoTransform> GenerateDeEncrpyter(string password)
        {
            Tuple<ICryptoTransform, ICryptoTransform> result;

            using (RijndaelManaged crypto = new RijndaelManaged())
            {
                PasswordDeriveBytes passwd = new PasswordDeriveBytes(
                    password,
                    new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });

                crypto.Key = passwd.GetBytes(32);
                crypto.IV = passwd.GetBytes(16);

                result = new Tuple<ICryptoTransform, ICryptoTransform>(crypto.CreateDecryptor(), crypto.CreateEncryptor());
            }

            return result;
        }

        public static string EncryptString(ICryptoTransform encryptor, string text)
        {
            MemoryStream memStream = null;
            string result = "";

            try
            {
                memStream = new MemoryStream();

                using (StreamWriter writer = new StreamWriter(new CryptoStream(memStream, encryptor, CryptoStreamMode.Write)))
                {
                    memStream = null;

                    writer.Write(text);

                    Stream buffer = writer.BaseStream;

                    if (buffer is MemoryStream)
                        result = Convert.ToBase64String(((MemoryStream)buffer).ToArray());
                }
            }
            finally
            {
                if (memStream != null)
                    memStream.Dispose();
            }

            return result;
        }

        public static string DecryptString(ICryptoTransform decryptor, string text)
        {
            Stream memStream = null;
            string result = "";

            try
            {
                memStream = new MemoryStream(Convert.FromBase64String(text));

                using (StreamReader reader = new StreamReader(new CryptoStream(memStream, decryptor, CryptoStreamMode.Read)))
                {
                    memStream = null;
                    result = reader.ReadToEnd();
                }
            }
            finally
            {
                if (memStream != null)
                    memStream.Dispose();
            }

            return result;
        }

        public static void SavePassword(string password)
        {
            #region reference -> http://stackoverflow.com/questions/4181198/how-to-hash-a-password/10402129#10402129
            byte[] salt;
            new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]);

            var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
            byte[] hash = pbkdf2.GetBytes(20);

            byte[] hashBytes = new byte[36];
            Array.Copy(salt, 0, hashBytes, 0, 16);
            Array.Copy(hash, 0, hashBytes, 16, 20);

            string savedPasswordHash = Convert.ToBase64String(hashBytes);
            #endregion

            Properties.Settings.Default.PasswordHash = savedPasswordHash;
            Properties.Settings.Default.Save();
        }

        public static bool IsPasswordCorrect(string password)
        {
            bool result = true;

            #region reference -> http://stackoverflow.com/questions/4181198/how-to-hash-a-password/10402129#10402129
            byte[] hashBytes = Convert.FromBase64String(Properties.Settings.Default.PasswordHash);

            byte[] salt = new byte[16];
            Array.Copy(hashBytes, 0, salt, 0, 16);

            var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000);
            byte[] hash = pbkdf2.GetBytes(20);

            for (int i = 0; i < 20; i++)
            {
                if (hashBytes[i + 16] != hash[i])
                {
                    result = false;
                    break;
                }
            }
            #endregion

            return result;
        }
    }
}
