// == 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.Scripting
{
    //! @addtogroup UVTexIntegra-Scripting名前空間
    //! @{

    public partial class ScriptMain
    {
        /// @cond <summary>面情報を取得/設定するための構造体を定義します。</summary> @endcond
        //! @brief 面情報を取得/設定するための構造体を定義します。
        public partial class Face : System.ICloneable
        {
            /// @cond <summary>あるひとつの同じUV座標を共有するFace参照データです</summary> @endcond
            //! @brief あるひとつの同じUV座標を共有するFace参照データです
            public struct ShareRelation
            {
                /// @cond <summary>あるUV座標を共有するひとつひとつの面と、その面のPoints配列の中でどの要素に一致するかを取得します。</summary> @endcond
                //! @brief あるUV座標を共有するひとつひとつの面と、その面のPoints配列の中でどの要素に一致するかを取得します。
                public interface IReference
                {
                    /// @cond <summary>あるUV座標を共有する面のひとつを参照します。</summary> @endcond
                    //! @brief あるUV座標を共有する面のひとつを参照します。
                    Face Face { get; }
                    /// @cond <summary>Face.Pointsにおける何番目のインデックスと一致しているのか取得します。</summary> @endcond
                    //! @brief Face.Pointsにおける何番目のインデックスと一致しているのか取得します。
                    int PointIndex { get; }
                    /// @cond <summary>実際に参照するPointクラスを返します。この実装は、利用者も実装者もFace[PointIndex]と等価と考えてください。Faceがnullの時例外を返す可能性があるとします。</summary> @endcond
                    //! @brief 実際に参照するPointクラスを返します。この実装は、利用者も実装者もFace[PointIndex]と等価と考えてください。Faceがnullの時例外を返す可能性があるとします。
                    Point Point { get; }
                }

                /// @cond <summary>IReferenceの読み取り専用配列として振舞います。IReferenceを列挙可能で、かつ[]によるランダムアクセスを提供します。</summary> @endcond
                //! @brief IReferenceの読み取り専用配列として振舞います。IReferenceを列挙可能で、かつ[]によるランダムアクセスを提供します。
                public interface IReferenceCollection : System.Collections.Generic.IEnumerable<IReference>
                {
                    /// @cond <summary>ランダムアクセスプロパティです。リードオンリー。</summary> @endcond
                    //! @brief ランダムアクセスプロパティです。リードオンリー。
                    IReference this[int index] { get; }
                    /// @cond <summary>個数を返します。</summary> @endcond
                    //! @brief 個数を返します。
                    int Count { get; }
                }

                /// @cond <summary>ShareRelationのコレクションを構築して返します。</summary> @endcond
                //! @brief ShareRelationのコレクションを構築して返します。
                //! @note Face[]のUV座標は書き換え可能であり、この関数が返すのは、呼び出し時点でのUV共有状態の一時的なスナップショットであることに注意してください。ShareRelationはUV座標の書き換えが生じても、変化を追跡して再構築する事はありません。
                public static System.Collections.ObjectModel.ReadOnlyCollection<ShareRelation> GetRelationsSnap(Face[] faces, float epsilon)
                {
                    System.Collections.Generic.List<ShareRelation> List = new System.Collections.Generic.List<ShareRelation>();
                    // check all Face
                    foreach (Face currentFace in faces)
                    {
                        if ((currentFace == null) || (currentFace.Points == null)) continue;
                        // check all point of Face.Points
                        for (int facePointIndex = 0; facePointIndex < currentFace.Points.Length; facePointIndex++)
                        {
                            // check a already existing point on ShareRelation collections
                            bool isBuffered = false;
                            foreach (ShareRelation relInList in List)
                            {
                                OpenTK.Vector2 sub = relInList.Coordinate - currentFace.Points[facePointIndex].Coordinate;
                                if (System.Math.Max(System.Math.Abs(sub.X), System.Math.Abs(sub.Y)) <= epsilon) {
                                    relInList.m_owners.List.Add(new Reference(currentFace, facePointIndex));
                                    isBuffered = true;
                                    break;
                                }
                            }
                            // if not already exists, a point of face is registed as new relation
                            if (isBuffered == false) List.Add(new ShareRelation(currentFace, facePointIndex));
                        }
                    }
                    return List.AsReadOnly();
                }

                /// @cond <summary>構築時に取得した、共有されるUV座標です。Ownersは全て、ShareRelation.Coordinateに対して構築時に指定された許容誤差Epsilonの範囲内にあるUV座標を持っています。</summary> @endcond
                //! @brief 構築時に取得した、共有されるUV座標です。Ownersは全て、ShareRelation.Coordinateに対して構築時に指定された許容誤差Epsilonの範囲内にあるUV座標を持っています。
                //! @note ただし、構築後に参照元のUV座標が変更される場合は、誤差範囲内にあるとは限りません。ShareRelationには構築時の共有関係をスナップショットする機能しかありません。
                public OpenTK.Vector2 Coordinate { get { return m_coordinate; } }

                /// @cond <summary>構築時に取得したCoordinateを共有する面のリストを返します。Ownersは全て、ShareRelation.Coordinateに対して構築時に指定された許容誤差Epsilonの範囲内にあるUV座標を持っています。</summary> @endcond
                //! @brief 構築時に取得したCoordinateを共有する面のリストを返します。Ownersは全て、ShareRelation.Coordinateに対して構築時に指定された許容誤差Epsilonの範囲内にあるUV座標を持っています。
                //! @note ただし、構築後に参照元のUV座標が変更される場合は、誤差範囲内にあるとは限りません。ShareRelationには構築時の共有関係をスナップショットする機能しかありません。
                public IReferenceCollection Owners { get { return m_owners; } }

                /// @cond <summary>コンストラクタ</summary> @endcond
                //! @brief コンストラクタ
                public ShareRelation(Face primaryFace, int PointIndex)
                {
                    m_coordinate = primaryFace.Points[PointIndex].Coordinate;
                    m_owners = new ReferenceCollection();
                    m_owners.List.Add(new Reference(primaryFace, PointIndex));
                }

                private class Reference : IReference
                {
                    public Face Face;
                    public int PointIndex;
                    public Point Point { get { return Face.Points[PointIndex]; } }
                    Face IReference.Face { get { return Face; } }
                    int IReference.PointIndex { get { return PointIndex; } }
                    /// @cond <summary>コンストラクタ</summary> @endcond
                    //! @brief コンストラクタ
                    public Reference(Face face, int pointIndex)
                    {
                        Face = face;
                        PointIndex = pointIndex;
                    }
                }

                private class ReferenceCollection : IReferenceCollection
                {
                    public class Enumerator : System.Collections.Generic.IEnumerator<IReference>
                    {
                        object System.Collections.IEnumerator.Current { get { return m_innerEnum.Current; } }
                        public IReference Current { get { return m_innerEnum.Current; } }
                        public void Dispose() { m_innerEnum.Dispose(); }
                        public bool MoveNext() { return m_innerEnum.MoveNext(); }
                        public void Reset() { throw new System.NotSupportedException(); }
                        public Enumerator(System.Collections.Generic.List<Reference>.Enumerator src) { m_innerEnum = src; }
                        private System.Collections.Generic.List<Reference>.Enumerator m_innerEnum;
                    }
                    public int Count { get { return List.Count; } }
                    public IReference this[int index] { get { return List[index]; } }
                    public System.Collections.Generic.IEnumerator<IReference> GetEnumerator()
                    {
                        return new Enumerator(List.GetEnumerator());
                    }
                    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
                    {
                        return new Enumerator(List.GetEnumerator());
                    }
                    public readonly System.Collections.Generic.List<Reference> List;
                    public ReferenceCollection()
                    {
                        List = new System.Collections.Generic.List<Reference>();
                    }
                }

                private OpenTK.Vector2 m_coordinate;
                private readonly ReferenceCollection m_owners;
            }
        }
    }
    //! @}
}
