using System;
using nft.core.geometry;

namespace nft.impl.game
{
	/// <summary>
	/// TerrainPiece の概要の説明です。
	/// ゲーム中で使用するため、編集には時間がかかるが、メモリサイズを節約。
	/// </summary>
	public struct TerrainPieceG : ITerrainPiece
	{
        private const ushort NE_BIT = 0x000f;
        private const ushort SE_BIT = 0x00f0;
        private const ushort SW_BIT = 0x0f00;
        private const ushort NW_BIT = 0xf000;
        private const ushort NE_MASK = 0xfff0;
        private const ushort SE_MASK = 0xff0f;
        private const ushort SW_MASK = 0xf0ff; 
		private const ushort NW_MASK = 0x0fff;
        private static readonly short[] shift = new short[] { 0, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8 };

		private short height;
		private ushort slope;
        private ushort mask;

		public TerrainPieceG(short baseHight, int ne, int se, int sw, int nw, bool convex)
		{
            this.height = baseHight;
            this.slope = 0;
            this.mask = 0;
			setSlope(ne,se,sw,nw);
		}

		public TerrainPieceG(ITerrainPiece org) {
			this.height = (short)org.BaseHeight;
            throw new NotImplementedException();
		}

        #region implementation of ITerrainPiece
        public int BaseHeight 
		{
			get{ return height>>1; }
			set
			{ 
				int tmp = value;
				if((tmp&0x4000) !=0 ) 
					tmp=value|0x3fff;
				height = (short)(tmp*2 + height&1);
			}
		}

        public int MaxHeight { 
			// Be careful! this formula is ooptimised for only 0,1,2,4,8 values.
			get{
				int s = slope|(slope>>4);
				s |= s>>16;
				return BaseHeight + shift[s&0xf];
			} 
		}

        public int MeanHeight { 
			get{
				int s = (slope&NE_BIT)+((slope&SE_BIT)>>4)+((slope&SW_BIT)>>8)+((slope&NW_BIT)>>16);
				return BaseHeight+s>>2;
			} 
		}

        public ushort OccupationMask { get { return mask; } }

        public TerrainPieceTemplate Template { get { return TerrainPieceTemplate.GetInstance(slope); } }

		public bool Convex{
			get{ return (height&1)==1; }
			set{ if(value)
					 height =(short)(height|0x1);
				 else
					 height = (short)(height&0xfffe);
			}
		}

        public int GetHeightAt(InterCardinalDirection dir) {
            return BaseHeight + GetOffsetHeightAt(dir);
		}

        public int GetOffsetHeightAt(InterCardinalDirection dir)
		{
			int i = (dir - InterCardinalDirection.NORTHEAST)*4;
            int u = (slope >> i) & 0xf;
            return (u == 0xf)?-1:u;
        }
        #endregion

		public void setSlope( int ne, int se, int sw, int nw )
		{
			int[] work = new int[]{ne,se,sw,nw};
			int b = TerrainUtil.CorrectCellVertices(ref work);
			if( b!=0 )
				BaseHeight+=b;
			int tmp = (work[0]&NE_BIT)+((work[1]&SE_BIT)<<4)+((work[2]&SW_BIT)<<8)+((work[3]&NW_BIT)<<12);
			this.slope = (ushort)tmp;
        }
    }
}
