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

namespace Object_Editor
{
    static class ObjectFile
    {
        [Serializable]
        public class AssertionFailException : FormatException
        {
            public AssertionFailException() { }
            public AssertionFailException(string message) : base(message) { }
            public AssertionFailException(string message, Exception inner) : base(message, inner) { }
            protected AssertionFailException(
              System.Runtime.Serialization.SerializationInfo info,
              System.Runtime.Serialization.StreamingContext context)
                : base(info, context) { }
        }

        private static void Assert(bool assert, string comment)
        {
            if (!assert) throw new AssertionFailException(comment);
        }

        private static int Assert(int assert, string comment, params int[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                if (assert == values[i]) return i;
            }
            throw new AssertionFailException(comment);
        }

        private static int Assert(char assert, string comment, params char[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                if (assert == values[i]) return i;
            }
            throw new AssertionFailException(comment);
        }

        private static int Assert(string assert, string comment, params string[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                if (assert.Equals(values[i], StringComparison.Ordinal)) return i;
            }
            throw new AssertionFailException(comment);
        }

        private static int Assert(string assert, string comment, StringComparison comparisonType, params string[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                if (assert.Equals(values[i], comparisonType)) return i;
            }
            throw new AssertionFailException(comment);
        }

        public static bool Open(out ObjectManager.Mesh[] Object, System.IO.FileStream FileStream)
        {
            bool Success = false;
            int l = 1;
            List<ObjectManager.Mesh> Meshes = new List<ObjectManager.Mesh>();
            try
            {
                System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture;
                System.IO.StreamReader s = new System.IO.StreamReader(FileStream, Encoding.UTF8);

                string cLine;

                #region Check Header Information
                Assert(s.ReadLine(), "File header", ";#=Object Editor 0.5"); l++;
                #endregion
                s.ReadLine(); l++;
                #region Read MeshBuilder Sections
                ObjectManager.Mesh Mesh;
                while (s.Peek() > -1)
                {
                    Mesh = new Mesh();
                    Assert(s.ReadLine(), "MeshBuilder section beginning", "CreateMeshBuilder"); l++;
                    // TODO:Mesh Name
                    cLine = s.ReadLine(); l++;
                    Assert(cLine.Substring(0, 3), "Mesh name", ";#=");
                    //Mesh.Name = cLine.Substring(3);
                    #region Vertices
                    while ((cLine = s.ReadLine()) != "")
                    {
                        string[] args = cLine.Split(new string[] { ", " }, StringSplitOptions.None);
                        Assert(args.Length, "No. of args for vertex", 7);
                        Assert(args[0], "Vertex command", "AddVertex");
                        ObjectManager.Vertex v = new ObjectManager.Vertex(double.Parse(args[1]),double.Parse(args[2]),double.Parse(args[3]),double.Parse(args[4]),double.Parse(args[5]),double.Parse(args[6]));
                        //v.Coordinates.X = double.Parse(args[1]);
                        //v.Coordinates.Y = double.Parse(args[2]);
                        //v.Coordinates.Z = double.Parse(args[3]);
                        //v.PresetNormals.X = double.Parse(args[4]);
                        //v.PresetNormals.X = double.Parse(args[5]);
                        //v.PresetNormals.X = double.Parse(args[6]);
                        Mesh.Vertices.Add(v);
                        l++;
                    }
                    l++;
                    #endregion
                    #region Faces
                    while ((cLine = s.ReadLine()) != "")
                    {
                        string[] args = cLine.Split(new string[] { ", " }, StringSplitOptions.None);
                        Assert(args.Length > 1, "Minimum no. of args for face");
                        Assert(args[0], "Face command", "AddFace");
                        ObjectManager.Face f = new ObjectManager.Face();
                        for (int i = 1; i < args.Length; i++)
                        {
                            int index = int.Parse(args[i]);
                            f.Vertices.Add(new ObjectManager.FaceVertex(Mesh, index));
                        }
                        f.CreateNormals();
                        Mesh.Faces.Add(f);
                        l++;
                    }
                    l++;
                    #endregion
                    #region Material
                    {
                        string[] args = s.ReadLine().Split(new string[] { ", " }, StringSplitOptions.None);
                        Assert(args.Length, "No. of args for SetColor", 5);
                        Assert(args[0], "Color command", "SetColor");
                        int r, g, b, a;
                        r = int.Parse(args[1]);
                        g = int.Parse(args[2]);
                        b = int.Parse(args[3]);
                        a = int.Parse(args[4]);
                        Assert(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 255, "Color in range");
                        Mesh.Material.Color = System.Drawing.Color.FromArgb(a, r, g, b);
                        l++;
                    }
                    {
                        string[] args = s.ReadLine().Split(new string[] { ", " }, StringSplitOptions.None);
                        if (Assert(args.Length, "EmissiveColor command", 4, 1) == 0 && Assert(args[0], "EmissiveColor command", "SetEmissiveColor", ";SetEmissiveColor") == 0)
                        {
                            int r, g, b;
                            r = int.Parse(args[1]);
                            g = int.Parse(args[2]);
                            b = int.Parse(args[3]);
                            Assert(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255, "Color in range");
                            Mesh.Material.EmissiveColorUsed = true;
                            Mesh.Material.EmissiveColor = System.Drawing.Color.FromArgb(r, g, b);
                        }
                        l++;
                    }
                    {
                        string[] args = s.ReadLine().Split(new string[] { ", " }, StringSplitOptions.None);
                        if (Assert(args.Length, "BlendMode command", 4, 1) == 0 && Assert(args[0], "BlendMode command", "SetBlendMode", ";SetEmissiveColor") == 0)
                        {
                            switch (Assert(args[1], "BlendMode arg", "Normal", "Additive"))
                            {
                                case 0:
                                    Mesh.Material.BlendMode = MeshMaterialBlendModeEnum.Normal;
                                    break;
                                case 1:
                                    Mesh.Material.BlendMode = MeshMaterialBlendModeEnum.Additive;
                                    break;
                            }
                            Mesh.Material.GlowHalfDistance = double.Parse(args[2]);
                            switch (Assert(args[3], "GlowAttenuationMode arg", "DivideExponent2", "DivideExponent4"))
                            {
                                case 0:
                                    Mesh.Material.GlowAttenuationMode = GlowAttenuationModeEnum.DivisionExponent2;
                                    break;
                                case 1:
                                    Mesh.Material.GlowAttenuationMode = GlowAttenuationModeEnum.DivisionExponent4;
                                    break;
                            }
                        }
                        l++;
                    }
                    {
                        string[] args = s.ReadLine().Split(new string[] { ", " }, StringSplitOptions.None);
                        // TODO: handle texture
                        switch (Assert(args.Length, "Texture command", 1, 2, 3))
                        {
                            case 0:
                                Assert(args[0], "Texture command", ";LoadTexture");
                                break;
                            case 1:
                                Assert(args[0], "Texture command", "LoadTexture");
                                if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileStream.Name), args[1])))
                                {
                                    Mesh.Material.DaytimeTexture = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileStream.Name), args[1]);
                                }
                                break;
                            case 2:
                                Assert(args[0], "Texture command", "LoadTexture");
                                if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileStream.Name), args[1])))
                                {
                                    Mesh.Material.DaytimeTexture = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileStream.Name), args[1]);
                                    if (System.IO.File.Exists(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileStream.Name), args[2])))
                                    {
                                        Mesh.Material.NighttimeTexture = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileStream.Name), args[2]);
                                    }
                                    break;
                                }
                                break;
                        }
                        l++;
                    }
                    {
                        string[] args = s.ReadLine().Split(new string[] { ", " }, StringSplitOptions.None);
                        if (Assert(args.Length, "Texture Transparent command", 4, 1) == 0 && Assert(args[0], "Texture Transparent command", "SetDecalTransparentColor", ";SetDecalTransparentColor") == 0)
                        {
                            int r, g, b;
                            r = int.Parse(args[1]);
                            g = int.Parse(args[2]);
                            b = int.Parse(args[3]);
                            Assert(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255, "Color in range");
                            Mesh.Material.TransparentColorUsed = true;
                            Mesh.Material.TransparentColor = System.Drawing.Color.FromArgb(r, g, b);
                        }
                        l++;
                    }
                    #endregion
                    s.ReadLine(); l++;
                    #region Static Transformation
                    //// X Rotation
                    //cLine = s.ReadLine();
                    //Assert(cLine.Substring(0, 17), "X Rotation command", "Rotate, 1, 0, 0, ");
                    //Mesh.StaticRotation.X = double.Parse(cLine.Substring(17));
                    //l++;
                    //// Y Rotation
                    //cLine = s.ReadLine();
                    //Assert(cLine.Substring(0, 17), "Y Rotation command", "Rotate, 0, 1, 0, ");
                    //Mesh.StaticRotation.Y = double.Parse(cLine.Substring(17));
                    //l++;
                    //// Z Rotation
                    //cLine = s.ReadLine();
                    //Assert(cLine.Substring(0, 17), "Z Rotation command", "Rotate, 0, 0, 1, ");
                    //Mesh.StaticRotation.Z = double.Parse(cLine.Substring(17));
                    //l++;
                    // Translation
                    {
                        string[] args = s.ReadLine().Split(new string[] { ", " }, StringSplitOptions.None);
                        Assert(args.Length, "No. of args for translate", 4);
                        Assert(args[0], "Translate command", "Translate");
                        Mesh.StaticTranslation.X = double.Parse(args[1]);
                        Mesh.StaticTranslation.Y = double.Parse(args[2]);
                        Mesh.StaticTranslation.Z = double.Parse(args[3]);
                        l++;
                    }
                    Mesh.RenewTransformedVertices();
                    #endregion
                    s.ReadLine(); l++;
                    Meshes.Add(Mesh);
                }
                #endregion
                Object = Meshes.ToArray();
                Success = true;
            }
            catch (Exception e)
            {
                System.Windows.Forms.MessageBox.Show("Line " + l.ToString() + "\n" + e.ToString());
                Object = new ObjectManager.Mesh[] { };
            }
            return Success;
        }
    }
}
