using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using IO;
using HaloPlugins;
using Utils;

namespace Global
{
    public struct TagMetrics
    {
        public int TotalTags;
        public int ExistingTags;
        public int NonExistingTags;
    }

    public class Project
    {
        // Project Files
        public string Directory { get; set; }
        public string Name { get; set; }
        string m_Engine = "";
        public string Engine { get { return m_Engine; } set { m_Engine = value; EngineManager.EngineId = value; } }
        public string Build { get; set; }
        public string TagFolder { get; set; }
        public string BinFolder { get; set; }
        public string TagsFile { get; set; }
        public string MapImage { get; set; }

        // Misc Project Files
        public List<string> Files { get; set; }

        // References
        public List<string> Tags { get; set; }

        Project()
        {
            // Init Lists
            Tags = new List<string>();
            Files = new List<string>();
        }

        public Project(string File)
        {
            // Open Project
            StreamReader sr = new StreamReader(File);

            // Read
            Directory = File.Substring(0, File.LastIndexOf("\\") + 1);
            Name = sr.ReadLine();
            Engine = sr.ReadLine();
            Build = sr.ReadLine();
            TagFolder = sr.ReadLine();
            BinFolder = sr.ReadLine();
            TagsFile = sr.ReadLine();
            MapImage = sr.ReadLine();

            // Misc Files
            Files = new List<string>();
            while (!sr.EndOfStream)
                Files.Add(sr.ReadLine());

            // Read Tags
            sr = new StreamReader(TagsFile);
            Tags = new List<string>();
            while (!sr.EndOfStream)
                Tags.Add(sr.ReadLine());

            // Close
            sr.Close();
        }

        public static Project Create(string Directory, string Name, string Engine, string MapImage)
        {
            // Create new Project
            Project p = new Project();

            // Fill in Fields
            p.Name = Name;
            p.Engine = Engine;
            p.Build = "0.0.0.0";
            p.Directory = Directory + "\\";
            p.TagFolder = p.Directory + "Tags\\";
            p.BinFolder = p.Directory + "Bin\\";
            p.MapImage = MapImage.Length > 0 ? p.BinFolder + "map_image." + MapImage.Substring(MapImage.Length - 3) : "";

            // Copy Map Image To Bin Folder
            try { File.Copy(MapImage, p.MapImage); }
            catch { }

            // Create Folders
            System.IO.Directory.CreateDirectory(p.TagFolder);
            System.IO.Directory.CreateDirectory(p.BinFolder);

            // Create Main Project File
            if (Name != "")
            {
                p.TagsFile = p.Directory + Name + ".htl";

                // Save
                StreamWriter sw = new StreamWriter(Directory + "\\" + Name + ".hpr");
                sw.WriteLine(p.Name);
                sw.WriteLine(p.Engine);
                sw.WriteLine(p.Build);
                sw.WriteLine(p.TagFolder);
                sw.WriteLine(p.BinFolder);
                sw.WriteLine(p.TagsFile);
                sw.WriteLine(p.MapImage);
                sw.Close();
            }

            // Return
            return p;
        }

        public void Save()
        {
            // Create New File
            StreamWriter sw = new StreamWriter(Directory + "\\" + Name + ".hpr");

            // Set tags file
            if (TagsFile == null)
                TagsFile = Directory + Name + ".htl";

            // Write Basic Info
            sw.WriteLine(Name);
            sw.WriteLine(Engine);
            sw.WriteLine(Build);
            sw.WriteLine(TagFolder);
            sw.WriteLine(BinFolder);
            sw.WriteLine(TagsFile);
            sw.WriteLine(MapImage);

            // Write Misc Info
            for (int i = 0; i < Files.Count; i++)
                sw.WriteLine(Files[i]);

            // Close
            sw.Close();

            // Write Tags
            sw = new StreamWriter(TagsFile);
            for (int i = 0; i < Tags.Count; i++)
                sw.WriteLine(Tags[i]);

            // Close
            sw.Close();
        }

        #region Import Tag

        public TagMetrics ImportTagInit(string TagPath, bool Recusive)
        {
            // Process tags
            return ImportTagInitProcess(TagPath, Recusive);
        }

        TagMetrics ImportTagInitProcess(string TagPath, bool Recursive)
        {
            // Open the tag
            EndianReader br = new EndianReader(Endianness.Little, new FileStream(TagPath, FileMode.Open, FileAccess.Read, FileShare.Read));

            // Parse the header
            int TagCount = br.ReadInt32();
            int StringCount = br.ReadInt32();
            int DataOffset = br.ReadInt32();
            int RawOffset = br.ReadInt32();
            string AbsolutePath = br.ReadString();

            // Read all the tag references
            string[] References = new string[TagCount];
            br.BaseStream.Position = DataOffset;
            for (int i = 0; i < TagCount; i++)
                References[i] = br.ReadNullTerminatingString();

            // Close stream
            br.Close();

            // Create our stats struct
            TagMetrics metrics = new TagMetrics();
            metrics.TotalTags = 1;
            metrics.ExistingTags = File.Exists(this.TagFolder + AbsolutePath) == true ? 1 : 0;
            metrics.NonExistingTags = metrics.ExistingTags == 1 ? 0 : 1;

            // Do we scan all tags or no
            if (Recursive)
            {
                // Scan all references
                string Directory = TagPath.Replace(AbsolutePath, "");
                for (int i = 0; i < References.Length; i++)
                {
                    // Check if its a self reference
                    if (References[i] != AbsolutePath)
                    {
                        // Check if its local
                        if (File.Exists(this.TagFolder + References[i]))
                            metrics.ExistingTags++;
                        else
                        {
                            // Add this tag
                            metrics.NonExistingTags++;

                            // Recursive scan
                            TagMetrics submetrics = ImportTagInitProcess(Directory + References[i], Recursive);
                            metrics.TotalTags += submetrics.TotalTags;
                            metrics.ExistingTags += submetrics.ExistingTags;
                            metrics.NonExistingTags += submetrics.NonExistingTags;
                        }
                        metrics.TotalTags++;
                    }
                }
            }

            // Done
            return metrics;
        }

