// Created with MonoDevelop
//
//    Copyright (C) 2018 Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;


namespace Hiperscan.Common
{
    public static class Misc
    {
        public static string MonoVersion
        {
            get
            {
                try
                {
                    string version = string.Empty;

                    Type type = Type.GetType("Mono.Runtime");
                    if (type != null)
                    {
                        MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
                        if (displayName != null)
                            version = displayName.Invoke(null, null).ToString();
                    }

                    return version;
                }
                catch(Exception)
                {
                    return string.Empty;
                }
            }
        }

        public static bool IsMonoRuntime
        {
            get
            {
                return Type.GetType("Mono.Runtime") != null;
            }
        }

        public static string AssemblyVersion
        {
            get { return Assembly.GetEntryAssembly().GetName().Version.ToString(); }
        }

        public static double ParseDoubleFromEnv(string variable_name, double default_val)
        {
            string variable_value = Environment.GetEnvironmentVariable(variable_name);
            if (string.IsNullOrEmpty(variable_value))
                return default_val;

            double parsed_val = double.Parse(variable_value, System.Globalization.CultureInfo.InvariantCulture);
            Console.Error.WriteLine("Using Environment variable {0}={1} to set internal double parameter.", variable_name, parsed_val);

            return parsed_val;
        }

        public static long GetEpochSeconds(DateTime date_time)
        {
            return (long)Math.Round((date_time.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalSeconds);
        }

        public static DateTime GetDateTime(long epoch_seconds)
        {
            return new DateTime(1970, 1, 1).AddSeconds(epoch_seconds).ToLocalTime();
        }

        public static IEnumerable<string> MaskPasswords(IEnumerable<string> args)
        {
            string password = args.SkipWhile(a => Regex.IsMatch(a, @"^-.*((pass)|(password)|(passwd)|(key))$") == false).ElementAtOrDefault(1);

            if (password != null)
                return args.Select(a => a.Replace(password, "*****"));

            return args.Select(a => a.Contains("--pass") ? Regex.Replace(a, @"=[^ ].*", @"=*****") : a);
        }

        public static string MaskPasswords(string args)
        {
            return string.Join(" ", Misc.MaskPasswords(args.Split(' ')));
        }

        public static List<string> GetLoadedAssemblies(string search_pattern = null)
        {
            List<string> loaded_assemblies = new List<string>();

            try
            {
                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

                foreach (Assembly assembly in assemblies)
                {
                    try
                    {
                        if (String.IsNullOrEmpty(search_pattern) == false)
                        {
                            
                            if (Regex.Matches(assembly.GetName().Name, search_pattern, RegexOptions.IgnoreCase).Count == 0)
                                continue;
                        }

                        string assembly_entry = String.Format("Assembly-FullName: {0}\nAssembly-Location: {1}",
                                                              assembly.GetName().FullName,
                                                              assembly.Location);
                        loaded_assemblies.Add(assembly_entry);                           

                    }
                    catch(Exception ex)
                    {
                        Console.WriteLine("Cannot read assembly: {0}", ex);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Cannot read assemblies from domain: {0}", ex);
            }

            return loaded_assemblies;
       
        }

        public static void ListEnvironmentVariables()
        {
            Console.WriteLine("Environment variables:");
            foreach (string variable in Environment.GetEnvironmentVariables().Keys)
            {
                string value = Environment.GetEnvironmentVariable(variable);
                Console.WriteLine("\t{0} = {1}", variable, value);
            }
        }

        public static string GetHexString(byte[] buf, int len=20)
        {
            StringBuilder sb = new StringBuilder();

            for (int ix = 0; ix < buf.Length; ++ix)
            {
                sb.AppendFormat(" 0x{0:X2}", buf[ix]);

                if (ix == len)
                {
                    sb.Append(" ...");
                    break;
                }
            }

            return sb.ToString();
        }

        public static void PrintEnvs()
        {
            Console.WriteLine("Overview of environment variables");
            List<string> envs = new List<string>();

            foreach (DictionaryEntry entry in Environment.GetEnvironmentVariables())
                envs.Add(entry.Key.ToString());

            envs.Sort();

            foreach (string name in envs)
                Console.WriteLine("  => {0} = {1}", name, Environment.GetEnvironmentVariable(name));
		}

        /// <summary>
        /// Gets the loaded assemblies
        /// without recursion mode
        /// </summary>
        /// <returns>The loaded assemblies.</returns>
        public static IEnumerable<Assembly> GetLoadedAssemblies()
        {
            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
            return assemblies;
        }

        public static Dictionary<Assembly, IEnumerable<Assembly>> GetLoadedAssembliesAndReferences()
        {
            Assembly[] main_assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Dictionary<Assembly, IEnumerable<Assembly>> all_assemblies = new Dictionary<Assembly, IEnumerable<Assembly>>();

            foreach (var item in main_assemblies)
            {
                all_assemblies.Add(item, Misc.GetReferencedAssemblies(item));
            }

            return all_assemblies;
        }

        public static IEnumerable<Assembly> GetReferencedAssemblies(Assembly assembly)
        {
            AssemblyName[] assembly_names = assembly.GetReferencedAssemblies();
            List<Assembly> assemblies = new List<Assembly>();

            foreach (AssemblyName assembly_name in assembly_names)
            {
                if (string.IsNullOrEmpty(assembly_name.Name))
                    continue;

                Assembly loaded_assembly = AppDomain.CurrentDomain.xGetAssemblyByName(assembly_name.Name);
                if (loaded_assembly != null)
                {
                    assemblies.Add(loaded_assembly);
                }
            }

            return assemblies;
        }

        public static Assembly xGetAssemblyByName(this AppDomain domain, string assemblyName)
        {
            return domain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assemblyName);
        }
    }
}
