using nft.core.geometry;
using nft.core.structure;
using nft.core.view;
using System;
using System.Collections.Generic;
using System.Text;
using ConditionedTextureSrc = nft.core.view.ConditionedResource<nft.core.graphics.TextureSrc>;

namespace nft.core.graphics {
    public abstract class DirectionalTextureSet : ICatalogSource<ConditionedTextureSrc> {
        public static DirectionalTextureSet Create(params ConditionedTextureSrc[] _textures) {
            switch(_textures.Length){
                case 1:
                    return new Rotation4FoldSymmetricTextureSet(_textures[0]);
                case 2:
                    return new Rotation2FoldSymmetricTextureSet(_textures[0], _textures[1]);
                case 4:
                    if (_textures[0] == _textures[2] && _textures[1] == _textures[3]) {
                        if (_textures[0] == _textures[1]) {
                            return new Rotation4FoldSymmetricTextureSet(_textures[0]);
                        } else {
                            return new Rotation2FoldSymmetricTextureSet(_textures[0], _textures[1]);
                        }
                    } else {
                        if (_textures[2] == null && _textures[3] == null) {
                            if (_textures[1] == null) {
                                return new Rotation4FoldSymmetricTextureSet(_textures[0]);
                            } else {
                                return new Rotation2FoldSymmetricTextureSet(_textures[0], _textures[1]);
                            }                           
                        } else {
                            return new DefaultDirectionalTextureSet(_textures);
                        }
                    }
                default:
                    string msg = String.Format("Cannot create from the array with {0} elements (only 1,2,4 is acceptable).", _textures.Length);
                    throw new ArgumentException(msg, "_textrues");
            }
        }

        public virtual ConditionedTextureSrc this[InterCardinalDirection upperDir] {
            get { 
                //return this[Direction.ToZeroBaseIndex(upperDir)];
                // 本来は↑とするのが正しいが、無駄な計算を省くため簡略化。
                // ToZeroBaseIndexは4を引くだけなので、4の剰余を採ればどのみち結果は同じ。
                return this[(int)upperDir]; 
            }
        }
        /// <summary>
        /// Returns TextureSrc for specific rotation.
        /// </summary>
        /// <param name="rotation">Rotation step, 1 for 90 degrees.</param>
        /// <returns></returns>
        public abstract ConditionedTextureSrc this[int rotation] { get; }

        public abstract int CatalogVariableSize { get; }

        protected abstract ConditionedTextureSrc[] ArrayForCatalogVariable { get; }

        public virtual IEnumerable<KeyValuePair<string, ICatalogVariable>> CatalogVariables {
            get {
                ConditionedTextureSrc[] array = ArrayForCatalogVariable;
                if (array != null && CatalogVariableSize > 1) {
                    ICatalogVariable var = new IndexVariable(CatalogVariableSize, true);
                    // デフォルトでは、回転パターンの方角をカタログ変数として返す
                    yield return new KeyValuePair<string, ICatalogVariable>(CatalogVarNameConstants.Direction, var);
                } else {
                    yield break;
                }
            }
        }

        public virtual bool Select(CatalogVariableCollection.ValueMap valueMap, out ConditionedTextureSrc result, out ICatalogSource<ConditionedTextureSrc> child) {
            //ConditionedTextureSrc[] array = ArrayForCatalogVariable;
            int idx = (int)valueMap[CatalogVarNameConstants.Direction];
            child = null;
            result = this[idx];
            return result!=null;
        }
    }

    public class DefaultDirectionalTextureSet : DirectionalTextureSet {
        protected ConditionedTextureSrc[] textures;
        public DefaultDirectionalTextureSet(params ConditionedTextureSrc[] _textures) {
            if (_textures.Length != 4) {
                throw new ArgumentException("the Array must has 4 elements.", "_textrues");
            }
            this.textures = _textures;
        }

        public override ConditionedTextureSrc this[int rotation] { 
            get{
                return textures[rotation&3];
            }
        }

        public override int CatalogVariableSize { get { return 4; } }

        protected override ConditionedTextureSrc[] ArrayForCatalogVariable {
            get { return textures; }
        }
    }

    /// <summary>
    /// 東西方向と南北方向の２パターンで構成されるテクスチャ
    /// </summary>
    public class Rotation2FoldSymmetricTextureSet : DirectionalTextureSet {
        protected ConditionedTextureSrc[] textures;
        public Rotation2FoldSymmetricTextureSet(params ConditionedTextureSrc[] _textures) {
            if (_textures.Length != 2) {
                throw new ArgumentException("the Array must has 2 elements.", "_textrues");
            }
            this.textures = _textures;
        }
        public override ConditionedTextureSrc this[int rotation] {
            get {
                return textures[rotation & 1];
            }
        }
        public override int CatalogVariableSize { get { return 2; } }
        protected override ConditionedTextureSrc[] ArrayForCatalogVariable {
            get { return textures; }
        }
    }

    /// <summary>
    /// ４方向どこから見ても同じテクスチャ
    /// </summary>
    public class Rotation4FoldSymmetricTextureSet : DirectionalTextureSet {
        protected ConditionedTextureSrc texture;

        public Rotation4FoldSymmetricTextureSet(ConditionedTextureSrc _texture) {
            this.texture = _texture;
        }

        public override ConditionedTextureSrc this[int rotation] {
            get { return texture; }
        }

        public override IEnumerable<KeyValuePair<string, ICatalogVariable>> CatalogVariables {
            get {
                yield break;
            }
        }
        public override int CatalogVariableSize { get { return 0; } }
        protected override ConditionedTextureSrc[] ArrayForCatalogVariable {
            // このクラスは CatalogVariablesを返さないので、このメソッドも呼ばれないはず。
            get { throw new NotSupportedException(); }
        }

        public override bool Select(CatalogVariableCollection.ValueMap valueMap, out ConditionedTextureSrc result, out ICatalogSource<ConditionedTextureSrc> child) {
            result = texture;
            child = null;
            return true;
        }
    }

}
