using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;

namespace ClosedXML
{
    /// <summary>
    ///   Help methods for work with files
    /// </summary>
    internal static class PathHelper
    {
        private static readonly Regex ms_checkValidAbsolutePathRegEx = new Regex(
                "^(([a-zA-Z]\\:)|\\\\)(\\\\|(\\\\[^\\n\\r\\t/:*?<>\\\"|]*)+)$",
                RegexOptions.IgnoreCase
                | RegexOptions.Singleline
                | RegexOptions.CultureInvariant
                | RegexOptions.Compiled
                );
        /// <summary>
        ///   Can check only .\dfdfd\dfdf\dfdf or ..\..\gfhfgh\fghfh
        /// </summary>
        private static readonly Regex ms_checkValidRelativePathRegEx = new Regex(
                "^(\\.\\.|\\.)(\\\\|(\\\\[^\\n\\r\\t/:*?<>\\\"|]*)+)$",
                RegexOptions.IgnoreCase
                | RegexOptions.Multiline
                | RegexOptions.CultureInvariant
                | RegexOptions.Compiled
                );

        private static readonly object ms_syncAbsolutePathObj = new object();

        /// <summary>
        ///   Gets data and time string stamp for file name
        /// </summary>
        /// <returns></returns>
        public static string GetTimeStamp()
        {
            return GetTimeStamp(DateTime.Now);
        }
        /// <summary>
        ///   Gets data and time string stamp for file name
        /// </summary>
        /// <returns></returns>
        public static string GetTimeStamp(DateTime dateTime)
        {
            return dateTime.ToString("ddMMMyyyy_HHmmss", DateTimeFormatInfo.InvariantInfo);
        }
        /// <summary>
        ///   Safety delete file(with try block)
        /// </summary>
        /// <param name = "fileName">file name</param>
        public static void SafetyDeleteFile(string fileName)
        {
            try
            {
                if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
                {
                    File.Delete(fileName);
                }
            }
            catch (Exception ex)
            {
                Debug.Fail("Error during file deleting. Error message:" + ex.Message);
            }
        }
        /// <summary>
        ///   Replace all not implemented symbols to '_'
        /// </summary>
        /// <param name = "fileName">input filename</param>
        /// <returns></returns>
        public static string NormalizeFileName(string fileName)
        {
            char[] invalidCharacters = Path.GetInvalidFileNameChars();
            #region Replace begin space
            string result = fileName.TrimStart(' ');
            if (result.Length < fileName.Length)
            {
                result = new string('_', fileName.Length - result.Length) + result;
            }
            #endregion
            foreach (char curChar in invalidCharacters)
            {
                result = result.Replace(curChar, '_');
            }
            return result;
        }

        public static string NormalizePathName(string fileName)
        {
            char[] invalidCharacters = Path.GetInvalidPathChars();
            string result = fileName.TrimStart(' ');
            foreach (char curChar in invalidCharacters)
            {
                result = result.Replace(curChar, '_');
            }
            return result;
        }
        /// <summary>
        ///   ValidatePath file or diretory path
        /// </summary>
        /// <param name = "path"></param>
        /// <param name = "type">path type</param>
        /// <returns></returns>
        public static bool ValidatePath(string path, PathTypes type)
        {
            bool _result = false;
            if ((type & PathTypes.Absolute) == PathTypes.Absolute)
            {
                _result |= ms_checkValidAbsolutePathRegEx.IsMatch(path);
            }
            if ((type & PathTypes.Relative) == PathTypes.Relative)
            {
                _result |= ms_checkValidRelativePathRegEx.IsMatch(path);
            }
            return _result;
        }

        /// <summary>
        ///   ValidatePath file or diretory path
        /// </summary>
        /// <param name = "fileName"></param>
        /// <returns></returns>
        public static bool ValidateFileName(string fileName)
        {
            return fileName.LastIndexOfAny(Path.GetInvalidFileNameChars()) < 0;
        }

        public static string EvaluateRelativePath(string mainDirPath, string absoluteFilePath)
        {
            string[] firstPathParts = mainDirPath.Trim(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);
            string[] secondPathParts = absoluteFilePath.Trim(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar);

            int sameCounter = 0;
            int partsCount = Math.Min(firstPathParts.Length, secondPathParts.Length);
            for (int i = 0; i < partsCount; i++)
            {
                if (String.Compare(firstPathParts[i], secondPathParts[i], true) != 0)
                {
                    break;
                }
                sameCounter++;
            }

            if (sameCounter == 0)
            {
                return absoluteFilePath;
            }

            string newPath = string.Empty;
            for (int i = sameCounter; i < firstPathParts.Length; i++)
            {
                if (i > sameCounter)
                {
                    newPath += Path.DirectorySeparatorChar;
                }
                newPath += "..";
            }
            if (newPath.Length == 0)
            {
                newPath = ".";
            }
            for (int i = sameCounter; i < secondPathParts.Length; i++)
            {
                newPath += Path.DirectorySeparatorChar;
                newPath += secondPathParts[i];
            }
            return newPath;
        }

        public static string EvaluateAbsolutePath(string rootPath, string relativePath)
        {
            lock (ms_syncAbsolutePathObj)
            {
                string _temp = Environment.CurrentDirectory;
                Environment.CurrentDirectory = rootPath;
                string _result = Path.GetFullPath(relativePath);
                Environment.CurrentDirectory = _temp;
                return _result;
            }
        }

        public static bool TryCreateFile(string filePath, out string message)
        {
            #region Check
            if (string.IsNullOrEmpty(filePath))
            {
                throw new ArgumentNullException("filePath");
            }
            #endregion
            try
            {
                if (File.Exists(filePath))
                {
                    message = null;
                    return true;
                }

                using (FileStream _stream = new FileStream(filePath,
                                                           FileMode.Create,
                                                           FileAccess.ReadWrite,
                                                           FileShare.Read,
                                                           1024,
                                                           FileOptions.DeleteOnClose))
                {
                }
            }
            catch (Exception exc)
            {
                message = exc.Message;
                return false;
            }

            message = null;
            return true;
        }

        public static string CreateDirectory(string path)
        {
            try
            {
                var info = new DirectoryInfo(path);
                if (!info.Exists)
                {
                    info.Create();
                }
            }
            catch
            {
            }
            return path;
        }

        public static void OpenFileFolder(string filePath)
        {
            string cmdLine = string.Format("/select,\"{0}\"", filePath);
            Process.Start(@"explorer.exe", cmdLine);
        }

        public static void OpenFile(string filePath)
        {
            Process.Start(filePath);
        }

        [Flags]
        public enum PathTypes
        {
            None = 0,
            Absolute = 1,
            Relative = 2
        }
    }
}