//------------------------------------------------------------------------------
// <copyright file="Utility.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
//      Implements Utility class
// </summary>
//------------------------------------------------------------------------------

namespace Microsoft.Hpc.HpcBasicProfile
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.ServiceModel;
    using System.Security;
    using System.Globalization;

    /// <summary>
    /// Misc utility function
    /// </summary>
    internal class Utility
    {
        /// <summary>
        /// Prevent instance creation
        /// </summary>
        private Utility() 
        {
        }

        public const int CRED_MAX_USERNAME_LENGTH = (256 + 1 + 256);
        public const int CRED_MAX_PASSWORD_LENGTH = 256;

        public const string EnterUserName = "Enter UserName (Hit ENTER if {0}):";
        public const string EnterPassword = "Enter Password:";

        /// <summary>
        /// Prompts for username and password from console. Incomplete implementation but
        /// good enough for sample
        /// </summary>
        /// <param name="defaultUserName">Default username</param>
        /// <param name="userName">Returns username entered by user</param>
        /// <param name="password">Returns password entered by user</param>
        static public void PromptForUserCredentials(string defaultUserName, ref string userName, out string password)
        {
            if (userName == null)
            {
                Console.WriteLine(EnterUserName, defaultUserName);

                userName = Console.ReadLine();
            }

            if (userName == String.Empty)
            {
                userName = defaultUserName;
            }

            Console.WriteLine(EnterPassword);
            StringBuilder sb = new StringBuilder();

            while (true)
            {
                ConsoleKeyInfo keyInfo = Console.ReadKey(true);

                if (keyInfo.Key == ConsoleKey.Enter)
                {
                    break;
                }
                else if (keyInfo.Key == ConsoleKey.Backspace)
                {
                    sb.Length--;
                }
                else
                {
                    sb.Append(keyInfo.KeyChar);
                }
            }

            Console.WriteLine();

            password = sb.ToString();
        }

        internal static void PromptForCredentials(string target, ref string username, ref SecureString password, out bool fSave, bool fConsole, IntPtr hwndParent)
        {
            NativeMethods.CREDUI_FLAGS flags = NativeMethods.CREDUI_FLAGS.GENERIC_CREDENTIALS |
                                                     NativeMethods.CREDUI_FLAGS.EXCLUDE_CERTIFICATES |
                                                     NativeMethods.CREDUI_FLAGS.DO_NOT_PERSIST |
                                                     NativeMethods.CREDUI_FLAGS.ALWAYS_SHOW_UI;
            PromptForCredentials(target, ref username, ref password, out fSave, fConsole, hwndParent, flags);
        }

        internal static void PromptForCredentials(string target, ref string username, ref SecureString password, out bool fSave, bool fConsole, IntPtr hwndParent, NativeMethods.CREDUI_FLAGS flags)
        {
            NativeMethods.CREDUI_INFO info = new NativeMethods.CREDUI_INFO();
            int result;

            int saveCredentials;
            StringBuilder user = new StringBuilder(username, CRED_MAX_USERNAME_LENGTH);
            StringBuilder pwd = new StringBuilder(CRED_MAX_PASSWORD_LENGTH);
            saveCredentials = 0;
            info.cbSize = Marshal.SizeOf(info);
            info.hwndParent = hwndParent;

            if (fConsole)
            {
                result = NativeMethods.CredUICmdLinePromptForCredentials(target,
                                                           IntPtr.Zero,
                                                           0,
                                                           user,
                                                           CRED_MAX_USERNAME_LENGTH,
                                                           pwd,
                                                           CRED_MAX_PASSWORD_LENGTH,
                                                           ref saveCredentials,
                                                           flags);
            }
            else
            {
                result = NativeMethods.CredUIPromptForCredentials(ref info,
                                                    target,
                                                    IntPtr.Zero,
                                                    0,
                                                    user,
                                                    CRED_MAX_USERNAME_LENGTH,
                                                    pwd,
                                                    CRED_MAX_PASSWORD_LENGTH,
                                                    ref saveCredentials,
                                                    flags);
            }
            if (result != 0)
            {
                throw new System.ComponentModel.Win32Exception(result);
            }
            if (saveCredentials != 0)
            {
                fSave = true;
            }
            else
            {
                fSave = false;
            }
            username = user.ToString();
            password = new SecureString();
            for (int i = 0; i < pwd.Length; i++)
            {
                password.AppendChar(pwd[i]);
                pwd[i] = ' ';                   // overwrite the cleartext password
            }
            password.MakeReadOnly();
        }

        //
        // helper function to convert a SecureString password back to a cleartext string
        //
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        internal static string UnsecureString(SecureString secureString)
        {
            if (secureString == null)
            {
                return null;
            }
            string unsecureString;

            IntPtr ptr = Marshal.SecureStringToGlobalAllocUnicode(secureString);
            try
            {
                unsecureString = Marshal.PtrToStringUni(ptr);
            }
            finally
            {
                Marshal.ZeroFreeGlobalAllocUnicode(ptr);
            }
            return unsecureString;
        }


        /// <summary>
        /// Converts .Net object to an XmlElement
        /// </summary>
        /// <param name="o">.Net object to convert</param>
        /// <param name="elementName">Name to give root XML element</param>
        /// <param name="defaultNamespace">Namespace for the XML infoset</param>
        /// <returns>XmlElement containing Xml serialized object</returns>
        
        public static XmlElement ConvertToXmlElement(object o, string elementName, string defaultNamespace)
        {
            string xml = ConvertToXml(o, elementName, defaultNamespace);

            // Convert XML string to XmlElement
            XmlDocument document = new XmlDocument();
            document.LoadXml(xml);
            return document.DocumentElement;
        }
        
        /// <summary>
        /// Converts .Net object to an XML string
        /// </summary>
        /// <param name="o">.Net object to convert</param>
        /// <param name="elementName">Name to give root XML element</param>
        /// <param name="defaultNamespace">Namespace for the XML infoset</param>
        /// <returns>Object's XML serialization string</returns>
        public static string ConvertToXml(object o, string elementName, string defaultNamespace)
        {
            // Serialize .Net object to XML string
            StringBuilder sb = new StringBuilder();
            XmlSerializer serializer = new XmlSerializer(
                                                o.GetType(), 
                                                new XmlAttributeOverrides(), 
                                                new Type[] { },
                                                new XmlRootAttribute(elementName), 
                                                defaultNamespace);

            using (StringWriter writer = new StringWriter(sb, NumberFormatInfo.InvariantInfo))
            {
                serializer.Serialize(writer, o);
            }

            return sb.ToString();
        }

        /// <summary>
        /// Creates a BES activity ID based on specified CCS job and task IDs
        /// </summary>
        /// <param name="baseAddress">Base address for activity ID EPR</param>
        /// <param name="jobID">CCS Job ID</param>
        /// <param name="taskID">CCS Task ID</param>
        /// <param name="ns">namespace to use in activity ID reference parameters</param>
        /// <returns>Activity ID</returns>
        static public EndpointReferenceType CreateActivityID(string baseAddress, int jobID, int taskID, string ns)
        {
            EndpointReferenceType epr = new EndpointReferenceType();
            XmlDocument doc = new XmlDocument();
            XmlElement xmlElement = null;

            // Create EPR
            epr.Address = new AttributedURIType();
            epr.Address.Value = baseAddress;
            epr.ReferenceParameters = new ReferenceParametersType();
            epr.ReferenceParameters.Any = new XmlElement[2];

            // Add Job ID as reference parameter
            xmlElement = doc.CreateElement("JobID", ns);
            xmlElement.InnerText = jobID.ToString(NumberFormatInfo.InvariantInfo);
            epr.ReferenceParameters.Any[0] = xmlElement;

            // Add Task ID as reference parameter
            xmlElement = doc.CreateElement("TaskID", ns);
            xmlElement.InnerText = taskID.ToString(NumberFormatInfo.InvariantInfo);
            epr.ReferenceParameters.Any[1] = xmlElement;

            return epr;
        }

        /// <summary>
        /// Deserializes a JobDefinition object
        /// </summary>
        /// <param name="reader">XmlTextReader containing the XML to deserialize</param>
        /// <returns>JSDL job object</returns>
        public static JobDefinition_Type Deserialize(XmlTextReader reader)
        {
            XmlSerializer inputSerializer = new XmlSerializer(
                                                        typeof(JobDefinition_Type), 
                                                        new XmlAttributeOverrides(), 
                                                        new Type[] { },
                                                        new XmlRootAttribute("JobDefinition"), 
                                                        HPCBPClient.JSDLNamespaceURI);

            return (JobDefinition_Type) inputSerializer.Deserialize(reader);
        }
    }
}
