using System.Net;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Net.Http;
using System;
using System.Linq;
using System.Web;
using Newtonsoft.Json;
using System.Net.Mime;
using HeyRed.Mime;

namespace Tesses.Http
{
    public class SubDirectoryVirtualStorage : VirtualStorage
    {
        public SubDirectoryVirtualStorage(VirtualStorage storage,string directory)
        {
            Storage=storage;
            Directory = directory;
        }
        public VirtualStorage Storage {get;set;}
        public string Directory {get;set;}
        public override void CreateDirectory(string path)
        {
            Storage.CreateDirectory(PathCombine(Directory,path));
        }

        public override void Delete(string file)
        {
            Storage.Delete(PathCombine(Directory,file));
        }

        public override bool DirectoryExists(string filename)
        {
            return Storage.DirectoryExists(PathCombine(Directory,filename));
        }

        public override IEnumerable<string> EnumerateDirectories(string dir)
        {
            return Storage.EnumerateDirectories(PathCombine(Directory,dir));
        }

        public override IEnumerable<string> EnumerateFiles(string dir)
        {
            return Storage.EnumerateFiles(PathCombine(Directory,dir));
        }

        public override bool FileExists(string filename)
        {
            return Storage.FileExists(PathCombine(Directory,filename));
        }

        public override Stream Open(string file, FileMode mode, FileAccess access, FileShare share)
        {
            return Storage.Open(PathCombine(Directory,file),mode,access,share);
        }
        public override void CopyDirectory(string src, string dest, bool allowOverwrite = false)
        {
            Storage.CopyDirectory(PathCombine(Directory,src),PathCombine(Directory,dest));
        }
        public override void CopyDirectoryTo(VirtualStorage storage, string src, string dest, bool allowOverwrite = false)
        {
            Storage.CopyDirectoryTo(storage,PathCombine(Directory,src),dest,allowOverwrite);
        }
        public override void CopyFile(string src, string dest)
        {
            Storage.CopyFile(PathCombine(Directory,src),PathCombine(Directory,dest));
        }
        public override void CopyFileTo(string filename, Stream strm)
        {
            Storage.CopyFileTo(PathCombine(Directory,filename),strm);
        }
        public override void DeleteDirectory(string dir, bool recursive = false)
        {
            Storage.DeleteDirectory(PathCombine(Directory,dir),recursive);
        }
        public override DateTime GetCreationTime(string filename)
        {
            return Storage.GetCreationTime(PathCombine(Directory,filename));
        }
        public override DateTime GetLastAccessTime(string filename)
        {
            return  Storage.GetCreationTime(PathCombine(Directory,filename));
        }
        public override DateTime GetLastWriteTime(string filename)
        {
            return  Storage.GetCreationTime(PathCombine(Directory,filename));
        }
        public override void MoveDirectory(string src, string dest, bool allowOverwrite = false)
        {
            Storage.MoveDirectory( PathCombine(Directory,src),  PathCombine(Directory,dest), allowOverwrite);
        }
        public override void MoveDirectoryTo(VirtualStorage storage, string src, string dest, bool allowOverwrite = false)
        {
            Storage.MoveDirectoryTo(storage, PathCombine(Directory,src),dest,allowOverwrite);
        }
        public override void MoveFile(string src, string dest)
        {
            Storage.MoveFile(PathCombine(Directory,src),PathCombine(Directory,dest));
        }
        public override void SetCreationTime(string filename, DateTime time)
        {
            Storage.SetCreationTime(PathCombine(Directory,filename),time);
        }
        public override void SetLastAccessTime(string filename, DateTime time)
        {
            Storage.SetLastAccessTime(PathCombine(Directory,filename),time);
        }
        public override void SetLastWriteTime(string filename, DateTime time)
        {
            Storage.SetLastWriteTime(PathCombine(Directory,filename),time);
        }
        public override string ActualPath => Path.Combine(Storage.ActualPath,Directory);
    }
    
    

    public class DirectoryVirtualStorage : VirtualStorage
    {
        public DirectoryVirtualStorage(string working)
        {
            WorkingDirectory = working;
        }
        public DirectoryVirtualStorage()
        {
            WorkingDirectory=Environment.CurrentDirectory;
        }
        
