using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using VisiMath;
using VisiMath.Geometry;
using System.ComponentModel;
using System.Xml.Serialization;
using System.Xml;

namespace VisimBox.Scene
{
    public abstract class NodeImporter
    {
        /// <summary>
        /// defines extension of file format it can import
        /// </summary>
        public abstract string FileFormat { get; }

        /// <summary>
        /// Import external file to node
        /// </summary>
        /// <param name="node">Node to hold imported nodes</param>
        /// <param name="filename">Filename to import</param>
        /// <returns>True if import sucessful</returns>
        public abstract bool Import(Node node, string filename);

        /// <summary>
        /// Basic settings for importing
        /// </summary>
        public NodeImportSettings Settings
        {
            get;
            set;
        }

    }

    /// <summary>
    /// Settings for node importing
    /// </summary>
    public class NodeImportSettings : IXmlSerializable
    {
        /// <summary>
        /// import and use materials? otherwise imported nodes will be used "default" material from the renderer
        /// </summary>
        [Description("Import and use materials? otherwise imported nodes will be used default material from the renderer")]
        [Category("Import settings")]
        public bool ImportMaterials
        {
            get;
            set;
        }

        /// <summary>
        /// Default scale for the position vertices (for models made in centimeters/inches)
        /// </summary>
        [Description("Default scale for the position vertices (for models made in centimeters/inches)")]
        [Category("Import settings")]
        public float DefaultScale
        {
            get;
            set;
        }

        /// <summary>
        /// Transformation to use on loaded vertices / normals (after DefaultScale used on positions)
        /// </summary>
        [Description("Transformation to use on loaded vertices / normals (after DefaultScale used on positions)")]
        [Category("Import settings")]
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public ITransformation DefaultTrans
        {
            get;
            set;
        }

        /// <summary>
        /// Use geometry nodes for holding imported data, or just use direct rendering objects?
        /// </summary>
        [Description("Use geometry nodes for holding imported data, or just use direct rendering objects?")]
        [Category("Import settings")]
        public bool AsGeometryNodes
        {
            get;
            set;
        }

        /// <summary>
        /// If true, all subnodes will have their own bounding boxes set
        /// </summary>
        [Description("If true, all subnodes will have their own bounding boxes set")]
        [Category("Import settings")]
        public bool BoundingBoxesForSubnodes
        {
            get;
            set;
        }

        /// <summary>
        /// If true, holder of the imported nodes will have bounding box updated to hold all subnodes
        /// </summary>
        [Description("If true, holder of the imported nodes will have bounding box updated to hold all subnodes")]
        [Category("Import settings")]
        public bool BoundingBoxForHolder
        {
            get;
            set;
        }

        public NodeImportSettings()
        {
            SetDefaults();
        }

        public NodeImportSettings(NodeImportSettings copy)
        {
            this.ImportMaterials = copy.ImportMaterials;
            this.DefaultTrans = copy.DefaultTrans;
            this.DefaultScale = copy.DefaultScale;
            this.BoundingBoxForHolder = copy.BoundingBoxForHolder;
            this.BoundingBoxesForSubnodes = copy.BoundingBoxesForSubnodes;
            this.AsGeometryNodes = copy.AsGeometryNodes;
        }

        public NodeImportSettings(XmlReader reader)
        {
            ReadXml(reader);
        }

        public void SetDefaults()
        {
            ImportMaterials = true;
            DefaultScale = 1;
            DefaultTrans = null;
            AsGeometryNodes = true;
            BoundingBoxesForSubnodes = false;
            BoundingBoxForHolder = true;
        }


        #region IXmlSerializable Members

