﻿using System;
using System.Collections.Generic;
using System.Text;

namespace IndianHealthService.BMXNet.Net
{
    /// <summary>
    /// This implementation EncryptionProvider is the crypto used for the standard Indian Health Service socket-based BMXNET Broker.    
    /// </summary>
    public sealed class BMXAdeCrypto : EncryptionProvider
    {
        public static EncryptionProvider Keyed(String[] aKey)
        {
            BMXAdeCrypto answer = new BMXAdeCrypto();
            answer.SetKey(aKey);
            return answer;
        }

        public BMXAdeCrypto()
        {
        }

        private string[] DefaultKey
        {
            get
            {
                return new string[]
			{
				@"wkEo-ZJt!dG)49K{nX1BS$vH<&:Myf*>Ae0jQW=;|#PsO`'%+rmb[gpqN,l6/hFC@DcUa ]z~R}""V\iIxu?872.(TYL5_3",
				@"rKv`R;M/9BqAF%&tSs#Vh)dO1DZP> *fX'u[.4lY=-mg_ci802N7LTG<]!CWo:3?{+,5Q}(@jaExn$~p\IyHwzU""|k6Jeb",
				@"\pV(ZJk""WQmCn!Y,y@1d+~8s?[lNMxgHEt=uw|X:qSLjAI*}6zoF{T3#;ca)/h5%`P4$r]G'9e2if_>UDKb7<v0&- RBO.",
				@"depjt3g4W)qD0V~NJar\B ""?OYhcu[<Ms%Z`RIL_6:]AX-zG.#}$@vk7/5x&*m;(yb2Fn+l'PwUof1K{9,|EQi>H=CT8S!",
				@"NZW:1}K$byP;jk)7'`x90B|cq@iSsEnu,(l-hf.&Y_?J#R]+voQXU8mrV[!p4tg~OMez CAaGFD6H53%L/dT2<*>""{\wI=",
				@"vCiJ<oZ9|phXVNn)m K`t/SI%]A5qOWe\&?;jT~M!fz1l>[D_0xR32c*4.P""G{r7}E8wUgyudF+6-:B=$(sY,LkbHa#'@Q",
				@"hvMX,'4Ty;[a8/{6l~F_V""}qLI\!@x(D7bRmUH]W15J%N0BYPkrs&9:$)Zj>u|zwQ=ieC-oGA.#?tfdcO3gp`S+En K2*<",
				@"jd!W5[];4'<C$/&x|rZ(k{>?ghBzIFN}fAK""#`p_TqtD*1E37XGVs@0nmSe+Y6Qyo-aUu%i8c=H2vJ\) R:MLb.9,wlO~P",
				@"2ThtjEM+!=xXb)7,ZV{*ci3""8@_l-HS69L>]\AUF/Q%:qD?1~m(yvO0e'<#o$p4dnIzKP|`NrkaGg.ufCRB[; sJYwW}5&",
				@"vB\5/zl-9y:Pj|=(R'7QJI *&CTX""p0]_3.idcuOefVU#omwNZ`$Fs?L+1Sk<,b)hM4A6[Y%aDrg@~KqEW8t>H};n!2xG{",
				@"sFz0Bo@_HfnK>LR}qWXV+D6`Y28=4Cm~G/7-5A\b9!a#rP.l&M$hc3ijQk;),TvUd<[:I""u1'NZSOw]*gxtE{eJp|y (?%",
				@"M@,D}|LJyGO8`$*ZqH .j>c~h<d=fimszv[#-53F!+a;NC'6T91IV?(0x&/{B)w""]Q\YUWprk4:ol%g2nE7teRKbAPuS_X",
				@".mjY#_0*H<B=Q+FML6]s;r2:e8R}[ic&KA 1w{)vV5d,$u""~xD/Pg?IyfthO@CzWp%!`N4Z'3-(o|J9XUE7k\TlqSb>anG",
				@"xVa1']_GU<X`|\NgM?LS9{""jT%s$}y[nvtlefB2RKJW~(/cIDCPow4,>#zm+:5b@06O3Ap8=*7ZFY!H-uEQk; .q)i&rhd",
				@"I]Jz7AG@QX.""%3Lq>METUo{Pp_ |a6<0dYVSv8:b)~W9NK`(r'4fs&wim\kReC2hg=HOj$1B*/nxt,;c#y+![?lFuZ-5D}",
				@"Rr(Ge6F Hx>q$m&C%M~Tn,:""o'tX/*yP.{lZ!YkiVhuw_<KE5a[;}W0gjsz3]@7cI2\QN?f#4p|vb1OUBD9)=-LJA+d`S8",
				@"I~k>y|m};d)-7DZ""Fe/Y<B:xwojR,Vh]O0Sc[`$sg8GXE!1&Qrzp._W%TNK(=J 3i*2abuHA4C'?Mv\Pq{n#56LftUl@9+",
				@"~A*>9 WidFN,1KsmwQ)GJM{I4:C%}#Ep(?HB/r;t.&U8o|l['Lg""2hRDyZ5`nbf]qjc0!zS-TkYO<_=76a\X@$Pe3+xVvu",
				@"yYgjf""5VdHc#uA,W1i+v'6|@pr{n;DJ!8(btPGaQM.LT3oe?NB/&9>Z`-}02*%x<7lsqz4OS ~E$\R]KI[:UwC_=h)kXmF",
				@"5:iar.{YU7mBZR@-K|2 ""+~`M%8sq4JhPo<_X\Sg3WC;Tuxz,fvEQ1p9=w}FAI&j/keD0c?)LN6OHV]lGy'$*>nd[(tb!#",
            };
            }
        }

