using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ChoiceMSLConnect
{
    /// <summary>
    /// This class is copied (Stripped down to it's core, with all unneccessary stuff for this instance removed)
    /// from Registration Server's SystemAccessVerifier.
    /// All new code should go there. No Pin is generated here. The pin for a userkey
    /// should always be generated at the registration server.
    /// This class however can be used to convert a SystemApiKey to a UserApiKey, usefull for what we are 
    /// trying to do in this service
    /// </summary>
    internal class SystemAccessVerifier
    {
        #region Consts
        const int SYSTEM_API_KEY_LENGTH = 22;
        const int USER_API_KEY_LENGTH = 20;
        const int PIN_LENGTH = 6;
        const string SYSTEM_API_KEY_BEGIN_MARKER = "$";
        const string SYSTEM_API_KEY_END_MARKER = "#";
        internal static readonly string LETTER_CHARS = "AZXVGLTCIRKPNOMQJSHUFWDEYB";
        internal static int GET_LETTER_CHARS_COUNT { get { return LETTER_CHARS.Length; } }
        #endregion

        #region System Api Key
        private string _systemApiKey = String.Empty;
        internal string SystemApiKey
        {
            get
            {
                return _systemApiKey;
            }
            set
            {
                if (ValidateSystemKey(value))
                    _systemApiKey = value.ToUpper().Trim();
            }
        }
        private static bool ValidateSystemKey(string key)
        {
            if (!String.IsNullOrEmpty(key))
                key = key.ToUpper().Trim();

            // $247B2CB4A437EB74C62#M
            if (!String.IsNullOrEmpty(key) && key.Length == SYSTEM_API_KEY_LENGTH &&
                 key[0] == Char.Parse(SYSTEM_API_KEY_BEGIN_MARKER) && key[SYSTEM_API_KEY_LENGTH - 2] == Char.Parse(SYSTEM_API_KEY_END_MARKER) &&
                 Char.IsLetter(key[SYSTEM_API_KEY_LENGTH - 1]))
            {
                // AllAreLettersOrDigit?
                for (int i = 1; i < SYSTEM_API_KEY_LENGTH - 2; ++i)
                {
                    if (!Char.IsLetterOrDigit(key[i]))
                        return false;
                }
                return true;
            }
            return false;
        }
        #endregion

        #region User Api Key
        private string _UserApiKey = String.Empty;
        internal string UserApiKey
        {
            get
            {
                return _UserApiKey;
            }
            set
            {
                if (ValidateUserKey(value))
                    _UserApiKey = value.ToUpper().Trim();
            }
        }
        private static bool ValidateUserKey(string key)
        {
            if (!String.IsNullOrEmpty(key))
                key = key.ToUpper().Trim();

            //247BQCB4A-37EB-4C62M
            if (!String.IsNullOrEmpty(key) && key.Length == USER_API_KEY_LENGTH &&
                 Char.IsLetter(key[USER_API_KEY_LENGTH - 1]) &&
                 Char.IsLetter(key[4]) && key[9] == '-' && key[14] == '-')
            {
                // Perform Product Security check 
                char productType = key[USER_API_KEY_LENGTH - 1];
                int v = (int)productType % GET_LETTER_CHARS_COUNT;
                if (key[4] == LETTER_CHARS[v])
                    return true;
            }
            return false;
        }
        #endregion

        #region Product Type
        private char _productType = '\0';
        internal char ProductType { get { return Char.ToUpper(_productType); } }
        internal bool IsValidProductType { get { return (_productType != '\0'); } }
        #endregion

        #region Construction

        internal SystemAccessVerifier(string userApiKey)
        {
            if (!String.IsNullOrEmpty(userApiKey))
                userApiKey = userApiKey.ToUpper().Trim();

            if (ValidateUserKey(userApiKey))
            {
                UserApiKey = userApiKey;
                ConvertUserToSystemApiKey();
                _productType = GetProductTypeFromSystemApiKey(SystemApiKey);
            }
        }
        #endregion

        #region Private Converters

        private void ConvertUserToSystemApiKey()
        {
            if (ValidateUserKey(UserApiKey))
            {
                // 247BQCB4A-37EB-4C62M                
                StringBuilder sb = new StringBuilder(UserApiKey);
                char productType = sb[USER_API_KEY_LENGTH - 1];
                sb.Remove(USER_API_KEY_LENGTH - 1, 1);

                // Add More Randomness into this (nobody will ever know :S)
                // Dynamic Calc for 2 - 2, 12 - 4
                int a = (int)'A';
                int z = (int)'Z';
                int ic = (int)sb[2];
                if ((ic - 2 >= a) && (ic <= z))
                    sb[2] = (char)(ic - 2);
                ic = sb[12];
                if ((ic - 4 >= a) && (ic <= z))
                    sb[12] = (char)(ic - 4);

                //Otherway arround arbitrary swap (just in case)
                // Swap 3 to 10, and 5 to 16
                char c = sb[3];
                sb[3] = sb[10];
                sb[10] = c;
                c = sb[5];
                sb[5] = sb[16];
                sb[16] = c;

                // Assign '-'
                sb[4] = '-';

                // How clever :S
                string s = ReverseString(sb.ToString());
                int n = 0;
                sb = new StringBuilder(s);
                for (int i = 0; i < s.Length; ++i)
                {
                    if (s[i] == '-')
                    {
                        sb[i] = (s[n]);
                        n++;
                    }
                    else
                    {
                        sb[i] = (s[i]);
                    }
                }

                // $247B2CB4A437EB74C62#M
                s = SYSTEM_API_KEY_BEGIN_MARKER + sb.ToString() + SYSTEM_API_KEY_END_MARKER + Char.ToUpper(productType);
                SystemApiKey = s;
            }
        }

        #endregion

        #region Internal Statics

        internal static char GetProductTypeFromSystemApiKey(string systemApiKey)
        {
            if (ValidateSystemKey(systemApiKey))
                return Char.ToUpper(systemApiKey[SYSTEM_API_KEY_LENGTH - 1]);
            else
                return '\0';
        }

        #endregion

        #region Private static Key Generation Helper Functions

        private static string ReverseString(string str)
        {
            if (!String.IsNullOrEmpty(str))
            {
                Stack<char> stack = new Stack<char>();
                foreach (char c in str)
                    stack.Push(c);
                StringBuilder sb = new StringBuilder();
                while (stack.Count > 0)
                    sb.Append(stack.Pop());
                return sb.ToString();
            }
            return String.Empty;
        }

        /// <summary>
        /// Perform checksum on string
        /// </summary>
        /// <param name="strAboutToBeChecksummed"></param>
        /// <returns>Checksum</returns>
        private static int PerformChecksum(string strAboutToBeChecksummed)
        {
            if (!String.IsNullOrEmpty(strAboutToBeChecksummed))
            {
                int nChecksum = 0;
                for (int i = 0; i < strAboutToBeChecksummed.Length; ++i)
                {
                    if (Char.IsDigit(strAboutToBeChecksummed[i]))
                        nChecksum = nChecksum + int.Parse(strAboutToBeChecksummed[i].ToString());
                }
                return nChecksum;
            }
            return 0;
        }

        /// <summary>
        /// Dash a String every Nth Character
        /// </summary>
        /// <returns>a dashed string</returns>
        private static string MakeIntoDashSeperatedString(string strAboutToBeDashed, int DashEveryNthCharacter)
        {
            if (!String.IsNullOrEmpty(strAboutToBeDashed))
            {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < strAboutToBeDashed.Length; i++)
                {
                    if ((i != 0) && ((i % DashEveryNthCharacter) == 0))
                        sb.Append("-");
                    sb.Append(strAboutToBeDashed[i]);
                }
                return sb.ToString();
            }
            return String.Empty;
        }

        /// <summary>
        /// Undash a String
        /// </summary>
        /// <returns>a string without dashes</returns>
        private static string MakeIntoDashUnseperatedString(string strAboutToBeUndashed)
        {
            if (!String.IsNullOrEmpty(strAboutToBeUndashed))
                return strAboutToBeUndashed.Replace("-", "");
            return String.Empty;
        }

        /// <summary>
        /// Check if the passed in string contains only digits
        /// </summary>
        /// <param name="strToCheckForDigits">string to check for digits for</param>
        /// <returns>true, if all digits are numbers</returns>
        private static bool ContainsOnlyDigits(string strToCheckForDigits)
        {
            if (!String.IsNullOrEmpty(strToCheckForDigits))
            {
                for (int i = 0; i < strToCheckForDigits.Length; ++i)
                {
                    if (!Char.IsDigit(strToCheckForDigits[i]))
                        return false;
                }
                return true;
            }
            return false;
        }

        #endregion
    }
}
