using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using IndianHealthService.BMXNet.WinForm.Forms;
using IndianHealthService.BMXNet.Net;
using IndianHealthService.BMXNet.WinForm.Configuration;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Security.Principal;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace IndianHealthService.BMXNet.WinForm.Forms
{
    public partial class VerifyCodeUpdateDialog : Form
    {
        /// <summary>
        /// Private variable that takes  framework as parameter
        /// </summary>
        private WinFramework _framework = null;

        /// <summary>
        /// Login process, not sure if this is needed
        /// </summary>
        private LoginProcess mylogin;

        private RpmsConnectionSpec _connectionSpec = null;

        /// <summary>
        /// The active ConnectionSpec being used to login.  With care, the property can be changed or the instance can be modified during the AttemptingLogin and LoginAttempted events
        /// with care.  
        /// </summary>
        public RpmsConnectionSpec ConnectionSpec
        {
            get { return _connectionSpec; }
            set { _connectionSpec = value; }
        }
        private string _accessCode;

        internal protected WinFramework Framework
        {
            get { return _framework; }
            set { _framework = value; }
        }

        private bool isUpdateSuccessful;

        public VerifyCodeUpdateDialog(WinFramework framework, RpmsConnectionSpec connspec, string accesscode)
        {
           this.Framework = framework;
            this._accessCode = accesscode;
            this.ConnectionSpec = connspec;
            mylogin = new LoginProcess(framework);

            InitializeComponent();
        }
  
        private bool _isSwitchServerModeEnabled = false;

        public bool IsSwitchServerModeEnabled
        {
            get { return _isSwitchServerModeEnabled; }
            set { _isSwitchServerModeEnabled = value; }
        }

        private void VerifyCodeUpdateDialog_Load(object sender, EventArgs e)
        {
            this.currentVerifyCodeEntry.Focus();
        }

        public void ShowView(IWin32Window anOwner)
        {
            this.ShowDialog(anOwner);     
        }

        public VerifyCodeUpdateResult ShowVerifyChangeDialog(IWin32Window anOwner)
        {
            DialogResult dr = new DialogResult();
            if (anOwner != null)
            {
                dr = this.ShowDialog(anOwner);
            }
            else
            {
                dr = this.ShowDialog();
            }

            VerifyCodeUpdateResult vr = new VerifyCodeUpdateResult();
            vr.DialogResult = dr;
            vr.WasVerifyCodeUpdatedSuccessfully = this.IsUpdateSuccessful;

            return vr;
        }

        public IWin32Window UiOwner
        {
            get { return this.Owner; }
        }


        #region RpmsLoginView Members

        public string Title
        {
            set { this.Text = value; }
        }

        public bool IsUpdateSuccessful
        {
            set { this.isUpdateSuccessful  = value; }
            get { return this.isUpdateSuccessful; }
        }

        public string CurrentVerifyCode
        {
            get { return this.currentVerifyCodeEntry.Text; }
        }

        public string AccessCode
        {
            set { this._accessCode  = value; }
            get { return this._accessCode; }
        }

        private string _updateMessage;

        public string UpdateMessage
        {
            set { this._updateMessage  = value; }
            get { return this._updateMessage; }
        }
        public string NewVerifyCode
        {
            get { return this.NewVerifyCodeEntry.Text; }
        }

        public string RepeatNewVerifyCode
        {
            get { return this.RepeatVerifyCodeEntry.Text; }
        }

        
        public event EventHandler Ok;

        public event EventHandler Cancel;

        #endregion

    
        public void CloseView()
        {
            this.Close();
        }

        private void okButton_Click(object sender, EventArgs e)
        {
                //make sure all fields were filled with values
                if ((this.currentVerifyCodeEntry.Text.Length == 0) ||
                    (this.NewVerifyCodeEntry.Text.Length == 0) || 
                    (this.RepeatVerifyCodeEntry.Text.Length == 0) )
                {
                    string message = "Please fill all blank fields";
                    string caption = "Verify Code Update";
			        MessageBoxButtons buttons = MessageBoxButtons.OK;
                    
			        DialogResult result;

			        // Displays the MessageBox.
			        result = MessageBox.Show(message, caption, buttons);
                }
                else if (this.currentVerifyCodeEntry.Text.Contains("^") ||
                         this.NewVerifyCodeEntry.Text.Contains("^") ||
                         this.RepeatVerifyCodeEntry.Text.Contains("^"))
                {
                    string message = "The character '^' is not allowed in any of the verify code entry fields.";
                    string caption = "Verify Code Update";
                    MessageBoxButtons buttons = MessageBoxButtons.OK;

                    DialogResult result;

                    //Display the MessageBox
                    result = MessageBox.Show(message, caption, buttons);

                    ResetVerifyCodeEntries();
                }
                else if (this.NewVerifyCodeEntry.Text != this.RepeatVerifyCodeEntry.Text)
                {
                    string message = "Please key in the same verify code twice.";
                    string caption = "Verify Code Update";
			        MessageBoxButtons buttons = MessageBoxButtons.OK;
                    
			        DialogResult result;

			        // Displays the MessageBox.
			        result = MessageBox.Show(message, caption, buttons);
                }
                else
                {
                     //get all the values and ready to udpate
                    string currentVCode = this.currentVerifyCodeEntry.Text;
                    string newVCode = this.NewVerifyCodeEntry.Text;
                    string repeatVCode = this.RepeatVerifyCodeEntry.Text;

                    this.IsUpdateSuccessful = SendSecurityRequest(EncryptAccessVerifyCode(this.AccessCode,currentVCode,newVCode));

                    if (!this.IsUpdateSuccessful)
                    {
                        ResetVerifyCodeEntries();
                    }
                }
        }

        private void cancelButton_Click(object sender, EventArgs e)
        {
            if (this.Cancel != null)
            {
                this.Cancel(this, new EventArgs());
            }
        }

        public void ResetVerifyCodeEntries()
        {
            this.currentVerifyCodeEntry.Clear();
            this.RepeatVerifyCodeEntry.Clear();
            this.NewVerifyCodeEntry.Clear();
            this.currentVerifyCodeEntry.Select();
        }

        public void ShowWait()
        {
            this.Cursor = Cursors.WaitCursor;
        }

        public void HideWait()
        {
            this.Cursor = Cursors.Default;
        }
        public String EncryptAccessVerifyCode(string anAccessCode, string aCurrentVerifyCode, string aNewVerifyCode)
        {
            BMXNetBroker broker = this.Framework.SocketBroker;
            string accessVerifyPair = anAccessCode.ToUpper() + ";" + aNewVerifyCode.ToUpper() + ";" + aCurrentVerifyCode.ToUpper();
            return broker.EncryptionProvider.Encrypt(accessVerifyPair);
        }

        private bool SendSecurityRequest(string encryptedAccessVerifyCode)
		{
           BMXNetSocketBroker broker = (BMXNetSocketBroker)this.Framework.SocketBroker;
            
			string		strReceive = "";
			string		cMSG;
		
            string m_sWKID = "BMX";
			string m_sWINH = "";
			string m_sPRCH = "";
			string m_sWISH = "";
			string m_cHDR = broker.ADEBHDR(m_sWKID,m_sWINH,m_sPRCH,m_sWISH);
            
            //open a TCP sockry
            TcpClient mySocket = this.OpenConnectionCommon();

            cMSG = broker.ADEBLDMsg(m_cHDR, "BMX CVC", encryptedAccessVerifyCode);
            SendString(mySocket, cMSG, "");

            strReceive = ReceiveString(mySocket);

            //"2VERIFY CODE must be a mix of alpha and numerics."
            //"3This code is the same as the current one."
            //"4This has been used previously as the VERIFY CODE."
            //"5VERIFY CODE must be different than the ACCESS CODE."
            List<string> rspMessages = new List<string>(); // List of received messages
            rspMessages.Add("Sorry that isn't the correct current code");
            rspMessages.Add("Enter 8-20 characters mixed alphanumeric and punctuation (except '^', ';', ':').");
            rspMessages.Add("VERIFY CODE must be a mix of alpha and numerics and punctuation.");                  // String element 2
            rspMessages.Add("This code is the same as the current one.");                   // 3
            rspMessages.Add("This has been used previously as the VERIFY CODE.");                // 4
            rspMessages.Add("VERIFY CODE must be different than the ACCESS CODE.");                     // 5

            if (strReceive.StartsWith("M ERROR="))
            {
                 string caption = "Verify Code Update";
			     MessageBoxButtons buttons = MessageBoxButtons.OK;
                    
			     DialogResult result;

			     // Displays the MessageBox.
			     result = MessageBox.Show(strReceive, caption, buttons);
                return false;
            }
            else
            {
                foreach (string errorMsg in rspMessages)
                {
                    if (strReceive.Contains(errorMsg))
                    {
                        this.UpdateMessage = errorMsg;
                        string caption = "Verify Code Update";
                         MessageBoxButtons buttons = MessageBoxButtons.OK;
                            
                         DialogResult result;

                         // Displays the MessageBox.
                         result = MessageBox.Show(errorMsg, caption, buttons);
                        return false;
                    }
                }

                //if the code runs here, it means the update is successful
                if (strReceive.Contains("0"))
                {
                    string message = "Verify code has been successfully updated. ";
			        MessageBoxButtons msgButtons = MessageBoxButtons.OK;

	                 // Displays the MessageBox.
	                 MessageBox.Show(message, "Verify Code Update", msgButtons);

                    return true;
                }
                else
                {
                        this.UpdateMessage = strReceive;
                        string caption = "Verify Code Update";
                         MessageBoxButtons buttons = MessageBoxButtons.OK;
                            
                         DialogResult result;

                         // Displays the MessageBox.
                         result = MessageBox.Show(strReceive, caption, buttons);
                        return false;
                }
            }	
		}

        protected void SendString(TcpClient tcpClient, string cSendString, string cMult)
		{
            String encodedString = this.EncodeSendString(cSendString, cMult);
		
			NetworkStream ns = tcpClient.GetStream();
            ns.WriteTimeout = 20000;
			byte[] sendBytes = Encoding.ASCII.GetBytes(encodedString);
			ns.Write(sendBytes,0,sendBytes.Length);
			return;
		}

        protected string EncodeSendString(String cSendString, String cMulti)
        {
            String encoded = null;

            int nLen = cSendString.Length;
            string sLen = nLen.ToString();
            sLen = sLen.PadLeft(5, '0');
            encoded = sLen + cSendString;

            nLen += 15;
            sLen = nLen.ToString();
            sLen = sLen.PadLeft(5, '0');

            encoded = "{BMX}" + sLen + encoded;
            encoded = encoded + cMulti;

            return encoded;
        }

        private string ReceiveString(TcpClient tcpClient)
		{
			NetworkStream ns = tcpClient.GetStream();
            ns.ReadTimeout = 40000;

            
            //TAE: This following is suspect. NetworkSTream Read and ReadTimeout provide
            //the same behavior.  Look at removing in the futuer.  For now, this works.
            int cyclePause = 25;
			int cycles = 0;
            DateTime start = DateTime.Now;
			while (ns.DataAvailable == false)
			{
                if (cycles++ > 999)
					break;
                if ((DateTime.Now-start).TotalMilliseconds > 40000)
                    break;
				Thread.Sleep(cyclePause);
			}

			if (!ns.DataAvailable)
			{
                this.Close();
				throw new Exception("BMXNetBroker.ReceiveString timeout.  Connection Closed.");
			}

			byte[] bReadBuffer = new byte[1024];
			string sReadBuffer = "";
			StringBuilder sbAll = new StringBuilder("", 1024);
			int numberOfBytesRead = 0;

			// Incoming message may be larger than the buffer size.

			bool bFinished = false;
			bool bStarted = false;
			int lpBuf = 0;
			string sError = "";
			string sAppError = "";
			do
			{

				numberOfBytesRead = ns.Read(bReadBuffer, 0, bReadBuffer.Length); 
				if ((numberOfBytesRead == 1)&&(bStarted == false))
				{
                    //TAE: This following is suspect. If Read is blocking then this sleep is extra.
                    //This is rarely called
                    Thread.Sleep(15);
					numberOfBytesRead += ns.Read(bReadBuffer,1, bReadBuffer.Length-1); 
				}
				if (bStarted == false)
				{
					//Process error info at beginning of returned string
					int nErrLen = bReadBuffer[0];
					int nAppLen = bReadBuffer[bReadBuffer[0]+1];
					if ((bReadBuffer[2] == 0)&&(bReadBuffer[3] == 0)) 
					{ //special case: M error trap invoked in SND^XWBTCPC
						lpBuf += 2;
					}
					sError = Encoding.ASCII.GetString(bReadBuffer, lpBuf + 1, nErrLen);
					if (sError != "")
					{
                        sAppError = Encoding.ASCII.GetString(bReadBuffer, lpBuf + 1 + nErrLen + 1, nAppLen);
                        throw new BMXNetException(sError);
					}
					sAppError = Encoding.ASCII.GetString(bReadBuffer, lpBuf+1+nErrLen+1, nAppLen);
					lpBuf += (nErrLen + nAppLen + 2);
					numberOfBytesRead -= (nErrLen + nAppLen + 2);
					bStarted = true;
				}

                bFinished = FindChar(bReadBuffer, (char)4) > -1;
				sReadBuffer = Encoding.ASCII.GetString(bReadBuffer, lpBuf, numberOfBytesRead);
				lpBuf = 0;
                if (bFinished)
				{
					sbAll.Append(sReadBuffer, 0, numberOfBytesRead -1);
				}
				else 
				{
					sbAll.Append(sReadBuffer);
				}
			}
			while(!bFinished);

            String decodedReceiveString = sbAll.ToString();
            return decodedReceiveString;
			
		}
              public static int FindChar(byte[] c, char y)
        {           
            int n = 0;
            int nRet = -1;
            for (n = 0; n < c.Length; n++)
            {
                if (y == (char)c[n])
                {
                    nRet = n;
                    break;
                }
            }

            return nRet;
        }

        public static int FindChar(string s, char y)
        {
            int n = 0;
            int nRet = -1;
            foreach (char c in s)
            {
                if (y == c)
                {
                    nRet = n;
                    break;
                }
                n++;
            }
            return nRet;
        }

        protected TcpClient OpenConnectionCommon()
		{
			try
			{
                TcpClient connector = null; 

				try 
				{
                    BMXNetSocketBroker broker = (BMXNetSocketBroker)this.Framework.SocketBroker;

                    BMXNetSocketConnectionSpec ConnectionSpec = broker.ConnectionSpec;
					connector = new TcpClient();
                    connector.SendTimeout = ConnectionSpec.SendTimeout;
                    connector.ReceiveTimeout = ConnectionSpec.ReceiveTimeout;
					connector.Connect(ConnectionSpec.Server, ConnectionSpec.Port);	
				}
				catch (SocketException exSocket)
				{
					string s = exSocket.Message + exSocket.StackTrace;
					throw new BMXNetException(s);
				}				
			
				//Prepare & send the connect message
				string cSend = "TCPconnect^" + "" + "^^";
               int nLen = cSend.Length;
				string sLen = nLen.ToString();
				sLen = sLen.PadLeft(5, '0');
				cSend = "{BMX}" + sLen + cSend;

				NetworkStream ns = connector.GetStream();
				byte[] sendBytes = Encoding.ASCII.GetBytes(cSend);
				ns.Write(sendBytes,0,sendBytes.Length);        
                
                return connector;
			}
			catch (BMXNetException bmxEx)
			{
				throw bmxEx;
			}
			catch (Exception ex)
			{
				string s = ex.Message + ex.StackTrace;
				throw new BMXNetException(s);
			}	
		}//End OpenConnectionCommon

    }
    /// <summary>
    /// Class for passing result of AttemptChangeVerifyCode to calling routine
    /// </summary>
    public class VerifyCodeUpdateResult
    {
        //Members
        private DialogResult dr = new DialogResult();
        private bool bVerifyCodeWasSucessfullyChanged;

        //Properties
        public DialogResult DialogResult
        {
            set { this.dr  = value; }
            get { return this.dr; }
        }

        public bool WasVerifyCodeUpdatedSuccessfully
        {
            set { this.bVerifyCodeWasSucessfullyChanged = value; }
            get { return this.bVerifyCodeWasSucessfullyChanged; }
        }


    }
}