        public override string ActualPath => WorkingDirectory;
        public override DateTime GetCreationTime(string filename)
        {
            if(FileExists(filename))
            {
                return File.GetCreationTime(Path.Combine(WorkingDirectory,filename));
            }
            else if(DirectoryExists(filename))
            {
                return Directory.GetCreationTime(Path.Combine(WorkingDirectory,filename));
            }
            else{
                return DateTime.Now;
            }

        }
        public override DateTime GetLastWriteTime(string filename)
        {
            if(FileExists(filename))
            {
                return File.GetLastWriteTime(Path.Combine(WorkingDirectory,filename));
            }
            else if(DirectoryExists(filename))
            {
                return Directory.GetLastWriteTime(Path.Combine(WorkingDirectory,filename));
            }
            else{
                return DateTime.Now;
            }

        }
        public override DateTime GetLastAccessTime(string filename)
        {
            if(FileExists(filename))
            {
                return File.GetLastAccessTime(Path.Combine(WorkingDirectory,filename));
            }
            else if(DirectoryExists(filename))
            {
                return Directory.GetLastAccessTime(Path.Combine(WorkingDirectory,filename));
            }
            else{
                return DateTime.Now;
            }

        }
        public override void SetCreationTime(string filename, DateTime time)
        {
            if(FileExists(filename))
            {
                File.SetCreationTime(Path.Combine(WorkingDirectory,filename),time);
            }
            else if(DirectoryExists(filename))
            {
                Directory.SetCreationTime(Path.Combine(WorkingDirectory,filename),time);
            }
        }
        public override void SetLastWriteTime(string filename, DateTime time)
        {
            if(FileExists(filename))
            {
                File.SetLastWriteTime(Path.Combine(WorkingDirectory,filename),time);
            }
            else if(DirectoryExists(filename))
            {
                Directory.SetLastWriteTime(Path.Combine(WorkingDirectory,filename),time);
            }
        }
        public override void SetLastAccessTime(string filename, DateTime time)
        {
            if(FileExists(filename))
            {
                File.SetLastAccessTime(Path.Combine(WorkingDirectory,filename),time);
            }
            else if(DirectoryExists(filename))
            {
                Directory.SetLastAccessTime(Path.Combine(WorkingDirectory,filename),time);
            }
        }
        public override void DeleteDirectory(string dir, bool recursive = false)
        {
            Directory.Delete(Path.Combine(WorkingDirectory,dir),recursive);
        }
        public override void MoveDirectory(string src, string dest, bool allowOverwrite = false)
        {
            Directory.Move(Path.Combine(WorkingDirectory,src),Path.Combine(WorkingDirectory,dest));
        }
        public override void MoveFile(string src, string dest)
        {
            File.Move(Path.Combine(WorkingDirectory,src),Path.Combine(WorkingDirectory,dest));
        }
        public string WorkingDirectory {get;set;}

        public override void CreateDirectory(string path)
        {
            Directory.CreateDirectory(Path.Combine(WorkingDirectory,path));
        }
        public override void CopyFile(string src, string dest)
        {
            File.Copy(Path.Combine(WorkingDirectory,src),Path.Combine(WorkingDirectory,dest));
       
        }
        public override void Delete(string file)
        {
            Delete(Path.Combine(WorkingDirectory,file));
        }

        public override bool DirectoryExists(string filename)
        {
            return Directory.Exists(Path.Combine(WorkingDirectory,filename));
        }

        public override IEnumerable<string> EnumerateDirectories(string dir)
        {
            foreach(var directory in Directory.EnumerateDirectories(Path.Combine(WorkingDirectory,dir)))
            {
                yield return Path.GetFileName(directory);
            }
        }

        public override IEnumerable<string> EnumerateFiles(string dir)
        {
            foreach(var file in Directory.EnumerateFiles(Path.Combine(WorkingDirectory,dir)))
            {
                yield return Path.GetFileName(file);
            }
        }

        public override bool FileExists(string filename)
        {
            return File.Exists(Path.Combine(WorkingDirectory,filename));
        }

