// == LICENSE INFORMATION ==
/*
 * First author tiritomato 2013.
 * This program is distributed under the GNU General Public License(GPL).
 * support blog (Japanese only) http://d.hatena.ne.jp/tiri_tomato/
 */
// == LICENSE INFORMATION ==

namespace UVTexIntegra.CustomControls
{
    public static partial class Drawing
    {
        //! @brief 面情報バッファ
        //! @details 計算済みのScripting.Face配列を元に、辺の共有情報を付与した読み取り専用のバッファを作ります。
        //! Scripting.Face情報のうち、レンダリングに必要ないデータがディープコピー時に切り捨てられ、代わりに辺の共有情報が追加されます。
        //! データや領域の再利用は想定しておらず、クリア系処理はありません。コンストラクトで一発リスト構築、使ったら即破棄しかできない仕様です。
        //! これは、パフォーマンスを言い出したらそれこそC++で実装するべきという理由から、
        //! その代わりに極端にC#らしい簡潔なコード・ラップしまくりで低速・メモリバカ食いで、アプリケーションスクリプトという割り切りの元「酷いが最低限」のロジックにしてあります。
        public class FaceBuffer
        {
            //! @brief UV座標の一致判定の閾値です。u,vそれぞれの軸で差が閾値より小さければ一致とみなします。デフォルトではfloat.Epsilonが設定されます。 
            public float Epsilon;
            public System.Collections.ObjectModel.ReadOnlyCollection<Face> Faces { get { return m_faces.AsReadOnly(); } }
            public System.Collections.ObjectModel.ReadOnlyCollection<Edge> Edges { get { return m_edges.AsReadOnly(); } }

            //! @brief　デフォルトコンストラクタ
            //! @details 初期値を設定します。
            public FaceBuffer()
            {
                Epsilon = float.Epsilon;
                m_faces = new System.Collections.Generic.List<Face>();
                m_edges = new System.Collections.Generic.List<Edge>();
            }

            //! @brief 面情報バッファを構築します。
            //! @details @exception System.NullReferenceException src が nullです。
            public FaceBuffer(Scripting.ScriptMain.Face[] src)
                : this()
            {
                foreach (Scripting.ScriptMain.Face addItem in src) AddFace(addItem);
            }

            private System.Collections.Generic.List<Face> m_faces;
            private System.Collections.Generic.List<Edge> m_edges;

            private void AddFace(Scripting.ScriptMain.Face src)
            {
                if ((src == null) || (src.Points == null) || (src.Points.Length < 3)) return;

                Face addItem = new Face(src);
                m_faces.Add(addItem);

                if (src.Points.Length == 3)
                {
                        // triangle edge 0-1/1-2/2-0 gravity(0-1-2)
                        OpenTK.Vector2 g = src.Points[0].Coordinate;
                        g += src.Points[1].Coordinate;
                        g += src.Points[2].Coordinate;
                        g /= 3.0f;

                        for (int index = 0; index < 3; index++)
                        {
                            int indexNext = (index < 2 ? index + 1 : 0);
                            int EdgeBufferIndex = m_edges.GetRegistedEdgeIndex(ref src.Points[index].Coordinate, ref src.Points[indexNext].Coordinate, Epsilon);
                            if (0 <= EdgeBufferIndex) m_edges[EdgeBufferIndex].OwnerInc();
                            else m_edges.Add(new Edge(ref src.Points[index].Coordinate, src.Points[index].Color, ref src.Points[indexNext].Coordinate, src.Points[indexNext].Color, ref g));
                        }
                    /*
                    catch (System.Exception ex)
                    {
                        System.Windows.Forms.MessageBox.Show(DotNetEx.String.Extention.ToNestedMultilineString(ex));
                        throw new System.Exception("rethrow", ex);
                    }*/
                }
                else
                {
                    // fan edge
                    // 0-1 gravity(0-1-2)
                    // 1-2 gravity(0-1-2), 2-3 gravity(0-2-3), 3-4 gravity(0-3-4)
                    // 4-0 gravity(0-3-4)
                    {
                        int EdgeBufferIndex = m_edges.GetRegistedEdgeIndex(ref src.Points[0].Coordinate, ref src.Points[1].Coordinate, Epsilon);
                        if (0 <= EdgeBufferIndex) m_edges[EdgeBufferIndex].OwnerInc();
                        else
                        {
                            OpenTK.Vector2 g = src.Points[0].Coordinate;
                            g += src.Points[1].Coordinate;
                            g += src.Points[2].Coordinate;
                            g /= 3.0f;
                            m_edges.Add(new Edge(ref src.Points[0].Coordinate, src.Points[0].Color, ref src.Points[1].Coordinate, src.Points[1].Color, ref g));
                        }
                    }
                    for (int index = 1; (index + 1) < src.Points.Length; index++)
                    {
                        int EdgeBufferIndex = m_edges.GetRegistedEdgeIndex(ref src.Points[index].Coordinate, ref src.Points[index + 1].Coordinate, Epsilon);
                        if (0 <= EdgeBufferIndex) m_edges[EdgeBufferIndex].OwnerInc();
                        else
                        {
                            OpenTK.Vector2 g = src.Points[0].Coordinate;
                            g += src.Points[index].Coordinate;
                            g += src.Points[index+1].Coordinate;
                            g /= 3.0f;
                            m_edges.Add(new Edge(ref src.Points[index].Coordinate, src.Points[index].Color, ref src.Points[index + 1].Coordinate, src.Points[index + 1].Color, ref g));
                        }
                    }
                    {
                        int firstIndex = src.Points.Length - 1;
                        int nextIndex = 0;
                        int EdgeBufferIndex = m_edges.GetRegistedEdgeIndex(ref src.Points[firstIndex].Coordinate, ref src.Points[nextIndex].Coordinate, Epsilon);
                        if (0 <= EdgeBufferIndex) m_edges[EdgeBufferIndex].OwnerInc();
                        else
                        {
                            OpenTK.Vector2 g = src.Points[0].Coordinate;
                            g += src.Points[firstIndex - 1].Coordinate;
                            g += src.Points[firstIndex].Coordinate;
                            g /= 3.0f;
                            m_edges.Add(new Edge(ref src.Points[firstIndex].Coordinate, src.Points[firstIndex].Color, ref src.Points[nextIndex].Coordinate, src.Points[nextIndex].Color, ref g));
                        }
                    }
                }
            }
            

            



        }

    }
}
