//  =====================================================
//  The path class is a static class for file system path
//  manipulation. The directory separator char is '/'
//  also on Windows because it is supported and makes
//  the implementation simpler.
//  =====================================================

using System;
using System.Collections.Generic;
using System.Text;

namespace System.IO
{
    public class InvalidPathException : Exception
    {
        public InvalidPathException(string message) : base(message)
        {
        }
    }

    [vmf=cwd]
    public extern string InternalGetCurrentWorkingDirectory();

    public string GetCurrentWorkingDirectory()
    {
        return Path.MakeCanonical(InternalGetCurrentWorkingDirectory());
    }

    public static class Path
    {
        public static string MakeCanonical(string path)
        {
            StringBuilder result = new StringBuilder();
            char prev = ' ';
            foreach (char c in path)
            {
                if (c == '\\')
                {
                    c = '/';
                }
                if (c == '/')
                {
                    if (prev != '/')
                    {
                        result.Append(c);
                    }
                }
                else
                {
                    result.Append(c);
                }
                prev = c;
            }
            string res = result.ToString();
            if (res.Length == 3 && char.IsCAlpha(res[0]) && res[1] == ':' && res[2] == '/')
            {
                return res;
            }
            if (res == "/")
            {
                return res;
            }
            if (!string.IsNullOrEmpty(res))
            {
                if (res[res.Length - 1] == '/')
                {
                    return res.Substring(0, res.Length - 1);
                }
            }
            return res;
        }
        public static string ChangeExtension(string path, string extension)
        {
            int lastDotPos = path.LastIndexOf('.');
            int lastSlashPos = path.LastIndexOf('/');
            if (lastSlashPos != -1 && lastDotPos < lastSlashPos)
            {
                lastDotPos = -1;
            }
            if (string.IsNullOrEmpty(extension))
            {
                if (lastDotPos != -1)
                {
                    return path.Substring(0, lastDotPos);
                }
                else
                {
                    return path;
                }
            }
            else
            {
                if (lastDotPos == -1)
                {
                    if (extension[0] == '.')
                    {
                        return path + extension;
                    }
                    else
                    {
                        return path + "." + extension;
                    }
                }
                else 
                {
                    if (extension[0] == '.')
                    {
                        return path.Substring(0, lastDotPos) + extension;
                    }
                    else
                    {
                        return path.Substring(0, lastDotPos + 1) + extension;
                    }
                }
            }
        }
        public static bool HasExtension(string path)
        {
            string p = MakeCanonical(path);
            int lastDotPos = p.LastIndexOf('.');
            if (lastDotPos != -1)
            {
                int lastColon = p.IndexOf(':', lastDotPos + 1);
                int lastDirSep = p.IndexOf('/', lastDotPos + 1);
                if (lastColon > lastDotPos || lastDirSep > lastDotPos)
                {
                    return false;
                }
                else if (lastDotPos < p.Length - 1)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        public static string GetExtension(string path)
        {
            string p = MakeCanonical(path);
            int lastDotPos = p.LastIndexOf('.');
            if (lastDotPos != -1)
            {
                if (p.IndexOf('/', lastDotPos + 1) != -1)
                {
                    return string.Empty;
                }
                else
                {
                    return p.Substring(lastDotPos);
                }
            }
            else
            {
                return string.Empty;
            }
        }
        public static string GetFileName(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return string.Empty;
            }
            else
            {
                string p = MakeCanonical(path);
                char lastChar = p[p.Length - 1];
                if (lastChar == '/' || lastChar == ':')
                {
                    return string.Empty;
                }
                else
                {
                    int lastDirSepPos = p.LastIndexOf('/');
                    if (lastDirSepPos != -1)
                    {
                        return p.Substring(lastDirSepPos + 1);
                    }
                    else
                    {
                        return p;
                    }
                }
            }
        }
        public static string GetFileNameWithoutExtension(string path)
        {
            string fileName = GetFileName(path);
            int lastDotPos = fileName.LastIndexOf('.');
            if (lastDotPos != -1)
            {
                return fileName.Substring(0, lastDotPos);
            }
            else
            {
                return fileName;
            }
        }
        public static string GetDirectoryName(string path)
        {
            string p = MakeCanonical(path);
            if (string.IsNullOrEmpty(p))
            {
                return string.Empty;
            }
            else if (p.Length == 3 && char.IsCAlpha(p[0]) && p[1] == ':' && p[2] == '/')
            {
                return string.Empty;
            }
            else
            {
                int lastDirSepPos = p.LastIndexOf('/');
                if (lastDirSepPos != -1)
                {
                    return p.Substring(0, lastDirSepPos);
                }
                else
                {
                    return string.Empty;
                }
            }
        }
        public static string Combine(string path1, string path2)
        {
            string p1 = MakeCanonical(path1);
            string p2 = MakeCanonical(path2);
            if (string.IsNullOrEmpty(p1))
            {
                return p2;
            }
            else if (string.IsNullOrEmpty(p2))
            {
                return p1;
            }
            else
            {
                if (IsAbsolute(p2))
                {
                    return p2;
                }
                else 
                {
                    StringBuilder result = new StringBuilder();
                    result.Append(p1);
                    if (p1[p1.Length - 1] != '/')
                    {
                        result.Append('/');
                    }
                    result.Append(p2);
                    return result.ToString();
                }
            }
        }
        public static bool IsAbsolute(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return false;
            }
            else
            {
                string p = MakeCanonical(path);
                if (p[0] == '/')
                {
                    return true;
                }
                else if (char.IsCAlpha(p[0]) && p[1] == ':' && p[2] == '/')
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        public static bool IsRelative(string path)
        {
            return !IsAbsolute(path);
        }
        public static string GetParent(string path)
        {
            string fullPath = GetFullPath(path);
            int lastSlashPos = fullPath.LastIndexOf('/');
            if (lastSlashPos == -1)
            {
                return string.Empty;
            }
            else
            {
                return fullPath.Substring(0, lastSlashPos);
            }
        }
    }

    public string GetFullPath(string path)
    {
        string cp = Path.MakeCanonical(path);
        StringBuilder p = new StringBuilder();
        p.Append(cp);
        if (Path.IsRelative(cp))
        {
            p.Clear();
            p.Append(GetCurrentWorkingDirectory());
            p.Append('/');
            p.Append(cp);
        }
        List<string> components = p.ToString().Split('/');
        int w = 0;
        int n = components.Count;
        for (int i = 0; i < n; ++i)
        {
            string c = components[i];
            if (i == 0 || (!string.IsNullOrEmpty(c) && c != "."))
            {
                if (c == "..")
                {
                    --w;
                    if (w < 0)
                    {
                        throw new InvalidPathException("path '" + path + "' is invalid");
                    }
                }
                else
                {
                    if (w != i)
                    {
                        components[w] = components[i];
                    }
                    ++w;
                }
            }
        }
        if (w == 0)
        {
            return "/";
        }
        else if (w == 1)
        {
            string p = components[0];
            if (p.Length == 2 && char.IsCAlpha(p[0]) && p[1] == ':')
            {
                return p + "/";
            }
        }
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < w; ++i)
        {
            if (i != 0)
            {
                result.Append('/');
            }
            result.Append(components[i]);
        }
        return result.ToString();
    }
}