/********************************************************************************\
*  This sample is supplied as is with no implied warranty.  
*  It is designed to assist you in using the Cisco AnyConnect VPN API. 
*  It is assumed that you will build a production application and 
*  refer to this sample as a reference only.
\********************************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using VpnApiLib;

namespace CSharpComSample
{
    public partial class MainForm : Form
    {
        private VpnApiClass vpnApi;
        public const string clientName = "Cisco AnyConnect VPN Client";

        private Icon connectedIcon;
        private Icon disconnectedIcon;
        private Icon reconnectingIcon;
        private Icon disconnectingIcon;

        private StatisticsDlg statsDlg = new StatisticsDlg();
        private MessagesDlg messagesDlg = new MessagesDlg();

        public MainForm()
        {
            InitializeComponent();
        }
        
        private void MainForm_Load(object sender, EventArgs e)
        {
            try
            {
                initializeVpnApi();

                IStringCollection hostNames = vpnApi.HostNames;
                for (int i = 1; i <= hostNames.Count; i++)
                {
                    connectToComboBox.Items.Add(hostNames[i]);
                }
                string defaultHost = vpnApi.DefaultHostName;
                if (connectToComboBox.Items.Contains(defaultHost))
                {
                    connectToComboBox.SelectedIndex =
                        connectToComboBox.Items.IndexOf(defaultHost);
                }
                else
                {
                    connectToComboBox.Items.Add(defaultHost);
                }

                System.Reflection.Assembly currentAssembly = System.Reflection.Assembly.GetExecutingAssembly();
                connectedIcon = new Icon(currentAssembly.GetManifestResourceStream("CSharpComSample.Resources.connected.ico"));
                disconnectedIcon = new Icon(currentAssembly.GetManifestResourceStream("CSharpComSample.Resources.unconnected.ico"));
                reconnectingIcon = new Icon(currentAssembly.GetManifestResourceStream("CSharpComSample.Resources.reconnecting.ico"));
                disconnectingIcon = new Icon(currentAssembly.GetManifestResourceStream("CSharpComSample.Resources.disconnecting.ico"));
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
                Close();
            }
        }

        private void initializeVpnApi()
        {
            vpnApi = new VpnApiClass();


            //Wire up the Banner Notification event mechanism.
            vpnApi.VpnBannerNotification
                += new IVpnApiEvents_VpnBannerNotificationEventHandler(VpnBannerNotificationEventHandler);
            //Wire up the PreConnect Reminder Notification event mechanism.
            vpnApi.VpnPreConnectReminderNotification
                += new IVpnApiEvents_VpnPreConnectReminderNotificationEventHandler(VpnPreConnectReminderNotificationEventHandler);
            //Wire up the Exit Notification event mechanism.
            vpnApi.VpnExitNotification
                += new IVpnApiEvents_VpnExitNotificationEventHandler(VpnExitNotificationEventHandler);
            //Wire up the Service Ready Notification event mechanism.
            vpnApi.VpnServiceReadyNotification
                += new IVpnApiEvents_VpnServiceReadyNotificationEventHandler(VpnServiceReadyNotificationEventHandler);
            //Wire up the User Prompt Notification event mechanism.
            vpnApi.VpnUserPromptNotification
                += new IVpnApiEvents_VpnUserPromptNotificationEventHandler(VpnUserPromptNotificationEventHandler);
            //Wire up the Web Launch Host Notification event mechanism.
            vpnApi.VpnWebLaunchHostNotification
                += new IVpnApiEvents_VpnWebLaunchHostNotificationEventHandler(VpnWebLaunchHostNotificationEventHandler);
            //Wire up the WMHint Notification event mechanism.
            vpnApi.VpnWMHintNotification
                += new IVpnApiEvents_VpnWMHintNotificationEventHandler(VpnWMHintNotificationEventHandler);
            //Wire up the Stats Notification event mechanism.
            vpnApi.VpnStatsNotification
                += new IVpnApiEvents_VpnStatsNotificationEventHandler(VpnStatsNotificationEventHandler);
            //Wire up the State Notification event mechanism.
            vpnApi.VpnStateNotification
                += new IVpnApiEvents_VpnStateNotificationEventHandler(VpnStateNotificationEventHandler);
            //Wire up the Notice Notification event mechanism.
            vpnApi.VpnNoticeNotification
                += new IVpnApiEvents_VpnNoticeNotificationEventHandler(VpnNoticeNotificationEventHandler);
            //Wire up the EventAvailable Notification event mechanism.
            vpnApi.VpnEventAvailableNotification
                += new IVpnApiEvents_VpnEventAvailableNotificationEventHandler(VpnEventAvailableNotificationEventHandler);

            //Enable the consumer driven event model.
            //It's required to enable this as the old event model is deprecated.
            vpnApi.EnableConsumerDrivenEventModel = true;

            //Start the AnyConnect VPN API by attaching to it.
            vpnApi.Attach(ClientType.ClientType_GUI, /* primary gui */
                          true,   /* don't want to be a diminished API user */
                          false); /* suppress auto-connect at launch */
        }

        ////////////////////////////// VPN API COM CALLBACKS /////////////////////////////////
        ///// NOTE:  COM Callbacks can be called in a different thread than the UI Controls. 
        /////        You cannot directly access controls from a thread that did not         
        /////        create the control. Attempting to do so results in an InvalidOperationException.         
        /////        Test for this case and delegate as needed.                                               
        //////////////////////////////////////////////////////////////////////////////////////

        //Process State callbacks
        private void VpnStateNotificationEventHandler(VPNState eState, VPNSubState eSubState, string stateString)
        {
            if (this.stateLabel.InvokeRequired)
            {
                
                BeginInvoke(new MethodInvoker(delegate { Vpn_State(eState, eSubState, stateString); }));
            }
            else
            {
                Vpn_State(eState, eSubState, stateString);
            }
        }

        //Exit notification. An Exit notification MUST be obeyed. The vpndownloader terminates
        //any process that has the vpn components loaded in memory, including the COM API dll.
        //
        private void VpnExitNotificationEventHandler(string strExitMessage, int exitCode)
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new MethodInvoker(delegate { Vpn_ExitNotify(); }));
            }
            else
            {
                Vpn_ExitNotify();
            }
        }

        private void VpnStatsNotificationEventHandler(IVpnStats vpnStats)
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new MethodInvoker(delegate { Vpn_Stats(vpnStats); }));
            }
            else
            {
                Vpn_Stats(vpnStats);
            }
        }

        private void VpnNoticeNotificationEventHandler(string noticeString, MessageType eMessageType)
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new MethodInvoker(delegate { Vpn_State(noticeString, eMessageType); }));
            }
            else
            {
                Vpn_State(noticeString, eMessageType);
            }
        }

        private void VpnBannerNotificationEventHandler(string strBannerMessage)
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new MethodInvoker(delegate { Vpn_Banner(strBannerMessage); }));
            }
            else
            {
                Vpn_Banner(strBannerMessage);
            }
             
        }

        private void VpnPreConnectReminderNotificationEventHandler(string strPreConnectReminderMessage)
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new MethodInvoker(delegate { Vpn_PreConnectReminder(strPreConnectReminderMessage); }));
            }
            else
            {
                Vpn_PreConnectReminder(strPreConnectReminderMessage);
            }

        }

        private void VpnServiceReadyNotificationEventHandler()
        {
            // Intentionally blank, UI visual state initialization
            // driven by state callbacks.
        }

        private void VpnUserPromptNotificationEventHandler(IConnectPromptInfo connectPromptInfo)
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new MethodInvoker(delegate { Vpn_UserPrompt(connectPromptInfo); }));
            }
            else
            {
                Vpn_UserPrompt(connectPromptInfo);
            }
        }

        private void VpnWebLaunchHostNotificationEventHandler(string strActiveHost)
        {
            if (this.InvokeRequired)
            {
                BeginInvoke(new MethodInvoker(delegate { connectToComboBox.Text = strActiveHost; }));
            }
            else
            {
                connectToComboBox.Text = strActiveHost;
            }
        }

        private void VpnWMHintNotificationEventHandler(WMHint eHint, WMHintReason eReason)
        {
            if (this.InvokeRequired)
            {

                BeginInvoke(new MethodInvoker(delegate { Vpn_WindowOperation(eHint, eReason); }));
            }
            else
            {
                Vpn_WindowOperation(eHint, eReason);
            }
        }


        private void connectedLayout()
        {
            stateIconLabel.Image = global::CSharpComSample.Properties.Resources.cvc_connect;
            toolStripProgressBar.Visible = false;
            connectButton.Visible = false;
            disconnectButton.Visible = true;
            disconnectButton.Enabled = true;
            notifyIcon.Icon = connectedIcon;
            disconnectedToolStripMenuItem.Enabled = true;
            connectTimePanel.Show();
        }

        private void disconnectedLayout()
        {
            stateIconLabel.Image = global::CSharpComSample.Properties.Resources.cvc_disconnect;
            toolStripProgressBar.Visible = false;
            connectButton.Enabled = true;
            connectButton.Visible = true;
            disconnectButton.Visible = false;
            notifyIcon.Icon = disconnectedIcon;
            disconnectedToolStripMenuItem.Enabled = false;
            connectTimePanel.Hide();
        }

        private void Vpn_State(VPNState eState, VPNSubState eSubState, string stateString)
        {
            stateLabel.Text = stateString;
            switch (eState)
            {
                case VPNState.CONNECTED:
                    connectedLayout();
                    break;
                case VPNState.DISCONNECTED:
                    disconnectedLayout();
                    break;
                case VPNState.DISCONNECTING:
                    notifyIcon.Icon = disconnectingIcon;
                    break;
                case VPNState.RECONNECTING:
                    notifyIcon.Icon = reconnectingIcon;
                    break;
                default:
                    stateIconLabel.Image = global::CSharpComSample.Properties.Resources.cvc_disconnect;
                    break;
            }
        }

        private void Vpn_ExitNotify()
        {
            Close();
        }

        private void Vpn_Stats(IVpnStats vpnStats)
        {
            try
            {
                timeConnectedLabel.Text = vpnStats[VPNStatsTag.TimeConnected];

                if (statsDlg.Visible)
                {
                    statsDlg.UpdateStats(vpnStats);
                }
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void Vpn_State(string noticeString, MessageType eMessageType)
        {
            messagesDlg.AddMessage(noticeString, eMessageType);
            switch (eMessageType)
            {
                case MessageType.MsgType_Error:
                    MessageBox.Show(noticeString, clientName, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    break;
                case MessageType.MsgType_Warn:
                    toolStripStatusLabel.Text = noticeString;
                    break;
                case MessageType.MsgType_Info:
                    toolStripStatusLabel.Text = noticeString;
                    break;
                case MessageType.MsgType_Status:
                    toolStripStatusLabel.Text = noticeString;
                    break;
            }
        }

        private void Vpn_Banner(string strBannerMessage)
        {
            BannerDlg bannerDlg = new BannerDlg(strBannerMessage);
            DialogResult dr = bannerDlg.ShowDialog();
            try
            {
                vpnApi.BannerResponse = ((dr == DialogResult.OK) ? true : false);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
       }

        private void Vpn_PreConnectReminder(string strPreConnectReminderMessage)
        {
            DialogResult dr = MessageBox.Show(strPreConnectReminderMessage, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
            try
            {
                vpnApi.PreConnectReminderResponse = ((dr == DialogResult.OK) ? true : false);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void Vpn_UserPrompt(IConnectPromptInfo connectPromptInfo)
        {
            DynamicInputDlg dynamicInputDlg = new DynamicInputDlg(vpnApi);
            dynamicInputDlg.CreateDynamicInputs(connectPromptInfo);
            DialogResult dr = dynamicInputDlg.ShowDialog();
            if (dr == DialogResult.Cancel)
            {
                disconnectedLayout();
            }
            else if (dr == DialogResult.OK)
            {
                BackgroundWorker backgroundSubmitWorker = new System.ComponentModel.BackgroundWorker();
                backgroundSubmitWorker.DoWork +=
                    new DoWorkEventHandler(userSubmit_DoWork);
                backgroundSubmitWorker.RunWorkerAsync();
            }
        }

        private void userSubmit_DoWork(object Sender, DoWorkEventArgs e)
        {
            vpnApi.UserSubmit();
        }

        private void Vpn_WindowOperation(WMHint eHint, WMHintReason eReason)
        {
            switch (eHint)
            {
                case WMHint.MINIMIZE:
                    Hide();
                    break;
                case WMHint.OPEN:
                    Show();
                    break;
                case WMHint.QUIT:
                    shutdownClient();
                    break;
            }
        }

        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            shutdownClient();
        }

        private void shutdownClient()
        {
            vpnApi.Detach();
            Application.Exit();
        }

        private void VpnEventAvailableNotificationEventHandler()
        {
            if (this.InvokeRequired)
            {
                //Handle ProcessEvents on UI thread to avoid blocking the VpnEventAvailableNotification callback
                BeginInvoke(new MethodInvoker(delegate { Vpn_EventAvailable(); }));
            }
            else
            {
                Vpn_EventAvailable();
            }
        }

        private void Vpn_EventAvailable()
        {
            try
            {
                vpnApi.ProcessEvents();
            }
            catch (Exception exception)
            {
                //Add your logging/messagebox implementation here.
                MessageBox.Show(exception.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void connectButton_Click(object sender, EventArgs e)
        {
            string host = (string)connectToComboBox.Text;
            showProgress();
            try
            {
                vpnApi.ConnectVpn(host);
                connectButton.Enabled = false;
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
                disconnectedLayout();
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutDlg aboutDlg = new AboutDlg();
            aboutDlg.ShowDialog();
        }

        private void preferencesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PreferencesDlg preferencesDlg = new PreferencesDlg();
            preferencesDlg.ShowDialog();
        }

        private void statisticsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            statsDlg.Show();
        }

        private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Show();
            BringToFront();
        }

        private void disconnectButton_Click(object sender, EventArgs e)
        {
            showProgress();

            vpnApi.DisconnectVpn();
        }

        private void showProgress()
        {
            toolStripProgressBar.ProgressBar.Style = ProgressBarStyle.Marquee;
            toolStripProgressBar.Visible = true;
            toolStripProgressBar.Value = 100;
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Show();
            BringToFront();
        }

        private void disconnectedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            vpnApi.DisconnectVpn();
        }

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            shutdownClient();
        }

        private void messagesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            messagesDlg.Show();
        }
    }
}