using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.IO;
using System.ComponentModel;
using System.Reflection;
using SevenZipLib;

namespace TwoMbit
{
    class File
    {
        public enum Type { NORMAL, TAR_GZ, ZIP, RAR, _7Z, GZ, TAR, BZ2, ARJ };
        public enum Error { OPEN, FINE, EXTRACTOR_DLL, TAR_GZ };
        public const long MAX_FILE_SIZE = 0x800000;

        FileStream fs;
        ulong size;
        string path = "";
        string uc_name = "";
        Type type = Type.NORMAL;
        Error error = Error.FINE;

        public File(string path)
        {
            this.path = path;
            type = getFileType();
        }

        public uint getSize()
        {
            return (uint)size;
        }

        public Error getError()
        {
            return error;
        }

        public string getFilename()
        {
            if (uc_name.Length == 0) uc_name = path;
            int start = uc_name.LastIndexOf("\\");
            if (start != -1) uc_name = uc_name.Substring(start+1);
            int end = uc_name.LastIndexOf(".");
            if (end != -1) uc_name = uc_name.Substring(0, end);
            return uc_name;
        }

        public string getPath()
        {
            int end = path.LastIndexOf("\\");
            if (end == -1) return path;
            return path.Substring(0, end+1);
        }

        public byte[] fetch()
        {
            switch(type) {
                default:
                case Type.NORMAL:
                    error = open();
                    if (error != Error.FINE) return null;
                    return read();
                case Type._7Z:
                case Type.ARJ:
                case Type.BZ2:
                case Type.GZ:
                case Type.RAR:
                case Type.TAR:
                case Type.ZIP:
                case Type.TAR_GZ:
                    byte[] data = openAndExtractArchive();
                    if (error != Error.FINE) return null;
                    return data;
            }
        }

        byte[] openAndExtractArchive() {
            size = 0;
            ArchiveEntry entryinUse = null;
            MemoryStream mStream;
            try {
                using (SevenZipArchive archive = new SevenZipArchive(path)) {
                    foreach (ArchiveEntry entry in archive) {
                        if ( (type == Type.BZ2) || (entry.Size > size && entry.Size <= (MAX_FILE_SIZE + 512))) {
                            entryinUse = entry;
                            this.size = entry.Size;
                            this.uc_name = entry.FileName;
                        }
                    }
                    if (entryinUse == null) {
                        throw new Exception();
                    }

                    mStream = new MemoryStream();
                    entryinUse.Extract(mStream);
                    if (this.size == 0) {
                        this.size = (ulong)mStream.Length;
                        if ( this.size == 0 || ( this.size > (MAX_FILE_SIZE + 512) ) ) {
                            throw new Exception();
                        }
                    }
                }
            } catch (Win32Exception) {
                error = Error.EXTRACTOR_DLL;
                return null;
            } catch {
                error = Error.OPEN;
                return null;
            }
            error = Error.FINE;
            return mStream.ToArray();
        }

        public void write(string path, byte[] data, uint size)
        {
            try {
                fs = new FileStream(path, FileMode.Create, FileAccess.Write);
            } catch {
                return;
            }

            fs.Write(data, 0, (int)size);
            fs.Close();
        }

        Error open()
        {
            try {
                fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            } catch {
                return Error.OPEN;
            }
            size = (uint)fs.Length;
            if ((size == 0) || (size > MAX_FILE_SIZE + 512)) {
                fs.Close();
                return Error.OPEN;
            }
            return Error.FINE;
        }

        byte[] read()
        {
            byte[] data = new byte[size];
            try {
                fs.Read(data, 0, (int)size);
            } catch {
                error = Error.OPEN;
                data = null;
            }
            fs.Close();
            return data;
        }

        Type getFileType()
        {
            string tmp_fn = path.ToLower();
            Type found = Type.NORMAL;
            Regex regex;
            Dictionary<string, Type> extensions = new Dictionary<string, Type>();

            extensions[ ".+\\.tar\\.gz$"] =	Type.TAR_GZ;
            extensions[ ".+\\.tgz$"	] =		Type.TAR_GZ;
            extensions[ ".+\\.tar$"	] =		Type.TAR;
            extensions[ ".+\\.gz$"	] =		Type.GZ;
            extensions[ ".+\\.zip$"	] =		Type.ZIP;
            extensions[ ".+\\.rar$"	] =		Type.RAR;
            extensions[ ".+\\.7z$"	] =		Type._7Z;
            extensions[ ".+\\.bz2$"	] =		Type.BZ2;
            extensions[ ".+\\.arj$"	] =		Type.ARJ;

            foreach(KeyValuePair<string, Type> extension in extensions) {
                regex = new Regex(extension.Key);
                if (regex.IsMatch(tmp_fn, 0)) {
                    if ((found == Type.NORMAL) || (extension.Value == Type.TAR_GZ)) {
			            found = extension.Value;
		            }
                }
            }
            return found;
        }

        static public bool generateDir(string _path)
        {
            if (!Directory.Exists(_path))
            {
                try
                {
                    Directory.CreateDirectory(_path);
                    if (Directory.Exists(_path))
                    {
                        return true;
                    }
                    return false;
                }
                catch { return false; }
            }
            return true;
        }
    }
}