        public void ImportTagContinue(string TagPath, bool Recursive, bool Overwrite)
        {
            // Open the tag
            EndianReader br = new EndianReader(Endianness.Little, new FileStream(TagPath, FileMode.Open, FileAccess.Read, FileShare.Read));

            // Parse the header
            int TagCount = br.ReadInt32();
            int StringCount = br.ReadInt32();
            int DataOffset = br.ReadInt32();
            int RawOffset = br.ReadInt32();
            string AbsolutePath = br.ReadString();

            // Read all the tag references
            string[] References = new string[TagCount];
            br.BaseStream.Position = DataOffset;
            for (int i = 0; i < TagCount; i++)
                References[i] = br.ReadNullTerminatingString();

            // Close stream
            br.Close();

            // Create directorys
            string Name = AbsolutePath.Substring(AbsolutePath.LastIndexOf("\\") + 1);
            string Path = this.TagFolder;
            string[] Folders = AbsolutePath.Split(new string[] { "\\" }, StringSplitOptions.None);
            for (int i = 0; i < Folders.Length - 1; i++)
            {
                Path += Folders[i] + "\\";
                if (!System.IO.Directory.Exists(Path))
                    System.IO.Directory.CreateDirectory(Path);
            }

            // Add this tag
            if (File.Exists(this.TagFolder + AbsolutePath))
            {
                // Do we overwrite
                if (Overwrite)
                    File.Copy(TagPath, this.TagFolder + AbsolutePath, Overwrite);
            }
            else
            {
                // Add tag
                File.Copy(TagPath, this.TagFolder + AbsolutePath, Overwrite);
                this.Tags.Insert(this.Tags.Count - 2, AbsolutePath);

                // Check for special tag types
                if (TagPath.Substring(TagPath.LastIndexOf(".") + 1) == "sound")
                {
                    // We need to add sound raw
                    string Coco = this.TagFolder + this.Tags[this.Tags.Count - 1];
                    string NewCoco = TagPath.Replace(AbsolutePath, "") + "i've got a lovely bunch of coconuts.sound_diagnostics";
                    ProjectHelper.AddSoundRaw(this.TagFolder + AbsolutePath, Coco, NewCoco);
                }
            }

            // Add references
            if (Recursive)
            {
                // Add every reference
                string Directory = TagPath.Replace(AbsolutePath, "");
                for (int i = 0; i < References.Length; i++)
                {
                    // Check its not a self reference
                    if (References[i] != AbsolutePath)
                        ImportTagContinue(Directory + References[i], Recursive, Overwrite);
                }
            }
        }

        #endregion

        public int AddTag(string TagPath, bool Recursive)
        {
            // Open the tag
            EndianReader br = new EndianReader(Endianness.Little, new FileStream(TagPath, FileMode.Open, FileAccess.Read, FileShare.Read));

            // Parse the header
            int TagCount = br.ReadInt32();
            int StringCount = br.ReadInt32();
            int DataOffset = br.ReadInt32();
            int RawOffset = br.ReadInt32();
            string AbsolutePath = br.ReadString();

            // Read all the tag references
            string[] References = new string[TagCount];
            br.BaseStream.Position = DataOffset;
            for (int i = 0; i < TagCount; i++)
                References[i] = br.ReadNullTerminatingString();

            // Close stream
            br.Close();

            // Create directorys
            string Name = AbsolutePath.Substring(AbsolutePath.LastIndexOf("\\") + 1);
            string Path = this.TagFolder;
            string[] Folders = AbsolutePath.Split(new string[] { "\\" }, StringSplitOptions.None);
            for (int i = 0; i < Folders.Length - 1; i++)
            {
                Path += Folders[i] + "\\";
                if (!System.IO.Directory.Exists(Path))
                    System.IO.Directory.CreateDirectory(Path);
            }

            // Check if the tag exists
            if (File.Exists(Path + Name))
            {
                // todo...
            }
            else
            {
                // Copy
                File.Copy(TagPath, Path + Name);

                // Add to project
                Tags.Add(AbsolutePath);
            }

            // Add references
            int Count = 1;
            if (Recursive)
            {
                string Directory = TagPath.Replace(AbsolutePath, "");
                for (int i = 0; i < TagCount; i++)
                {
                    // Check its not a self reference
                    if (References[i] != AbsolutePath)
                        Count += AddTag(Directory + References[i], Recursive);
                }
            }

            // Done
            return Count;
        }
    }
}