        public override Stream Open(string file, FileMode mode, FileAccess access, FileShare share)
        {
            return File.Open(file,mode,access,share);
        }
    }
    public abstract class VirtualStorage
    {
        public string GetActualPath(string path)
        {
            if(ActualPath.EndsWith("/"))
            {
                return $"{ActualPath}{path.TrimStart('/')}";
            }else{
                 return $"{ActualPath}/{path.TrimStart('/')}";
            }
        }
        public virtual string ActualPath {get{return "vfs:///";}}
        public void CopyFileTo(VirtualStorage storage,string src,string dest)
        {

            using(var strm=storage.Open(dest,FileMode.Create,FileAccess.Write,FileShare.None))
            {
                CopyFileTo(src,strm);
                storage.SetCreationTime(dest,DateTime.Now);
                storage.SetLastWriteTime(dest,GetLastWriteTime(src));
                storage.SetLastAccessTime(dest,DateTime.Now);
            }
        }
        public virtual void CopyFile(string src,string dest)
        {
            CopyFileTo(this,src,dest);
        }
        public virtual void MoveFile(string src,string dest)
        {
            CopyFileTo(this,src,dest);
            Delete(src);
        }
        public abstract void CreateDirectory(string path);
      
        public virtual void CopyDirectory(string src,string dest,bool allowOverwrite=false)
        {
            DateTime lastWrite=GetLastWriteTime(src);
            
            CreateDirectory(dest);
            foreach(var item in EnumerateDirectories(src))
            {
                CopyDirectory(PathCombine(src,item),PathCombine(dest,item),allowOverwrite);
            }
            foreach(var file in EnumerateFiles(src))
            {
                if(FileExists(PathCombine(src,file)) && !allowOverwrite)
                {
                    continue;
                }
                CopyFile(PathCombine(src,file),PathCombine(dest,file));
            }
            SetCreationTime(dest,DateTime.Now);
            SetLastWriteTime(dest,lastWrite);
            SetLastAccessTime(dest,DateTime.Now);
        }
        public virtual void CopyDirectoryTo(VirtualStorage storage,string src,string dest,bool allowOverwrite=false)
        {
            DateTime lastWrite=GetLastWriteTime(src);
            
            storage.CreateDirectory(dest);
            foreach(var item in EnumerateDirectories(src))
            {
                CopyDirectoryTo(storage,PathCombine(src,item),PathCombine(dest,item),allowOverwrite);
            }
            foreach(var file in EnumerateFiles(src))
            {
                if(storage.FileExists(PathCombine(src,file)) && !allowOverwrite)
                {
                    continue;
                }
                CopyFileTo(storage,PathCombine(src,file),PathCombine(dest,file));
            }
            storage.SetCreationTime(dest,DateTime.Now);
            storage.SetLastWriteTime(dest,lastWrite);
            storage.SetLastAccessTime(dest,DateTime.Now);
        }
        public virtual void MoveDirectory(string src,string dest,bool allowOverwrite=false)
        {
            DateTime lastWrite=GetLastWriteTime(src);
            
             CreateDirectory(dest);
            foreach(var item in EnumerateDirectories(src))
            {
                MoveDirectory(PathCombine(src,item),PathCombine(dest,item));
            }
            foreach(var file in EnumerateFiles(src))
            {
                if(FileExists(PathCombine(src,file)) && !allowOverwrite)
                {
                    continue;
                }
                MoveFile(PathCombine(src,file),PathCombine(dest,file));
            }
            SetCreationTime(dest,GetCreationTime(src));
            SetLastWriteTime(dest,lastWrite);
            SetLastAccessTime(dest,DateTime.Now);
            try{
                if(EnumerateDirectories(src).Count() > 0 || EnumerateFiles(src).Count() > 0)
                {
                    return;
                }
                DeleteDirectory(src);
            }catch(Exception ex)
            {
                _=ex;
            }
        }
          public virtual void MoveDirectoryTo(VirtualStorage storage,string src,string dest,bool allowOverwrite=false)
        {
            DateTime lastWrite=GetLastWriteTime(src);
             storage.CreateDirectory(dest);
            foreach(var item in EnumerateDirectories(src))
            {
                MoveDirectoryTo(storage,PathCombine(src,item),PathCombine(dest,item));
            }
            foreach(var file in EnumerateFiles(src))
            {
                if(FileExists(PathCombine(src,file)) && !allowOverwrite)
                {
                    continue;
                }
                CopyFileTo(storage,PathCombine(src,file),PathCombine(dest,file));
                 storage.SetCreationTime(PathCombine(dest,file),GetCreationTime(PathCombine(src,file)));
                storage.SetLastWriteTime(PathCombine(dest,file),GetLastWriteTime(PathCombine(src,file)));
                storage.SetLastAccessTime(PathCombine(dest,file),DateTime.Now);
                Delete(PathCombine(src,file));
            }
            storage.SetCreationTime(dest,GetCreationTime(src));
            storage.SetLastWriteTime(dest,lastWrite);
            storage.SetLastAccessTime(dest,DateTime.Now);
            try{
                if(EnumerateDirectories(src).Count() > 0 || EnumerateFiles(src).Count() > 0)
                {
                    return;
                }
                DeleteDirectory(src);
            }catch(Exception ex)
            {
                _=ex;
            }
        }


