/******************************************************************************/
/* SlunkCrypt, by LoRd_MuldeR <MuldeR2@GMX.de>                                */
/* This work has been released under the CC0 1.0 Universal license!           */
/******************************************************************************/

using System;
using System.IO;
using System.Text;

namespace com.muldersoft.slunkcrypt.gui.utils
{
    public static class PathUtils
    {
        private const char DOUBLE_QUOTE = '"', WILDCARD_ONE = '?', WILDCARD_ANY = '*';

        private static readonly Lazy<string> m_homeDirectory = new Lazy<string>(InitHomeDirectory);

        // =============================================================================
        // Public methods
        // =============================================================================

        public static bool IsInvalidPath(string filePath)
        {
            if (!string.IsNullOrEmpty(filePath))
            {
                if (filePath.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
                {
                    return true;
                }
                foreach (char c in filePath.Substring(HasDrivePrefix(filePath) ? 2 : 0))
                {
                    if (IsWildcardChar(c) || char.IsControl(c) || (c == Path.VolumeSeparatorChar))
                    {
                        return true;
                    }
                }
                if (IsDirectoryOrVolumeSeparatorChar(filePath[filePath.Length - 1]))
                {
                    return true;
                }
            }
            return false;
        }

        public static string CleanUpFilePathString(string filePath)
        {
            if (!string.IsNullOrEmpty(filePath))
            {
                filePath = NormalizePathSeparators(RemoveQuotesAndControlChars(filePath));
                if (filePath.Length > 0)
                {
                    try
                    {
                        if (!Path.IsPathRooted(filePath))
                        {
                            filePath = Path.Combine(m_homeDirectory.Value, filePath);
                        }
                    }
                    catch { }
                    try
                    {
                        filePath = Path.GetFullPath(filePath);
                    }
                    catch { }
                }
            }
            return filePath;
        }

        public static string TryGetDirectoryName(string filePath)
        {
            if (!string.IsNullOrEmpty(filePath))
            {
                try
                {
                    string directoryName = Path.GetDirectoryName(filePath);
                    if (!string.IsNullOrEmpty(directoryName))
                    {
                        return directoryName;
                    }
                }
                catch { }
            }
            return string.Empty;
        }

        public static bool TryCreateDirectory(string directoryPath)
        {
            try
            {
                Directory.CreateDirectory(directoryPath);
                return true;
            }
            catch { }
            return false;
        }

        public static void TryRemoveFile(string filePath)
        {
            try
            {
                File.Delete(filePath);
            }
            catch { }
        }

        public static string CreatePathSpec(params string[] entries)
        {
            StringBuilder pathBuilder = new StringBuilder();
            foreach (string pathEntry in entries)
            {
                AppendPathEntry(pathBuilder, pathEntry);
            }
            AppendPathEntry(pathBuilder, Environment.SystemDirectory);
            return pathBuilder.ToString();
        }

        // =============================================================================
        // Internal methods
        // =============================================================================

        private static string RemoveQuotesAndControlChars(string filePath)
        {
            if (filePath.Length > 0)
            {
                StringBuilder sb = new StringBuilder();
                foreach (char c in filePath)
                {
                    if ((!char.IsControl(c)) || (c != DOUBLE_QUOTE))
                    {
                        sb.Append(c);
                    }
                }
                filePath = sb.ToString();
            }
            return filePath;
        }

        private static string NormalizePathSeparators(string filePath)
        {
            if (filePath.Length > 0)
            {
                StringBuilder sb = new StringBuilder();
                char[] separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, Path.VolumeSeparatorChar };
                string pathPrefix;
                foreach (string token in SplitPathPrefix(out pathPrefix, filePath.Trim()).Split(separators, StringSplitOptions.RemoveEmptyEntries))
                {
                    string tokenTrimmed = token.Trim();
                    if (tokenTrimmed.Length > 0)
                    {
                        ((sb.Length > 0) ? sb.Append(Path.DirectorySeparatorChar) : sb.Append(pathPrefix)).Append(tokenTrimmed);
                    }
                }
                filePath = sb.ToString();
            }
            return filePath;
        }

        private static string SplitPathPrefix(out string prefix, string filePath)
        {
            if (HasDrivePrefix(filePath))
            {
                prefix = new string(new char[] { char.ToUpper(filePath[0]), Path.VolumeSeparatorChar, Path.DirectorySeparatorChar });
                return filePath.Substring(2);
            }
            if (HasUNCPrefix(filePath))
            {
                prefix = new string(new char[] { Path.DirectorySeparatorChar, Path.DirectorySeparatorChar });
                return filePath.Substring(2);
            }
            prefix = string.Empty;
            return filePath;
        }

        private static string InitHomeDirectory()
        {
            try
            {
                return Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            }
            catch
            {
                try
                {
                    return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
                }
                catch
                {
                    return AppDomain.CurrentDomain.BaseDirectory;
                }
            }
        }

        private static void AppendPathEntry(StringBuilder builder, string pathEntry)
        {
            if (!string.IsNullOrWhiteSpace(pathEntry))
            {
                if (builder.Length > 0)
                {
                    builder.Append(Path.PathSeparator);
                }
                if (pathEntry.IndexOf(Path.PathSeparator) == -1)
                {
                    builder.Append(pathEntry.Trim());
                }
                else
                {
                    builder.Append('"').Append(pathEntry.Trim()).Append('"');
                }
            }
        }

        private static bool HasDrivePrefix(string filePath)
        {
            return (filePath.Length > 1) && IsUsEnglishLetter(filePath[0]) && (filePath[1] == Path.VolumeSeparatorChar);
        }

        private static bool HasUNCPrefix(string filePath)
        {
            return (filePath.Length > 1) && IsDirectorySeparatorChar(filePath[0]) && IsDirectorySeparatorChar(filePath[1]);
        }

        private static bool IsDirectorySeparatorChar(char c)
        {
            return (c == Path.DirectorySeparatorChar) || (c == Path.AltDirectorySeparatorChar);
        }

        private static bool IsDirectoryOrVolumeSeparatorChar(char c)
        {
            return IsDirectorySeparatorChar(c) || (c == Path.VolumeSeparatorChar);
        }

        private static bool IsUsEnglishLetter(char c)
        {
            return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
        }

        private static bool IsWildcardChar(char c)
        {
            return ((c == WILDCARD_ONE) || (c == WILDCARD_ANY));
        }
    }
}
