/*
 * CommonOps.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.Text.RegularExpressions;
using Microsoft.Win32;
using Eagle._Attributes;
using Eagle._Containers.Private;

namespace Eagle._Components.Private
{
    [ObjectId("c385e1b9-95b0-4cd5-b0d9-a5fe582d7162")]
    internal static class CommonOps
    {
        #region Runtime Detection Support Class
        [ObjectId("e9622641-301b-4208-a5cc-3801edf4854e")]
        internal static class Runtime
        {
            #region Private Constants
            private static readonly string MonoRuntimeType = "Mono.Runtime";
            private static readonly string MonoDisplayNameMember = "GetDisplayName";

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

            //
            // NOTE: Create a list of regular expression patterns to check the
            //       Mono runtime version against.
            //
            private static readonly RegExList MonoVersionRegExList =
                new RegExList(new Regex[] {
                new Regex(" (\\d+(?:\\.\\d+)+)$", /* NOTE: Pre-2.6.0? */
                    RegexOptions.IgnoreCase | RegexOptions.CultureInvariant),
                new Regex("^(\\d+(?:\\.\\d+)+) ", /* NOTE: Post-2.6.0? */
                    RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)
            });

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

            private static readonly string MonoRuntimeName = "Mono";
            private static readonly string MicrosoftRuntimeName = "Microsoft.NET";

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

            private static readonly object syncRoot = new object();

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

            private static bool? isMono = null;
            private static string frameworkExtraVersion = null;

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

            private static Version FrameworkVersion = null;

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

            private static readonly string FrameworkSetup20KeyName =
                "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727";

            private static readonly string FrameworkSetup20ValueName = "Increment";

            private static readonly string FrameworkSetup40KeyName =
                "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full";

            private static readonly string FrameworkSetup40ValueName = "Release";

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

            private static readonly int FrameworkSetup45Value = 378389; // >= indicates 4.5
            private static readonly int FrameworkSetup451Value = 378758; // >= indicates 4.5.1
            #endregion

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

            #region Runtime Detection Methods
            public static bool IsMono()
            {
                try
                {
                    lock (syncRoot)
                    {
                        if (isMono == null)
                            isMono = (Type.GetType(MonoRuntimeType) != null);

                        return (bool)isMono;
                    }
                }
                catch
                {
                    // do nothing.
                }

                return false;
            }

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

            private static Version GetMonoVersion()
            {
                try
                {
                    Type type = Type.GetType(MonoRuntimeType);

                    if (type != null)
                    {
                        string displayName = type.InvokeMember(
                            MonoDisplayNameMember,
                            MarshalOps.PrivateStaticMethodBindingFlags,
                            null, null, null) as string;

                        if (!String.IsNullOrEmpty(displayName))
                        {
                            if (MonoVersionRegExList != null)
                            {
                                foreach (Regex regEx in MonoVersionRegExList)
                                {
                                    Match match = regEx.Match(displayName);

                                    if ((match != null) && match.Success)
                                        return new Version(match.Value);
                                }
                            }
                        }
                    }
                }
                catch
                {
                    // do nothing.
                }

                return null;
            }
            #endregion

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

            #region Runtime Information Methods
            public static string GetRuntimeName()
            {
                return IsMono() ? MonoRuntimeName : MicrosoftRuntimeName;
            }

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

            public static Version GetRuntimeVersion()
            {
                //
                // HACK: Currently, the runtime version is the same as
                //       the framework version when not running on Mono.
                //
                return IsMono() ? GetMonoVersion() : GetFrameworkVersion();
            }

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

            public static string GetRuntimeExtraVersion()
            {
                //
                // HACK: Currently, the runtime version is the same as
                //       the framework version when not running on Mono.
                //
                return IsMono() ? null : GetFrameworkExtraVersion();
            }

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

            public static string GetRuntimeNameAndVersion()
            {
                return FormatOps.NameAndVersion(
                    GetRuntimeName(), GetRuntimeVersion(),
                    GetRuntimeExtraVersion());
            }
            #endregion

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

            #region Framework Information Methods
            public static Version GetFrameworkVersion()
            {
                lock (syncRoot)
                {
                    if (FrameworkVersion == null)
                        FrameworkVersion = System.Environment.Version;

                    return FrameworkVersion;
                }
            }

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

            public static string GetFrameworkExtraVersion()
            {
                try
                {
                    lock (syncRoot)
                    {
                        if (frameworkExtraVersion == null)
                        {
                            object value = null;

                            if (IsFramework40())
                            {
                                value = Registry.GetValue(
                                    FrameworkSetup40KeyName,
                                    FrameworkSetup40ValueName, null);
                            }
                            else if (IsFramework20())
                            {
                                value = Registry.GetValue(
                                    FrameworkSetup20KeyName,
                                    FrameworkSetup20ValueName, null);
                            }

                            //
                            // NOTE: The value may still be null at this point
                            //       and that means this code may be executed
                            //       again the next time this method is called
                            //       (i.e. we have no way of caching the null
                            //       value).
                            //
                            frameworkExtraVersion = (value != null) ?
                                value.ToString() : null;
                        }

                        return frameworkExtraVersion;
                    }
                }
                catch
                {
                    // do nothing.
                }

                return null;
            }
            #endregion

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

            #region Framework Detection Methods
            //
            // NOTE: Be sure to use !IsMono also.
            //
            public static bool IsFramework20()
            {
                Version version = GetFrameworkVersion();

                return (version != null) && (version.Major == 2);
            }

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

            //
            // NOTE: Be sure to use !IsMono also.
            //
            public static bool IsFramework40()
            {
                Version version = GetFrameworkVersion();

                return (version != null) && (version.Major == 4);
            }

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

            //
            // NOTE: Be sure to use !IsMono also.
            //
            public static bool IsFramework45()
            {
                Version version = GetFrameworkVersion();

                if ((version == null) || (version.Major != 4))
                    return false;

                int extraValue;

                if (!int.TryParse(GetFrameworkExtraVersion(), out extraValue))
                    return false;

                return (extraValue >= FrameworkSetup45Value);
            }

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

            //
            // NOTE: Be sure to use !IsMono also.
            //
            public static bool IsFramework451()
            {
                Version version = GetFrameworkVersion();

                if ((version == null) || (version.Major != 4))
                    return false;

                int extraValue;

                if (!int.TryParse(GetFrameworkExtraVersion(), out extraValue))
                    return false;

                return (extraValue >= FrameworkSetup451Value);
            }
            #endregion
        }
        #endregion

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

        #region Environment Variable Support Class
        [ObjectId("24328505-60ed-4a79-89dd-41014d024f6d")]
        internal static class Environment
        {
            public static string GetVariable(
                string variable
                )
            {
                return System.Environment.GetEnvironmentVariable(variable);
            }

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

            public static void SetVariable(
                string variable,
                string value
                )
            {
                System.Environment.SetEnvironmentVariable(variable, value);
            }

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

            public static string ExpandVariables(
                string name
                )
            {
                return System.Environment.ExpandEnvironmentVariables(name);
            }

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

            public static bool DoesVariableExist(
                string name
                )
            {
                string value = null;

                return DoesVariableExist(name, ref value);
            }

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

            public static bool DoesVariableExist(
                string name,
                ref string value
                )
            {
                value = GetVariable(name);
                return value != null;
            }
        }
        #endregion

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

        #region Hash Code Support Class
        [ObjectId("b1bf9f59-a8a5-45b3-b54b-02c3d7107b85")]
        internal static class HashCodes
        {
            public static int Combine(
                int X,
                int Y
                )
            {
                byte[] bytes = new byte[sizeof(int) * 2];

                Array.Copy(BitConverter.GetBytes(X),
                    0, bytes, 0, sizeof(int));

                Array.Copy(BitConverter.GetBytes(Y),
                    0, bytes, sizeof(int), sizeof(int));

                return ConversionOps.ToInt(MathOps.HashFnv1UInt(bytes, true));
            }

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

            public static int Combine(
                int X,
                int Y,
                int Z
                )
            {
                byte[] bytes = new byte[sizeof(int) * 3];

                Array.Copy(BitConverter.GetBytes(X),
                    0, bytes, 0, sizeof(int));

                Array.Copy(BitConverter.GetBytes(Y),
                    0, bytes, sizeof(int), sizeof(int));

                Array.Copy(BitConverter.GetBytes(Z),
                    0, bytes, sizeof(int) * 2, sizeof(int));

                return ConversionOps.ToInt(MathOps.HashFnv1UInt(bytes, true));
            }
        }
        #endregion

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

        #region Bi-directional Looping Support Methods
        public static bool ForCondition(
            bool increment,
            int index,
            int lowerBound,
            int upperBound
            )
        {
            if (increment)
                return (index <= upperBound);
            else
                return (index >= lowerBound);
        }

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

        public static void ForLoop(
            bool increment,
            ref int index
            )
        {
            if (increment)
                index++;
            else
                index--;
        }
        #endregion
    }
}
