using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace VisimBox
{
    /// <summary>
    /// Type of the file entry (normal, archived file, archive)
    /// </summary>
    public enum FileEntryType { Normal = 0, Archived = 1, Archive = 2 };

    public class FileChangedEvent
    {
    }

    /// <summary>
    /// One file entry
    /// </summary>
    public class FileEntry
    {
        /// <summary>
        /// Filename with full path from current directory
        /// </summary>
        public string FileName;

        /// <summary>
        /// Type of the file
        /// </summary>
        public FileEntryType FileType;

        /// <summary>
        /// Null for normal files and archives, set only for archived files
        /// </summary>
        public FileEntry Archive;

        /// <summary>
        /// If true file is being watched for changes
        /// </summary>
        public bool WatchForChanges;

        public delegate void FileChangedHandler(object sender, FileChangedEvent e);
        //public event FileChangedHandler onFileChange;
    }


    /// <summary>
    /// Filesystem manager - allows loading / saving files from archives or disk with automatic patching
    /// </summary>    
    public class FileSystem : Singleton<FileSystem>, IDisposable
    {
        List<string> AllFiles = null;
        bool initialized = false;
        FileSystemWatcher fileWatcher = null;

        public string StartPath
        {
            get { return _StartPath; }
        }

        string _StartPath;

        public bool Initialize()
        {
            if (initialized)
                return true;
            initialized = true;
            AllFiles = new List<string>();
            RegisterAllFiles();

            fileWatcher = new FileSystemWatcher(".");
            fileWatcher.Changed += new FileSystemEventHandler(fw_Changed);

            _StartPath = System.IO.Directory.GetCurrentDirectory();

            return true;

            //Ionic.Zip.ZipFile z = Ionic.Zip.ZipFile.Read("bakalaka");
            //Ionic.Zip.ZipEntry ze = z["baka"];
            //ze.Extract(
        }

        void fw_Changed(object sender, FileSystemEventArgs e)
        {
            
        }

        public string GetRelativePath(string path)
        {
            for (int i = 0; i < path.Length; i++)
            {
                if ((_StartPath.Length == i) || (_StartPath[i] != path[i]))
                {                    
                    return ClipStart(path.Substring(i));
                }
            }
            return "";
        }

        protected string ClipStart(string path)
        {
            if (path.Length >= 1)
            {
                if ((path[0] == '\\') || (path[0] == '/'))
                    return path.Substring(1);
            }
            return path;
        }

        /// <summary>
        /// Cleanup on closing
        /// </summary>
        public void Close()
        {
            if (initialized)
            {
                fileWatcher.Dispose();
            }
            initialized = false;
        }

        public void Dispose()
        {
            Close();
        }



        //managing all archives

        /// <summary>
        /// Opens all archives and readies them for using.
        /// In essence enumerates all archives in the working directory, all files in all sub-directories in the working directory 
        /// (does NOT enumerate files directly in the working directory, as these files are either binaries, archives or files
        /// not managed by the filesystem. In case of conflict with archives and normal files always uses normal files as primary sources. 
        /// If some file is in more archives, newest one (based on the date and time of the archive) will be used
        /// Use only for the simplest applications
        /// </summary>
        public void RegisterAllFiles()
        {
            AllFiles.AddRange(EnumerateAllNormalFiles());
            //Unfinished!!! - check for duplicities for repeated calls
        }

        //getting info about files


        public string[] EnumerateFiles()
        {
            return AllFiles.ToArray();
        }


        /// <summary>
        /// Enumerates all files in FS
        /// </summary>
        /// <returns>String array with all filenames</returns>
        string[] EnumerateAllNormalFiles()
        {
            string[] retval = EnumInDirectory(".", true);
            return retval;
        }

        string[] EnumInDirectory(string dir, bool ignorefiles = false)
        {
            List<string> retval = new List<string>();

            string curr = Directory.GetCurrentDirectory();

            Directory.SetCurrentDirectory(curr+"\\" + dir);
            
            string check = Directory.GetCurrentDirectory();

            string[] dirs = Directory.GetDirectories(".");

            foreach (string sdir in dirs)
            {
                string[] rv = EnumInDirectory(sdir);
                if (rv != null)
                    foreach (string s in rv)
                    {
                        retval.Add(dir+s.Substring(1));
                    }
            }

            if (!ignorefiles)
            {
                string[] files = Directory.GetFiles(".");
                if (files != null)
                {
                    foreach (string s in files)
                    {
                        retval.Add(dir+s.Substring(1));
                    }
                }
            }

            if (dir !=".")
                Directory.SetCurrentDirectory("..");
            return retval.ToArray();
        }


        /// <summary>
        /// Checks existence of the file inside the filesystem
        /// </summary>
        /// <param name="file">name of the file with full path</param>
        /// <returns>True if file is in the FS</returns>
        public bool FileExists(string file)
        {
            // for now just check real files
            return 
                System.IO.File.Exists(file);                        
        }

        //reading from archives
        /// <summary>
        /// Opens file from FS for reading and returns stream to use
        /// </summary>
        /// <param name="file">Name of the file to open</param>
        /// <returns></returns>
        public Stream GetFile(string file)
        {
            // no checking for now, just return normal exception from the SYstem.IO
            Stream sb = File.OpenRead(file);
            return sb;
        }

        //writing

        public Stream WriteFile(string file)
        {
            Stream sb = File.OpenWrite(file);
            return sb;
        }

    }
}