        private void WARNING(string txt)
        {
            var col=Console.ForegroundColor;
            Console.ForegroundColor=ConsoleColor.Yellow;
            Console.WriteLine($"WARNING: {txt}");
            Console.ForegroundColor=col;
        }
        public virtual DateTime GetCreationTime(string filename)
        {
            WARNING("GetCreationTime Not Implemented");
            return DateTime.Now;
        }
        public virtual DateTime GetLastAccessTime(string filename)
        {
            
            WARNING("GetLastAccessTime Not Implemented");
            return DateTime.Now;
        }
        public virtual DateTime GetLastWriteTime(string filename)
        {
            
            WARNING("GetLastWriteTime Not Implemented");
            return DateTime.Now;
        }
        public virtual void SetCreationTime(string filename,DateTime time)
        {
             WARNING("SetCreationTime Not Implemented");
           
        }
        public virtual void SetLastWriteTime(string filename,DateTime time)
        {
             WARNING("SetLastWriteTime Not Implemented");
           
        }
        public virtual void SetLastAccessTime(string filename,DateTime time)
        {
             WARNING("SetLastAccessTime Not Implemented");
           
        }

        public abstract bool FileExists(string filename);
        public abstract bool DirectoryExists(string filename);
        public virtual void CopyFileTo(string filename,Stream strm)
        {
            using(var strmIn = Open(filename,FileMode.Open,FileAccess.Read,FileShare.Read))
            {
                strmIn.CopyTo(strm);
            }   
        }
        public abstract void Delete(string file);

        protected virtual void DeleteEmptyDirectory(string dir)
        {

        }
        public string PathCombine(string p1,string p2)
        {
            return p1.TrimEnd('/') + '/' + p2.TrimStart('/');
        }
        public string PathCombine(IEnumerable<string> args)
        {
            string s="";

            var enumerator = args.GetEnumerator();
            if(!enumerator.MoveNext())
            {
                return "";
            }
            s=enumerator.Current.ToString();
            while(enumerator.MoveNext())
            {
                s=PathCombine(s,enumerator.Current);
            }
            return s;
        }
        public string PathCombine(params string[] path)
        {
            return PathCombine((IEnumerable<string>)path);
        }
        public abstract IEnumerable<string> EnumerateFiles(string dir);
       
        public abstract IEnumerable<string> EnumerateDirectories(string dir);
        public virtual void DeleteDirectory(string dir,bool recursive=false)
        {
            if(recursive)
            {
                foreach(var dirname in EnumerateDirectories(dir))
                {
                    DeleteDirectory(PathCombine(dir,dirname),true);
                }
                foreach(var file in EnumerateFiles(dir))
                {
                    Delete(PathCombine(dir,file));
                }
            }
            DeleteEmptyDirectory(dir);
            
        }
        



        public abstract Stream Open(string file,FileMode mode,FileAccess access,FileShare share);
    }
}