/*
 * SocketOps.cs --
 *
 * Copyright (c) 2007-2012 by Joe Mistachkin.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: $
 */

using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using Eagle._Attributes;
using Eagle._Components.Public;
using Eagle._Components.Public.Delegates;
using Eagle._Constants;
using Eagle._Containers.Public;
using Eagle._Interfaces.Public;

namespace Eagle._Components.Private
{
    [ObjectId("71b14766-48a0-45d5-9254-640fde03509d")]
    internal static class SocketOps
    {
        #region Private Static Data
        private static readonly object syncRoot = new object();

        ///////////////////////////////////////////////////////////////////////////////////////////////

        private static PropertyInfo tcpListenerActive;
        private static PropertyInfo socketCleanedUp;
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Private Event Helper Methods
        private static StringList GetAsyncCompletedArguments(
            Uri uri,
            string method,
            NameValueCollection data,
            string fileName,
            AsyncCompletedEventArgs eventArgs
            )
        {
            StringList result = new StringList();

            if (uri != null)
            {
                result.Add("uri");
                result.Add(uri.ToString());
            }

            if (method != null)
            {
                result.Add("method");
                result.Add(method);
            }

            if (data != null)
            {
                result.Add("data");
                result.Add(ListOps.FromNameValueCollection(
                    data, new StringList()).ToString());
            }

            if (fileName != null)
            {
                result.Add("fileName");
                result.Add(fileName);
            }

            if (eventArgs != null)
            {
                bool canceled = eventArgs.Cancelled;

                result.Add("canceled");
                result.Add(canceled.ToString());

                Exception exception = eventArgs.Error;

                if (exception != null)
                {
                    result.Add("exception");
                    result.Add(exception.GetType().ToString());
                    result.Add("error");
                    result.Add(exception.ToString());
                }
            }

            return result;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Private Download Event Handlers
        #region Download Data Event Handlers
        private static void DownloadDataAsyncCompleted(
            object sender,
            DownloadDataCompletedEventArgs e
            )
        {
            try
            {
                if (e == null)
                    return;

                ICallback callback = e.UserState as ICallback;

                if (callback == null)
                    return;

                Uri uri = null;
                IClientData clientData = callback.ClientData;

                if (clientData != null)
                {
                    IAnyPair<WebClient, Uri> pair =
                        clientData.Data as IAnyPair<WebClient, Uri>;

                    if (pair != null)
                    {
                        WebClient webClient = pair.X;

                        if (webClient != null)
                        {
                            webClient.Dispose();
                            webClient = null;
                        }

                        uri = pair.Y;
                    }

                    clientData.Data = null;
                }

                /* NO RESULT */
                callback.FireEventHandler(sender, e,
                    GetAsyncCompletedArguments(uri, null, null, null, e));
            }
            catch (Exception ex)
            {
                DebugOps.Complain(ReturnCode.Error, ex);
            }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Download File Event Handlers
        private static void DownloadFileAsyncCompleted(
            object sender,
            AsyncCompletedEventArgs e
            )
        {
            try
            {
                if (e == null)
                    return;

                ICallback callback = e.UserState as ICallback;

                if (callback == null)
                    return;

                Uri uri = null;
                string method = null;
                string fileName = null;
                IClientData clientData = callback.ClientData;

                if (clientData != null)
                {
                    IAnyTriplet<WebClient, Uri, string> triplet =
                        clientData.Data as IAnyTriplet<WebClient, Uri, string>;

                    if (triplet != null)
                    {
                        WebClient webClient = triplet.X;

                        if (webClient != null)
                        {
                            webClient.Dispose();
                            webClient = null;
                        }

                        uri = triplet.Y;
                        fileName = triplet.Z;
                    }

                    clientData.Data = null;
                }

                ReturnCode code;
                Result result = null;

                code = callback.Invoke(
                    GetAsyncCompletedArguments(uri, method, null, fileName, e),
                    ref result);

                if (code != ReturnCode.Ok)
                    DebugOps.Complain(code, result);
            }
            catch (Exception ex)
            {
                DebugOps.Complain(ReturnCode.Error, ex);
            }
        }
        #endregion
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Public Web Download Methods
        #region WebClient Support Methods
        public static WebClient CreateWebClient(
            Interpreter interpreter,
            string argument
            )
        {
            if (interpreter != null)
            {
                NewWebClientCallback callback = interpreter.NewWebClientCallback;

                if (callback != null)
                    return callback(interpreter, argument);
            }

            return new WebClient();
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Download Data Methods
        public static ReturnCode DownloadData(
            Interpreter interpreter,
            Uri uri,
            bool trusted,
            ref byte[] bytes,
            ref Result error
            )
        {
            bool wasTrusted = UpdateOps.IsTrusted();

            TraceOps.DebugTrace(String.Format(
                "{0}: DownloadData: uri = {1}, trusted = {2}, wasTrusted = {3}{4}",
                GlobalState.GetCurrentThreadId(), FormatOps.WrapOrNull(uri),
                trusted, wasTrusted, Environment.NewLine), typeof(SocketOps).Name);

            ReturnCode code = trusted ? wasTrusted ? ReturnCode.Ok :
                UpdateOps.SetTrusted(trusted, ref error) : ReturnCode.Ok;

            if (code == ReturnCode.Ok)
            {
                try
                {
                    try
                    {
                        using (WebClient webClient = CreateWebClient(
                                interpreter, "DownloadData"))
                        {
                            if (webClient != null)
                            {
                                bytes = webClient.DownloadData(uri);
                                return ReturnCode.Ok;
                            }
                            else
                            {
                                error = "could not create web client";
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                    }
                }
                finally
                {
                    if (trusted && !wasTrusted)
                    {
                        ReturnCode untrustCode;
                        Result untrustError = null;

                        untrustCode = UpdateOps.SetTrusted(
                            false, ref untrustError);

                        if (untrustCode != ReturnCode.Ok)
                            DebugOps.Complain(untrustCode, untrustError);
                    }
                }
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode DownloadDataAsync(
            Interpreter interpreter,
            StringList arguments,
            CallbackFlags callbackFlags,
            Uri uri,
            ref Result error
            )
        {
            TraceOps.DebugTrace(String.Format(
                "{0}: DownloadDataAsync: interpreter = {1}, " +
                "arguments = {2}, callbackFlags = {3}, uri = {4}{5}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(interpreter),
                FormatOps.WrapOrNull(true, true, arguments),
                FormatOps.WrapOrNull(callbackFlags),
                FormatOps.WrapOrNull(uri),
                Environment.NewLine), typeof(SocketOps).Name);

            ReturnCode code = ReturnCode.Ok;
            WebClient webClient = null;

            try
            {
                ICallback callback = CommandCallback.Create(
                    callbackFlags, ObjectFlags.Callback, interpreter, null,
                    arguments, ref error);

                if (callback != null)
                {
                    try
                    {
                        webClient = CreateWebClient(
                            interpreter, "DownloadDataAsync");

                        if (webClient != null)
                        {
                            callback.ClientData = new ClientData(
                                new AnyPair<WebClient, Uri>(
                                    webClient, uri));

                            webClient.DownloadDataCompleted +=
                                new DownloadDataCompletedEventHandler(
                                    DownloadDataAsyncCompleted);

                            /* NO RESULT */
                            webClient.DownloadDataAsync(uri, callback);
                        }
                        else
                        {
                            error = "could not create web client";
                            code = ReturnCode.Error;
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                        code = ReturnCode.Error;
                    }
                }
                else
                {
                    code = ReturnCode.Error;
                }
            }
            finally
            {
                if ((code != ReturnCode.Ok) && (webClient != null))
                {
                    ReturnCode disposeCode;
                    Result disposeError = null;

                    disposeCode = ObjectOps.TryDispose(
                        webClient, ref disposeError);

                    if (disposeCode != ReturnCode.Ok)
                        DebugOps.Complain(interpreter, disposeCode, disposeError);
                }
            }

            return code;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Download File Methods
        public static ReturnCode DownloadFile(
            Interpreter interpreter,
            Uri uri,
            string fileName,
            bool trusted,
            ref Result error
            )
        {
            bool wasTrusted = UpdateOps.IsTrusted();

            TraceOps.DebugTrace(String.Format(
                "{0}: DownloadFile: uri = {1}, fileName = {2}, trusted = {3}, wasTrusted = {4}{5}",
                GlobalState.GetCurrentThreadId(), FormatOps.WrapOrNull(uri),
                FormatOps.WrapOrNull(fileName), trusted, wasTrusted, Environment.NewLine),
                typeof(SocketOps).Name);

            ReturnCode code = trusted ? wasTrusted ? ReturnCode.Ok :
                UpdateOps.SetTrusted(trusted, ref error) : ReturnCode.Ok;

            if (code == ReturnCode.Ok)
            {
                try
                {
                    try
                    {
                        using (WebClient webClient = CreateWebClient(
                                interpreter, "DownloadFile"))
                        {
                            if (webClient != null)
                            {
                                /* NO RESULT */
                                webClient.DownloadFile(uri, fileName);

                                return ReturnCode.Ok;
                            }
                            else
                            {
                                error = "could not create web client";
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                    }
                }
                finally
                {
                    if (trusted && !wasTrusted)
                    {
                        ReturnCode untrustCode;
                        Result untrustError = null;

                        untrustCode = UpdateOps.SetTrusted(
                            false, ref untrustError);

                        if (untrustCode != ReturnCode.Ok)
                            DebugOps.Complain(untrustCode, untrustError);
                    }
                }
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode DownloadFileAsync(
            Interpreter interpreter,
            StringList arguments,
            CallbackFlags callbackFlags,
            Uri uri,
            string fileName,
            ref Result error
            )
        {
            TraceOps.DebugTrace(String.Format(
                "{0}: DownloadFileAsync: interpreter = {1}, " +
                "arguments = {2}, callbackFlags = {3}, uri = {4}, " +
                "fileName = {5}{6}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(interpreter),
                FormatOps.WrapOrNull(true, true, arguments),
                FormatOps.WrapOrNull(callbackFlags),
                FormatOps.WrapOrNull(uri),
                FormatOps.WrapOrNull(fileName),
                Environment.NewLine), typeof(SocketOps).Name);

            ReturnCode code = ReturnCode.Ok;
            WebClient webClient = null;

            try
            {
                ICallback callback = CommandCallback.Create(
                    callbackFlags, ObjectFlags.Callback, interpreter, null,
                    arguments, ref error);

                if (callback != null)
                {
                    try
                    {
                        webClient = CreateWebClient(
                            interpreter, "DownloadFileAsync");

                        if (webClient != null)
                        {
                            callback.ClientData = new ClientData(
                                new AnyTriplet<WebClient, Uri, string>(
                                    webClient, uri, fileName));

                            webClient.DownloadFileCompleted +=
                                new AsyncCompletedEventHandler(
                                    DownloadFileAsyncCompleted);

                            /* NO RESULT */
                            webClient.DownloadFileAsync(uri, fileName, callback);
                        }
                        else
                        {
                            error = "could not create web client";
                            code = ReturnCode.Error;
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                        code = ReturnCode.Error;
                    }
                }
                else
                {
                    code = ReturnCode.Error;
                }
            }
            finally
            {
                if ((code != ReturnCode.Ok) && (webClient != null))
                {
                    ReturnCode disposeCode;
                    Result disposeError = null;

                    disposeCode = ObjectOps.TryDispose(
                        webClient, ref disposeError);

                    if (disposeCode != ReturnCode.Ok)
                        DebugOps.Complain(interpreter, disposeCode, disposeError);
                }
            }

            return code;
        }
        #endregion
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Private Upload Event Handlers
        #region Upload Values Event Handlers
        private static void UploadValuesAsyncCompleted(
            object sender,
            UploadValuesCompletedEventArgs e
            )
        {
            try
            {
                if (e == null)
                    return;

                ICallback callback = e.UserState as ICallback;

                if (callback == null)
                    return;

                Uri uri = null;
                string method = null;
                NameValueCollection data = null;
                IClientData clientData = callback.ClientData;

                if (clientData != null)
                {
                    IAnyTriplet<WebClient, Uri, IAnyPair<string, NameValueCollection>>
                        triplet = clientData.Data as
                    IAnyTriplet<WebClient, Uri, IAnyPair<string, NameValueCollection>>;

                    if (triplet != null)
                    {
                        WebClient webClient = triplet.X;

                        if (webClient != null)
                        {
                            webClient.Dispose();
                            webClient = null;
                        }

                        uri = triplet.Y;

                        IAnyPair<string, NameValueCollection> pair = triplet.Z;

                        if (pair != null)
                        {
                            method = pair.X;
                            data = pair.Y;
                        }
                    }

                    clientData.Data = null;
                }

                /* NO RESULT */
                callback.FireEventHandler(sender, e,
                    GetAsyncCompletedArguments(uri, method, data, null, e));
            }
            catch (Exception ex)
            {
                DebugOps.Complain(ReturnCode.Error, ex);
            }
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Upload File Event Handlers
        private static void UploadFileAsyncCompleted(
            object sender,
            UploadFileCompletedEventArgs e
            )
        {
            try
            {
                if (e == null)
                    return;

                ICallback callback = e.UserState as ICallback;

                if (callback == null)
                    return;

                Uri uri = null;
                string method = null;
                string fileName = null;
                IClientData clientData = callback.ClientData;

                if (clientData != null)
                {
                    IAnyTriplet<WebClient, Uri, IAnyPair<string, string>>
                        triplet = clientData.Data as
                    IAnyTriplet<WebClient, Uri, IAnyPair<string, string>>;

                    if (triplet != null)
                    {
                        WebClient webClient = triplet.X;

                        if (webClient != null)
                        {
                            webClient.Dispose();
                            webClient = null;
                        }

                        uri = triplet.Y;

                        IAnyPair<string, string> pair = triplet.Z;

                        if (pair != null)
                        {
                            method = pair.X;
                            fileName = pair.Y;
                        }
                    }

                    clientData.Data = null;
                }

                ReturnCode code;
                Result result = null;

                code = callback.Invoke(
                    GetAsyncCompletedArguments(uri, method, null, fileName, e),
                    ref result);

                if (code != ReturnCode.Ok)
                    DebugOps.Complain(code, result);
            }
            catch (Exception ex)
            {
                DebugOps.Complain(ReturnCode.Error, ex);
            }
        }
        #endregion
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Public Web Upload Methods
        #region Upload Values Methods
        public static ReturnCode UploadValues(
            Interpreter interpreter,
            Uri uri,
            string method,
            NameValueCollection data,
            bool trusted,
            ref byte[] bytes,
            ref Result error
            )
        {
            bool wasTrusted = UpdateOps.IsTrusted();

            TraceOps.DebugTrace(String.Format(
                "{0}: UploadValues: uri = {1}, method = {2}, " +
                "data = {3}, trusted = {4}, wasTrusted = {5}{6}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(uri), FormatOps.WrapOrNull(method),
                FormatOps.NameValueCollection(data, true),
                trusted, wasTrusted, Environment.NewLine),
                typeof(SocketOps).Name);

            ReturnCode code = trusted ? wasTrusted ? ReturnCode.Ok :
                UpdateOps.SetTrusted(trusted, ref error) : ReturnCode.Ok;

            if (code == ReturnCode.Ok)
            {
                try
                {
                    try
                    {
                        using (WebClient webClient = CreateWebClient(
                                interpreter, "UploadValues"))
                        {
                            if (webClient != null)
                            {
                                bytes = webClient.UploadValues(uri, method, data);
                                return ReturnCode.Ok;
                            }
                            else
                            {
                                error = "could not create web client";
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                    }
                }
                finally
                {
                    if (trusted && !wasTrusted)
                    {
                        ReturnCode untrustCode;
                        Result untrustError = null;

                        untrustCode = UpdateOps.SetTrusted(
                            false, ref untrustError);

                        if (untrustCode != ReturnCode.Ok)
                            DebugOps.Complain(untrustCode, untrustError);
                    }
                }
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode UploadValuesAsync(
            Interpreter interpreter,
            StringList arguments,
            CallbackFlags callbackFlags,
            Uri uri,
            string method,
            NameValueCollection data,
            ref Result error
            )
        {
            TraceOps.DebugTrace(String.Format(
                "{0}: UploadValuesAsync: interpreter = {1}, " +
                "arguments = {2}, callbackFlags = {3}, uri = {4}, " +
                "method = {5}, data = {6}{7}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(interpreter),
                FormatOps.WrapOrNull(true, true, arguments),
                FormatOps.WrapOrNull(callbackFlags),
                FormatOps.WrapOrNull(uri),
                FormatOps.WrapOrNull(method),
                FormatOps.NameValueCollection(data, true),
                Environment.NewLine), typeof(SocketOps).Name);

            ReturnCode code = ReturnCode.Ok;
            WebClient webClient = null;

            try
            {
                ICallback callback = CommandCallback.Create(
                    callbackFlags, ObjectFlags.Callback, interpreter, null,
                    arguments, ref error);

                if (callback != null)
                {
                    try
                    {
                        webClient = CreateWebClient(
                            interpreter, "UploadValuesAsync");

                        if (webClient != null)
                        {
                            callback.ClientData = new ClientData(
                                new AnyTriplet<WebClient, Uri,
                                    IAnyPair<string, NameValueCollection>>(
                                        webClient, uri, new AnyPair<string,
                                            NameValueCollection>(method, data)));

                            webClient.UploadValuesCompleted +=
                                new UploadValuesCompletedEventHandler(
                                    UploadValuesAsyncCompleted);

                            /* NO RESULT */
                            webClient.UploadValuesAsync(uri, method, data, callback);
                        }
                        else
                        {
                            error = "could not create web client";
                            code = ReturnCode.Error;
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                        code = ReturnCode.Error;
                    }
                }
                else
                {
                    code = ReturnCode.Error;
                }
            }
            finally
            {
                if ((code != ReturnCode.Ok) && (webClient != null))
                {
                    ReturnCode disposeCode;
                    Result disposeError = null;

                    disposeCode = ObjectOps.TryDispose(
                        webClient, ref disposeError);

                    if (disposeCode != ReturnCode.Ok)
                        DebugOps.Complain(interpreter, disposeCode, disposeError);
                }
            }

            return code;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Upload File Methods
        public static ReturnCode UploadFile(
            Interpreter interpreter,
            Uri uri,
            string method,
            string fileName,
            bool trusted,
            ref Result error
            )
        {
            bool wasTrusted = UpdateOps.IsTrusted();

            TraceOps.DebugTrace(String.Format(
                "{0}: UploadFile: uri = {1}, method = {2}, " +
                "fileName = {3}, trusted = {4}, wasTrusted = {5}{6}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(uri),
                FormatOps.WrapOrNull(method),
                FormatOps.WrapOrNull(fileName),
                trusted, wasTrusted, Environment.NewLine),
                typeof(SocketOps).Name);

            ReturnCode code = trusted ? wasTrusted ? ReturnCode.Ok :
                UpdateOps.SetTrusted(trusted, ref error) : ReturnCode.Ok;

            if (code == ReturnCode.Ok)
            {
                try
                {
                    try
                    {
                        using (WebClient webClient = CreateWebClient(
                                interpreter, "UploadFile"))
                        {
                            if (webClient != null)
                            {
                                /* NO RESULT */
                                webClient.UploadFile(uri, method, fileName);

                                return ReturnCode.Ok;
                            }
                            else
                            {
                                error = "could not create web client";
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                    }
                }
                finally
                {
                    if (trusted && !wasTrusted)
                    {
                        ReturnCode untrustCode;
                        Result untrustError = null;

                        untrustCode = UpdateOps.SetTrusted(
                            false, ref untrustError);

                        if (untrustCode != ReturnCode.Ok)
                            DebugOps.Complain(untrustCode, untrustError);
                    }
                }
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode UploadFileAsync(
            Interpreter interpreter,
            StringList arguments,
            CallbackFlags callbackFlags,
            Uri uri,
            string method,
            string fileName,
            ref Result error
            )
        {
            TraceOps.DebugTrace(String.Format(
                "{0}: UploadFileAsync: interpreter = {1}, " +
                "arguments = {2}, callbackFlags = {3}, uri = {4}, " +
                "method = {5}, fileName = {6}{7}",
                GlobalState.GetCurrentThreadId(),
                FormatOps.WrapOrNull(interpreter),
                FormatOps.WrapOrNull(true, true, arguments),
                FormatOps.WrapOrNull(callbackFlags),
                FormatOps.WrapOrNull(uri),
                FormatOps.WrapOrNull(method),
                FormatOps.WrapOrNull(fileName),
                Environment.NewLine), typeof(SocketOps).Name);

            ReturnCode code = ReturnCode.Ok;
            WebClient webClient = null;

            try
            {
                ICallback callback = CommandCallback.Create(
                    callbackFlags, ObjectFlags.Callback, interpreter, null,
                    arguments, ref error);

                if (callback != null)
                {
                    try
                    {
                        webClient = CreateWebClient(
                            interpreter, "UploadFileAsync");

                        if (webClient != null)
                        {
                            callback.ClientData = new ClientData(
                                new AnyTriplet<WebClient, Uri,
                                    IAnyPair<string, string>>(
                                        webClient, uri, new AnyPair<string,
                                            string>(method, fileName)));

                            webClient.UploadFileCompleted +=
                                new UploadFileCompletedEventHandler(
                                    UploadFileAsyncCompleted);

                            /* NO RESULT */
                            webClient.UploadFileAsync(
                                uri, method, fileName, callback);
                        }
                        else
                        {
                            error = "could not create web client";
                            code = ReturnCode.Error;
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                        code = ReturnCode.Error;
                    }
                }
                else
                {
                    code = ReturnCode.Error;
                }
            }
            finally
            {
                if ((code != ReturnCode.Ok) && (webClient != null))
                {
                    ReturnCode disposeCode;
                    Result disposeError = null;

                    disposeCode = ObjectOps.TryDispose(
                        webClient, ref disposeError);

                    if (disposeCode != ReturnCode.Ok)
                        DebugOps.Complain(interpreter, disposeCode, disposeError);
                }
            }

            return code;
        }
        #endregion
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Public Diagnostic Methods
        public static ReturnCode Ping(
            string host,
            int timeout,
            ref IPStatus status,
            ref long roundtripTime,
            ref Result error
            )
        {
            try
            {
                using (Ping ping = new Ping())
                {
                    PingReply reply = ping.Send(host, timeout);

                    //
                    // NOTE: Populate reply information for the caller.
                    //
                    status = reply.Status;
                    roundtripTime = reply.RoundtripTime;
                }

                return ReturnCode.Ok;
            }
            catch (Exception e)
            {
                //
                // NOTE: Populate error information for the caller.
                //
                error = e;
            }

            return ReturnCode.Error;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Public Network Address Methods
        public static IPAddress GetIpAddress(
            string host,
            bool strict,
            ref Result error
            )
        {
            IPAddress result = null;

            if (!String.IsNullOrEmpty(host))
            {
                if (!IPAddress.TryParse(host, out result))
                {
                    try
                    {
                        //
                        // NOTE: Attempt to resolve the host name to one or
                        //       more IP addresses. This is required even for
                        //       things like "localhost", etc.
                        //
                        IPAddress[] addresses = Dns.GetHostAddresses(host);

                        if (addresses != null)
                        {
                            //
                            // NOTE: We require an IPv4 address.
                            //
                            for (int index = 0; index < addresses.Length; index++)
                            {
                                if (addresses[index].AddressFamily == AddressFamily.InterNetwork)
                                {
                                    result = addresses[index];
                                    break;
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        error = e;
                    }
                }
            }
            else
            {
                if (strict)
                    error = "invalid host name or address";
                else
                    result = IPAddress.Any;
            }

            return result;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static int GetPortNumber(
            string port,
            CultureInfo cultureInfo,
            bool strict,
            ref Result error
            )
        {
            int result = Port.Invalid;

            if (!String.IsNullOrEmpty(port))
            {
                if (Value.GetInteger2(port, ValueFlags.AnyInteger, cultureInfo,
                        ref result, ref error) == ReturnCode.Ok)
                {
                    return result;
                }

                //
                // FIXME: Else we should try to lookup the service name using
                //        getservbyname() API; however, the .NET Framework does
                //        not expose this functionality.  A possible workaround
                //        would be to see if the necessary DLL is already loaded
                //        (e.g. WS2_32.DLL) and simply P/Invoke the function
                //        ourselves.
                //
            }
            else if (!strict)
            {
                result = Port.Automatic;
            }

            return result;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Public Network Client Methods
        public static TcpClient NewTcpClient(
            string localAddress,
            string localPort,
            CultureInfo cultureInfo,
            ref Result error
            )
        {
            IPAddress address = GetIpAddress(localAddress, false, ref error);

            if (address != null)
            {
                int port = GetPortNumber(
                    localPort, cultureInfo, false, ref error);

                if (port != Port.Invalid)
                    return new TcpClient(new IPEndPoint(address, (int)port));
            }

            return null;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static ReturnCode Connect(
            TcpClient client,
            string remoteHost,
            string remotePort,
            CultureInfo cultureInfo,
            ref Result error
            )
        {
            IPAddress address = GetIpAddress(remoteHost, true, ref error);

            if (address != null)
            {
                int port = GetPortNumber(
                    remotePort, cultureInfo, true, ref error);

                if (port != Port.Invalid)
                {
                    try
                    {
                        client.Connect(new IPEndPoint(address, (int)port));

                        return ReturnCode.Ok;
                    }
                    catch (Exception e)
                    {
                        error = e;
                    }
                }
            }

            return ReturnCode.Error;
        }
        #endregion

        ///////////////////////////////////////////////////////////////////////////////////////////////

        #region Public Network Server Methods
        public static ReturnCode GetServerScript(
            TcpClient client,
            string channelId,
            string oldScript,
            ref string newScript,
            ref Result error
            )
        {
            try
            {
                if (client != null)
                {
                    Socket socket = client.Client;

                    if (socket != null)
                    {
                        IPEndPoint endPoint = socket.RemoteEndPoint as IPEndPoint;

                        if (endPoint != null)
                        {
                            StringBuilder builder = StringOps.NewStringBuilder(oldScript);

                            builder.Append(Characters.Space);
                            builder.Append(channelId);
                            builder.Append(Characters.Space);
                            builder.Append(endPoint.Address);
                            builder.Append(Characters.Space);
                            builder.Append(endPoint.Port);

                            newScript = builder.ToString();

                            return ReturnCode.Ok;
                        }
                        else
                        {
                            error = "invalid remote endpoint";
                        }
                    }
                    else
                    {
                        error = "invalid client socket";
                    }
                }
                else
                {
                    error = "invalid client";
                }
            }
            catch (Exception e)
            {
                error = e;
            }

            return ReturnCode.Error;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsListenerActive(
            TcpListener listener
            )
        {
            try
            {
                #region Static Lock Held
                lock (syncRoot)
                {
                    //
                    // HACK: Why must we do this?  This member is marked as "protected";
                    //       however, we need to know this information and we do not want
                    //       to derive a custom class to get it; therefore, just use
                    //       Reflection.  We cache the PropertyInfo object so that we do
                    //       not need to look it up more than once.
                    //
                    if (tcpListenerActive == null)
                    {
                        tcpListenerActive = typeof(TcpListener).GetProperty(
                            "Active", BindingFlags.Instance | BindingFlags.NonPublic);
                    }

                    if ((tcpListenerActive != null) && (listener != null))
                        return (bool)tcpListenerActive.GetValue(listener, null);
                }
                #endregion
            }
            catch
            {
                // do nothing.
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static bool IsCleanedUp(
            Socket socket
            )
        {
            try
            {
                #region Static Lock Held
                lock (syncRoot)
                {
                    //
                    // HACK: Why must we do this?  This member is marked as "protected";
                    //       however, we need to know this information and we do not want
                    //       to derive a custom class to get it; therefore, just use
                    //       Reflection.  We cache the PropertyInfo object so that we do
                    //       not need to look it up more than once.
                    //
                    if (socketCleanedUp == null)
                    {
                        socketCleanedUp = typeof(Socket).GetProperty(
                            "CleanedUp", BindingFlags.Instance | BindingFlags.NonPublic);
                    }

                    if ((socketCleanedUp != null) && (socket != null))
                        return (bool)socketCleanedUp.GetValue(socket, null);
                }
                #endregion
            }
            catch
            {
                // do nothing.
            }

            return false;
        }

        ///////////////////////////////////////////////////////////////////////////////////////////////

        public static TcpListener NewTcpListener(
            string localAddress,
            string localPort,
            CultureInfo cultureInfo,
            ref Result error
            )
        {
            IPAddress address = null;

            if (localAddress != null)
                address = GetIpAddress(localAddress, true, ref error);

            if ((localAddress == null) || (address != null))
            {
                int port = GetPortNumber(
                    localPort, cultureInfo, true, ref error);

                if (port != Port.Invalid)
                {
                    return (address != null) ?
                        new TcpListener(address, port) : new TcpListener(port);
                }
            }

            return null;
        }
        #endregion
    }
}
