using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Net.Sockets;
using ComponentPro.Net;

namespace ProxySample
{
    public partial class Sample : Form
    {
        private readonly SettingInfo _settings;

        public Sample()
        {
            InitializeComponent();

            // Set timeout values to 15s.
            proxyClient.Timeout = 15000;

            // Load settings from the Registry.
            _settings = SettingInfo.LoadConfig();

            // Get proxy settings.
            txtProxyHost.Text = _settings.ProxyServer;
            txtProxyPort.Text = _settings.ProxyPort.ToString();
            cbxType.SelectedIndex = (int)_settings.ProxyType;
            txtUserName.Text = _settings.ProxyUser;
            txtPassword.Text = _settings.ProxyPassword;
            txtDomain.Text = _settings.ProxyDomain;
            cbxMethod.SelectedIndex = (int)_settings.ProxyMethod;

            // Get destination host address.
            txtHost.Text = _settings.Host;
            txtPort.Text = _settings.Port.ToString();

#if !Framework4_5
            this.proxyClient.ConnectCompleted += this.proxyClient_ConnectCompleted;
#endif
        }

        /// <summary>
        /// Handles the form's Closed event.
        /// </summary>
        /// <param name="e">The event arguments.</param>
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            // Update settings.
            _settings.ProxyServer = txtProxyHost.Text;
            try
            {
                _settings.ProxyPort = int.Parse(txtProxyPort.Text);
            }
            catch
            {
                _settings.ProxyPort = 1080;
            }
            _settings.ProxyType = (ProxyType)cbxType.SelectedIndex;
            _settings.ProxyUser = txtUserName.Text;
            _settings.ProxyPassword = txtPassword.Text;
            _settings.ProxyDomain = txtDomain.Text;
            _settings.ProxyMethod = (ProxyHttpConnectAuthMethod)cbxMethod.SelectedIndex;            
            
            // Host info.
            _settings.Host = txtHost.Text;
            try
            {
                _settings.Port = int.Parse(txtPort.Text);
            }
            catch { _settings.Port = 80; }
            
            // And save to the Registry.
            _settings.SaveConfig();
        }

        /// <summary>
        /// Enables/disables controls.
        /// </summary>
        /// <param name="enable">Indicates whether to enable or disable the dialog.</param>
        private void EnableDialog(bool enable)
        {
            btnGet.Enabled = enable;
            grbSettings.Enabled = enable;
            grbHost.Enabled = enable;
            Util.EnableCloseButton(this, enable);
        }

        /// <summary>
        /// Handles the Get button's Click event.
        /// </summary>
        /// <param name="sender">The button object.</param>
        /// <param name="e">The event arguments.</param>
        private 
#if Framework4_5
            async
#endif
            void btnGet_Click(object sender, EventArgs e)
        {
            if (txtHost.Text.Length == 0)
            {
                MessageBox.Show("Host Name is required", "Error");
                return;
            }
            int port;

            try
            {
                // Parse the port number.
                port = int.Parse(txtPort.Text);
            }
            catch (Exception exc)
            {
                MessageBox.Show("Invalid port number: " + exc.Message, "Error");
                return;
            }

            int proxyPort = 0;
            if (txtProxyPort.Text.Length > 0)
                try
                {
                    // Parse the proxy port number.
                    proxyPort = int.Parse(txtProxyPort.Text);
                }
                catch (Exception exc)
                {
                    MessageBox.Show("Invalid proxy port number: " + exc.Message, "Error");
                    return;
                }

            // Disable dialog.
            EnableDialog(false);

            proxyClient.ProxyHost = txtProxyHost.Text;
            proxyClient.ProxyPort = proxyPort;
            proxyClient.ProxyType = (ProxyType)cbxType.SelectedIndex;
            proxyClient.UserName = txtUserName.Text;
            proxyClient.Password = txtPassword.Text;
            proxyClient.Domain = txtDomain.Text;
            proxyClient.AuthenticationMethod = (ProxyHttpConnectAuthMethod)cbxMethod.SelectedIndex;

            txtContent.Text = "Downloading...";

#if Framework4_5
            try
            {
                // Asynchronously connect to the specified host.
                await proxyClient.ConnectAsync(txtHost.Text, port);
                ShowResult();
            }
            catch (Exception ex)
            {
                Util.ShowError(ex);
                txtContent.Text = string.Empty;
            }

            // Close the connection.
			proxyClient.Disconnect();

            // Enable dialog.
            EnableDialog(true);
#else
            // Asynchronously connect to the specified host.
            proxyClient.ConnectAsync(txtHost.Text, port);
#endif
        }

        void ShowResult()
        {
            try
            {
                // Connected?
                if (proxyClient.Connected)
                {
                    // Construct a request message sending to the destination host.
                    string requestMessage =
                        "GET / HTTP/1.1\r\n" +
                        "Host: " + txtHost.Text + "\r\n" +
                        "Connection: close\r\n" +
                        "\r\n";

                    // Send the request message.
                    NetworkStream nsr = proxyClient.GetStream();
                    StreamWriter streamWriter = new StreamWriter(nsr);
                    streamWriter.Write(requestMessage);
                    streamWriter.Flush();

                    // And read the response.
                    StreamReader streamReader = new StreamReader(nsr);
                    txtContent.Text = streamReader.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
                Util.ShowError(ex);
                txtContent.Text = string.Empty;
            }
        }

#if !Framework4_5
        /// <summary>
        /// Handles the proxy client's ConnectCompleted event.
        /// </summary>
        /// <param name="sender">The proxy client object.</param>
        /// <param name="e">The event arguments.</param>
        private void proxyClient_ConnectCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                Util.ShowError(e.Error);
                txtContent.Text = string.Empty;
            }
            else
            {
                ShowResult();
            }

			// Close the connection.
			proxyClient.Disconnect();

            // Enable dialog.
            EnableDialog(true);
        }
#endif

        /// <summary>
        /// Handles the combo box proxy type's SelectedIndexChanged event.
        /// </summary>
        /// <param name="sender">The combo box.</param>
        /// <param name="e">The event arguments.</param>
        private void cbx_SelectedIndexChanged(object sender, EventArgs e)
        {
            bool enable = cbxType.SelectedIndex > 0;

            cbxMethod.Enabled = cbxType.SelectedIndex == (int)ProxyType.HttpConnect; // Authentication method is available for HTTP Connect only.
            txtDomain.Enabled = cbxMethod.Enabled && cbxMethod.SelectedIndex == (int)ProxyHttpConnectAuthMethod.Ntlm; // Domain is available for NTLM authentication method only.
            txtUserName.Enabled = enable/* && cbxType.SelectedIndex != (int)ProxyType.SendToProxy*/; // User name and password are ignored with SendToProxy proxy type.
            txtPassword.Enabled = enable/* && cbxType.SelectedIndex != (int)ProxyType.SendToProxy*/;
            txtProxyHost.Enabled = enable; // Proxy host and port are not available in NoProxy type.
            txtProxyPort.Enabled = enable;
        }
    }
}