        /// <summary>
        /// Set a site specific key
        /// </summary>
        /// <param name="aKey">The required key for BMX Ade</param>
        public void SetKey(String[] aKey)
        {
            this.Key = aKey;
        }

        private string[] _key = null;

        private string[] Key
        {
            get {return (_key == null) ? this.DefaultKey : this._key; }
            set { _key = value; }
        }

        /// <summary>
        /// BMXAde Implementation
        /// </summary>
        /// <param name="aString">String to encrypt</param>
        /// <returns>Encrypt string</returns>
        public string Encrypt(string aString)
        {
            return this.ADEEncryp(aString);
        }

        /// <summary>
        /// BMXAde Implementation
        /// </summary>
        /// <param name="aString">String to decrypt</param>
        /// <returns>Decrypted string</returns>
        public string Decrypt(string aString)
        {
            return this.ADEDecryp(aString);
        }


        private string ADEEncryp(string sInput)
        {
            //Encrypt a string
            string strPercent;
            string strAssoc;
            string strIdix;
            int nPercent;
            int nAssocix;
            int nIdix;

            System.Random rRand = new Random(DateTime.Now.Second);

            nPercent = rRand.Next(0, 10000);
            nPercent += 72439;
            nAssocix = nPercent % 20;
            nAssocix++;
            this.Assert(nAssocix < 21, "Associx to large");
            strPercent = nPercent.ToString();
            strPercent = strPercent.Substring(1, 2);
            nIdix = Convert.ToInt32(strPercent);
            nIdix = nIdix % 20;
            nIdix++;
            this.Assert(nIdix < 21, "Idix to large");
            
            const int nEncryptBase = 101;
            strAssoc = LoadKey(nEncryptBase + nAssocix);
            this.Assert(strAssoc.Length == 94, "Associator length invalid");
            strIdix = LoadKey(nEncryptBase + nIdix);
            this.Assert(strAssoc.Length == 94, "Identifier length invalid");
            string sEncrypted = "";

            foreach (char c in sInput)
            {
                string d = c.ToString();
                int nFindChar = strIdix.IndexOf(c);
                if (nFindChar > -1)
                {
                    d = strAssoc.Substring(nFindChar, 1);
                }
                sEncrypted += d;
            }

            String debugPreview = (char)(nIdix + 31) + sEncrypted + (char)(nAssocix + 31);
            return debugPreview;
        }


        private string LoadKey(int nID)
        {
            nID -= 102;

            this.Assert(nID < 20, "Key array length too long");

            return this.Key[nID];
        }

        private void Assert(bool aBoolean, String aMessage)
        {
            if (!aBoolean)
            {
                throw new BMXNetException("BmxAdeEncryptionProvider Assertion failed: " + aMessage);
            }
        }

        private string ADEDecryp(string sInput)
        {
            //Encrypt a string
            string strAssoc;
            string strIdix;
            int nAssocix;
            int nIdix;
            this.Assert(sInput != "", "Unable to encrypt empty string");

            //get associator string index
            char cAssocix = sInput[sInput.Length - 1];
            nAssocix = (int)cAssocix;
            nAssocix -= 31;
            this.Assert(nAssocix < 21, "Associx to large");


            //get identifier string index
            char cIdix = sInput[0];
            nIdix = (int)cIdix;
            nIdix -= 31;
            this.Assert(nIdix < 21, "Idix to large");

            //get associator string
            const int nEncryptBase = 101;
            strAssoc = LoadKey(nEncryptBase + nAssocix);
            this.Assert(strAssoc.Length == 94, "Associator length invalid");

            //get identifier string
            strIdix = LoadKey(nEncryptBase + nIdix);
            this.Assert(strAssoc.Length == 94, "Identifier length invalid");

            //translated result
            string sDecrypted = "";
            sInput = sInput.Substring(1, sInput.Length - 2);
            foreach (char c in sInput)
            {
                string d = c.ToString();
                int nFindChar = strAssoc.IndexOf(c);
                if (nFindChar > -1)
                {
                    d = strIdix.Substring(nFindChar, 1);
                }
                sDecrypted += d;
            }

            return sDecrypted;
        }
    }
}