        /// <summary>
        /// Reqired by IXMLserializable
        /// </summary>
        /// <returns>Null</returns>
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Deserializes object from XML
        /// </summary>
        /// <param name="reader">XML reader</param>
        public void ReadXml(System.Xml.XmlReader reader)
        {
            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    switch (reader.Name)
                    {
                        case "ImportMaterials":
                            ImportMaterials = XMLHelper.ReadAsBool(reader);
                            break;
                        case "DefaultScale":
                            DefaultScale = XMLHelper.ReadAsFloat(reader);
                            break;
                        case "DefaultTrans":
                            DefaultTrans = TransformationFactory.ReadFromXML(reader);
                            break;
                        case "AsGeometryNodes":
                            AsGeometryNodes = XMLHelper.ReadAsBool(reader);
                            break;
                        case "BoundingBoxesForSubnodes":
                            BoundingBoxesForSubnodes = XMLHelper.ReadAsBool(reader);
                            break;
                        case "BoundingBoxForHolder":
                            BoundingBoxForHolder = XMLHelper.ReadAsBool(reader);
                            break;
                        default:
                            reader.Skip();
                            break;
                    }
                }
                else return;
            }
        }

        /// <summary>
        /// Serializes object to XML
        /// </summary>
        /// <param name="writer">XML writer to save values</param>
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteElementString("ImportMaterials", ImportMaterials.ToString());
            writer.WriteElementString("AsGeometryNodes", AsGeometryNodes.ToString());
            writer.WriteElementString("BoundingBoxesForSubnodes", BoundingBoxesForSubnodes.ToString());
            writer.WriteElementString("BoundingBoxForHolder", BoundingBoxForHolder.ToString());
            XMLHelper.WriteFloat(writer, "DefaultScale", DefaultScale);
            if (DefaultTrans != null)
            {
                writer.WriteStartElement("DefaultTrans");
                DefaultTrans.WriteXml(writer);
                writer.WriteEndElement();
            }
        }

        #endregion
    }

    /// <summary>
    /// Imported from Lightwave objects
    /// </summary>
    public class LightWaveObjImporter : NodeImporter
    {
        //todo - support all possible obj elements (http://www.martinreddy.net/gfx/3d/OBJ.spec)



        public LightWaveObjImporter()
        {
        }

        public override string FileFormat
        {
            get { return "obj"; }
        }

        protected struct OBJIndice
        {
            public int Pos;
            public int Norm;
            public int Tex;

            public OBJIndice(int p, int n, int t)
            {
                Pos = p;
                Norm = n;
                Tex = t;
            }
        }

        public override bool Import(Node node, string filename)
        {
            if (Settings == null)
                Settings = new NodeImportSettings();
            
            TextReader sourcefile;

            try
            {
                sourcefile = new StreamReader(filename);
            }
            catch
            {
                Console.DebugLine("OBJ import failure: Can't open file " + filename);
                return false;
            }

            MatLibrary = new Dictionary<string, OBJMat>();

            List<Vector3> Vertices = new List<Vector3>(100);
            List<Vector3> Normals = new List<Vector3>(100);
            List<Vector2> TexCoords = new List<Vector2>(100);

            List<List<OBJIndice>> AllIndices = new List<List<OBJIndice>>(1);
            List<string> MaterialNames = new List<string>(1);

            List<OBJIndice> Indices = null;

            bool tex3dwarning = false;

            string sourceline;
            while ((sourceline = sourcefile.ReadLine())!= null)
            {                
                if ((sourceline == "") || (sourceline[0] == '#'))
                    continue;   //ignore comments and empty lines
                
                if (sourceline.StartsWith("mtllib"))
                {
                    //import material library

                    if (Settings.ImportMaterials)
                    {

                        string libname = sourceline.Substring(6);

                        libname = libname.TrimStart(new char[] { ' ', '.', '/', '\\' });

                        string path = Path.GetDirectoryName(filename);
                        if (path != null)
                        {
                            libname = path + "/" + libname;
                        }

                        if (!LoadMaterialLibrary(libname))
                        {
                            Console.DebugLine("Material library " + libname + "failed. Using default materials");
                        }
                    }
                    continue;
                }
                
                if (sourceline[0] == 'g')
                {
                    //start new group - ignore for now, only different mtls differentiate                   
                    continue;
                }
                if (sourceline.StartsWith("v "))
                {
                    //read vertex
                    Vector3 v = ReadVector(sourceline.Substring(2));
                    if (v.IsValid())
                    {
                        Vertices.Add(v);
                    }
                    else
                    {
                        Vertices.Add(new Vector3());
                        Console.DebugLine("OBJ Import line " + sourceline + " doesn't contain valid vertex!");
                    }
                    continue;
                }

                if (sourceline.StartsWith("vt "))
                {
                    //read texture coordinate
                    Vector3 v = ReadVector(sourceline.Substring(3));
                    if ((v.X == float.NaN) || (v.Y == float.NaN))
                    {
                        Console.DebugLine("OBJ Import line " + sourceline + " doesn't contain valid vertex!");
                        TexCoords.Add(new Vector2());
                        continue;
                    }
                    if ((v.Z != float.NaN))
                    {
                        if (!tex3dwarning)
                        {
                            tex3dwarning = true;
                            Console.DebugLine("OBJ Import - 3d texturing is not supported, ignoring!");
                        }
                        TexCoords.Add(new Vector2());
                        continue;
                    }

                    Vector2 vv = new Vector2(v.X, v.Y);
                    TexCoords.Add(vv);
                    continue;
                }

                if (sourceline.StartsWith("vn "))
                {
                    //read normal
                    Vector3 v = ReadVector(sourceline.Substring(3));
                    if (v.IsValid())
                    {
                        Normals.Add(v);
                    }
                    else
                    {
                        Normals.Add(new Vector3());
                        Console.DebugLine("OBJ Import line " + sourceline + " doesn't contain valid vertex!");
                    }
                    continue;
                }

                if (sourceline.StartsWith("usemtl "))
                {
                    if (Settings.ImportMaterials)
                    {
                        //switch material / group
                        if (Indices != null)
                        {
                            AllIndices.Add(Indices);
                            if (MaterialNames.Count == 0)
                                MaterialNames.Add(null);
                        }

                        string matname = sourceline.Substring(7);

                        MaterialNames.Add(matname);

                        Indices = new List<OBJIndice>(500);
                    }
                    continue;
                }
                if (sourceline.StartsWith("f "))
                {
                    //read new face

                    List<OBJIndice> face = ReadFace(sourceline.Substring(2));
                    if (face == null)
                    {
                        Console.DebugLine("OBJ Import line " + sourceline + " doesn't contain valid face data!");
                        continue;
                    }

                    if (Indices == null)
                        Indices = new List<OBJIndice>(500);

                    Indices.AddRange(face);
                    continue;
                }
                if (sourceline.StartsWith("s "))
                {
                    //smooth group, ignore
                    continue;
                }

            }

            if (Indices != null)
                AllIndices.Add(Indices);

            sourcefile.Close();

            //now convert all this junk to normal vertex array and index array
            //todo - currently using Trilist, rework for indexed mesh and/or editable mesh

            //VertexArray<NormalVertex> varray = new VertexArray<NormalVertex>(Vertices.Count);


            //since some models are REALLY stupid, fist group all indices together for same materials...

            List<List<OBJIndice>> reformated = new List<List<OBJIndice>>(MatLibrary.Count);
            List<string> reformatednames = new List<string>(MatLibrary.Count);

            if (Settings.ImportMaterials)
            {

                for (int block = 0; block < AllIndices.Count; block++)
                {
                    Indices = AllIndices[block];

                    if (Indices.Count == 0)
                    {
                        Console.DebugLine("OBJ import block " + block.ToString() + " is empty, ignoring");
                        continue;
                    }

                    string matname = MaterialNames[block];

                    if ((matname == null) || (!MatLibrary.ContainsKey(matname)))
                    {
                        Console.DebugLine("OBJ import block " + block.ToString() + " has no loaded material, using default");
                        matname = "default";
                    }

                    int idx = reformatednames.IndexOf(matname);

                    if (idx >= 0)
                    {
                        reformated[idx].AddRange(Indices);
                    }
                    else
                    {
                        Renderer.IRenderMaterial mat;

                        //create material from loaded mat library
                        if (VisimEngine.Instance.Renderer.MaterialMan.Contains(matname))
                        {
                            mat = VisimEngine.Instance.Renderer.MaterialMan[matname];   //TODO check for renamed materials here!!!
                        }
                        else
                        {
                            mat = CreateOBJMat(MatLibrary[matname]);
                        }
                        reformatednames.Add(matname);
                        reformated.Add(Indices);
                    }
                }

                AllIndices = reformated;
            }
            else
            {
                reformatednames.Add("default");
            }
    
            //TODO - it would be probably faster to move all vertices / normals etc to normal array and perform block transformations
            if ((Settings.DefaultTrans != null) || (Settings.DefaultScale != 1))
            {
                Matrix4 matrix;
                float scale = Settings.DefaultScale;
                if (Settings.DefaultTrans != null)
                {
                    matrix = Settings.DefaultTrans.TransMatrix;
                }
                else matrix = Matrix4.Identitity;
                for (int i = 0; i < Vertices.Count; i++)
                {
                    Vector3 vec = Vertices[i];
                    if (scale != 1)
                    {
                        vec.Mul(scale);
                    }

                    if (Settings.DefaultTrans != null)
                    {
                        matrix.Transform(ref vec);
                    }
                    Vertices[i] = vec;
                }

                if (Settings.DefaultTrans != null)
                {
                    for (int i = 0; i < Normals.Count; i++)
                    {
                        Vector3 nor = Normals[i];
                        matrix.TransformNormal(ref nor);
                        Normals[i] = nor;
                    }
                }
            }

            

            for (int block = 0; block < AllIndices.Count; block++)
            {
                Indices = AllIndices[block];

                if (Indices.Count == 0)
                {
                    Console.DebugLine("OBJ import block " + block.ToString() + " is empty, ignoring");
                    continue;
                }

                VertexArray<TexturedVertex> varray = new VertexArray<TexturedVertex>(Indices.Count);


                for (int i = 0; i < varray.Count; i++)
                {
                    Vector3 pos = new Vector3(Vertices[Indices[i].Pos - 1]);
                    Vector3 norm = Normals[Indices[i].Norm - 1];
                    Vector2 tex = TexCoords[Indices[i].Tex - 1];


                    varray[i] = new TexturedVertex(pos, norm, tex);
                }

                TriangleList tris = new TriangleList(varray);
                Renderer.IRenderMaterial mat = VisimEngine.Instance.Renderer.MaterialMan[reformatednames[block]];

                AABox box = null;
                if (Settings.BoundingBoxesForSubnodes)
                {
                    box = GeoFactory.CreateBoundingBox(tris);
                }

                if (Settings.AsGeometryNodes)
                {
                    GeometryNode geonode = new GeometryNode(tris, tris.PreferredVertexType, mat);
                    geonode.BoundingObject = box;
                    node.AddNode(geonode, filename+"_block#"+block.ToString());
                }
                else
                {
                    var mesh = VisimEngine.Instance.Renderer.MeshMan.Add(tris, "OBJ " + block.ToString());
                    NodeMesh nm = new NodeMesh(mesh, mat);
                    nm.BoundingObject = box;
                    node.AddNode(nm, filename+"_block#"+block.ToString());
                }
            }

            RenderableNode holder = node as RenderableNode;
            if (Settings.BoundingBoxForHolder && (holder != null))
            {
                AABox box = AABox.ConstructFromPositions(Vertices);
                holder.BoundingObject = box;
            }


            ImportedNode inode = node as ImportedNode;
            if (inode != null)
            {
                inode.ImportedSettings = new NodeImportSettings(this.Settings);
                inode.FileFormat = this.FileFormat;
            }

            Console.WriteLine(filename + " imported");
            return true;
        }

        protected Renderer.IRenderMaterial CreateOBJMat(OBJMat mat)
        {
            if (mat.AmbientTexture != null)
            {
                Console.DebugLine("OBJ MAT import - material " + mat.name + " has ambient texture - ignoring");
            }
            if (mat.SpecularTexture != null)
            {
                Console.DebugLine("OBJ MAT import - material " + mat.name + " has specular texture - ignoring");
            }
            if (mat.BumpTexture != null)
            {
                Console.DebugLine("OBJ MAT import - material " + mat.name + " has bump texture - ignoring");
            }

            //create color values

            mat.colorvals.Shininess = mat.SpecCoeff;
            if ((mat.alpha != 1)&&(mat.alpha != 0))
            {
                mat.colorvals.Alpha = true;
                mat.colorvals.Diffuse.A = (byte)(255 * mat.alpha);
            }

            if (mat.DiffuseTexture != null)
            {
                //create colored textured
                return VisimEngine.Instance.Renderer.MaterialMan.Add(mat.name, mat.DiffuseTexture, ref mat.colorvals);
            }
            return VisimEngine.Instance.Renderer.MaterialMan.Add(mat.name, ref mat.colorvals);
        }

        protected class OBJMat
        {
            public string name;
            public Renderer.StandardColorValues colorvals;
            public int ilummodel;
            public float SpecCoeff;
            public float alpha;
            public string AmbientTexture;
            public string DiffuseTexture;
            public string SpecularTexture;
            public string BumpTexture;
        }

        protected Dictionary<string, OBJMat> MatLibrary;

        ColorByte GetColor(string line)
        {
            Vector3 vec = ReadVector(line);
            if ((vec.X == float.NaN) || (vec.Y == float.NaN) || (vec.Z == float.NaN))
            {
                Console.DebugLine("OBJMAT importer: color reading failed");
                return new ColorByte(0);
            }

            return new ColorByte(vec.X, vec.Y, vec.Z); 
        }

        bool LoadMaterialLibrary(string filename)
        {
            TextReader sourcefile;            

            try
            {
                sourcefile = new StreamReader(filename);
            }
            catch
            {
                Console.DebugLine("OBJ MATLIB import failure: Can't open file " + filename);
                return false;
            }

            if (MatLibrary == null)
                MatLibrary = new Dictionary<string, OBJMat>();

            OBJMat current = null;

            string sourceline;
            while ((sourceline = sourcefile.ReadLine()) != null)
            {
                if ((sourceline == "") || (sourceline[0] == '#'))
                    continue;   //ignore comments and empty lines
                if (sourceline.StartsWith("newmtl"))
                {
                    //start new material
                    if (current != null)
                    {
                        if (current.name == null)
                        {
                            Console.DebugLine("Unknamed material! Using name defaultimported");
                            current.name = "defaultimported";
                        }
                        if (!MatLibrary.ContainsKey(current.name))
                        {
                            MatLibrary.Add(current.name, current);
                        }
                        else
                        {
                            Console.DebugLine("Material with the same name already in the library!");
                            int x = 0;
                            string newname = current.name + x.ToString();
                            while (MatLibrary.ContainsKey(newname))
                            {
                                newname = current.name + x.ToString();
                            }
                            Console.DebugLine("Renamed to " + newname);
                            current.name = newname;
                            MatLibrary.Add(newname, current);
                        }
                    }
                    current = new OBJMat();

                    try
                    {
                        current.name = sourceline.Substring(7);
                    }
                    catch
                    {
                        Console.DebugLine("OBJ importing - Wrong name for material?");
                    }
                    continue;
                }
                if (sourceline.StartsWith("Ka "))
                {
                    //ambient color
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.colorvals.Ambient = GetColor(sourceline.Substring(3));
                    continue;
                }
                if (sourceline.StartsWith("Kd "))
                {
                    //diffuse color
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.colorvals.Diffuse = GetColor(sourceline.Substring(3));
                    continue;
                }
                if (sourceline.StartsWith("Ks "))
                {
                    //specular color
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.colorvals.Specular = GetColor(sourceline.Substring(3));

                    continue;
                }
                if (sourceline.StartsWith("Ns "))
                {
                    //specular color coeficient                   
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.colorvals.Shininess = XMLHelper.FloatFromString(sourceline.Substring(3));
                    continue;
                }
                if (sourceline.StartsWith("d ") || sourceline.StartsWith("Tr "))
                {
                    //alpha value
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    string alpha;
                    if (sourceline.StartsWith("d "))
                    {
                        alpha = sourceline.Substring(2);
                    }
                    else
                    {
                        alpha = sourceline.Substring(3);
                    }

                    current.alpha = XMLHelper.FloatFromString(sourceline);
                    continue;
                }
                if (sourceline.StartsWith("illum"))
                {
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    //illumination model - ignoring for now
                    /*
                        0. Color on and Ambient off
                        1. Color on and Ambient on
                        2. Highlight on
                        3. Reflection on and Ray trace on
                        4. Transparency: Glass on, Reflection: Ray trace on
                        5. Reflection: Fresnel on and Ray trace on
                        6. Transparency: Refraction on, Reflection: Fresnel off and Ray trace on
                        7. Transparency: Refraction on, Reflection: Fresnel on and Ray trace on
                        8. Reflection on and Ray trace off
                        9. Transparency: Glass on, Reflection: Ray trace off
                        10. Casts shadows onto invisible surfaces
                        */
                    int.TryParse(sourceline.Substring(5), out current.ilummodel);
                    continue;
                }
                if (sourceline.StartsWith("map_Ka"))
                {
                    //ambient texture
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.AmbientTexture = sourceline.Substring(6);
                    continue;
                }
                if (sourceline.StartsWith("map_Kd"))
                {
                    //diffuse texture
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.DiffuseTexture = sourceline.Substring(6);
                    continue;
                }
                if (sourceline.StartsWith("map_Ks"))
                {
                    //specular texture
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.SpecularTexture = sourceline.Substring(6);
                    continue;
                }
                if (sourceline.StartsWith("map_bump"))
                {
                    //bump map
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.BumpTexture = sourceline.Substring(8);
                    continue;
                }
                if (sourceline.StartsWith("bump"))
                {
                    if (current == null)
                    {
                        Console.DebugLine("OBJ mat import - value without specified name! " + sourceline);
                        continue;
                    }
                    current.BumpTexture = sourceline.Substring(4);
                    continue;
                }
                Console.WriteLine("OBJ material importer: " + sourceline + " wasn't identified, ignoring");
            }

            sourcefile.Close();
            return true;
        }

        Vector3 ReadVector(string line)
        {
            Vector3 rval = Vector3.Invalid;

            line = line.TrimStart(new char[] {' '});
            StringParser sp = new StringParser(line); //do it nicer! this is slow and ugly
            for (int i = 0; i < 3; i++)
            {
                rval[i] = XMLHelper.FloatFromString(sp.GetWord(i)); 
            }

            return rval;
        }

        List<OBJIndice> ReadFace(string line)
        {
            line = line.TrimStart(new char[] { ' ' });
            List<OBJIndice> rval = new List<OBJIndice>();
            
            StringParser sp = new StringParser(line);

            for (int i = 0; i < sp.WordCount; i++)
            {
                StringParser sp2 = new StringParser(sp.GetWord(i), '/');
                
                int elcount = sp2.WordCount;

                switch (elcount)
                {
                    case 1:
                        //vertex only
                        rval.Add(new OBJIndice(sp2.GetInteger(0), -1, -1));
                        break;
                    case 2:
                        //vertex and normal
                        rval.Add(new OBJIndice(sp2.GetInteger(0), sp2.GetInteger(1), -1));
                        break;
                    case 3:
                        //vertex, texture, normal
                        rval.Add(new OBJIndice(sp2.GetInteger(0), sp2.GetInteger(2), sp2.GetInteger(1)));
                        break;
                    default:
                        //corrupted?!
                        Console.DebugLine("OBJ Import line " + line + " doesn't contain valid face data!");
                        break;
                }
            }

            switch (rval.Count)
            {
                case 3:
                    return rval;
                case 4:
                    List<OBJIndice>newrval = new List<OBJIndice>(6);
                    newrval.Add(rval[0]);
                    newrval.Add(rval[1]);
                    newrval.Add(rval[2]);
                    newrval.Add(rval[2]);
                    newrval.Add(rval[3]);
                    newrval.Add(rval[0]);
                    return newrval;
                default:                    
                    return null;
            }
        }    
    }
}
