using System;
using System.Collections.Generic;
using Box2DX.Common;
using Box2DX.Dynamics;
using DxLibDLL;
using Nlgp1.B2DX;
using Nlgp1.Common;
using Nlgp1.Drawers;
using Nlgp1.Stages;
using Nlgp1.Utilities;

namespace Nlgp1.Sprites {
	/// <summary>
	/// スプライトを表す。
	/// </summary>
	public class Sprite
	{

		#region クラス変数
		/// <summary>
		/// デバッグ用クラス変数、最初の1度目でフラグがおられる、Start()相当
		/// </summary>
		bool first = true;
		/// <summary>
		/// 剛体の定義
		/// </summary>
		public RigidBody Body;

		const float liniervelocity = 0.2f, restitution = 0, density = 0.1f;
		#endregion
		/// <summary>
		/// Spriteのコンストラクタ
		/// </summary>
		/// <param name="world">Worldオブジェクト</param>
		/// <param name="chipId">識別ID</param>
		/// <param name="friction">摩擦力</param>
		/// <param name="location">現在の座標</param>
		public Sprite( World world , ChipId chipId , double friction , Vec2 location ) {
			this.World = world;
			this.ChipId = chipId;
			this.Location = location;
			#region デバッグ
			this.DebugId = chipId.ToString();
			#endregion
			#region コレクション
			lock ( spriteList )
				spriteList.Add( this );
			#endregion
			
			switch (chipId.LayerType)
			{
				case LayerType.Characters:
					Body = new DynamicRigid(location, 0, liniervelocity, 0);
					Body.SetAsBox(16, 16, new Polygondef2((float)friction, restitution, density, new Nlgp1UserData(this)), false);
					Body.NoRotate = true;
					Body.MakeBody(world);
					break;
				case LayerType.Front:
					Body = new StaticRigid(location, 0, 0, 0);
					Body.SetAsBox(16, 16, new Polygondef2((float)friction, restitution, 0, new Nlgp1UserData(this)), false);
					Body.MakeBody(world);
					break;
				default:
					break;
			}

		}

		/// <summary>
		/// ステージから削除される際に呼ばれます
		/// </summary>
		public void Unload() {
			//Body = null;

		}

		/// <summary>
		/// 1フレーム毎に呼ばれます
		/// </summary>
		public void Update() {
			List<Body> bodylist = new List<Body>();
			//何かやたら無意味なものになっちゃった、GetNext()はnullを返さないことが分かった
			//ちなみにspriteとbodyの数は必ずしも一致しない、一つのspriteが複数のBodyもっちゃうこともあるし
			//for (Body selfbody = World.GetBodyList(); selfbody != null||first; selfbody.GetNext())
			//{
			//	bodylist.Add(selfbody);
			//	if (bodylist.Count > 650000) break;
			//}
			first = false;
			if (Body!=null)
			{
				if (Body.GetType().Equals(typeof(DynamicRigid)))
				{
					((DynamicRigid)Body).ApplyForce(new Vec2(0, 3));
				}
			}
		}

		/// <summary>
		/// チップの種類を識別するIDを取得します。
		/// </summary>
		public ChipId ChipId
		{
			get;
			private set;
		}

		/// <summary>
		/// Worldオブジェクトを取得します。
		/// </summary>
		public World World {
			get;
			private set;
		}

		/// <summary>
		/// 現在の座標を取得します。
		/// </summary>
		public Vec2 Location {
			get;
			private set;
		}

		#region デバッグ
		/// <summary>
		/// デバッグ用 識別子 (通常使わない)
		/// </summary>
		public string DebugId {
			get;
			private set;
		}
		public override string ToString() {
			if( this.DebugId != null ) {
				return this.DebugId;
			}
			else {
				return base.ToString();
			}
		}
		#endregion

		#region コレクション
		/// <summary>
		/// ステージを読み込みます。
		/// </summary>
		/// <param name="mapHeight">シートの縦幅</param>
		/// <param name="mapWidth">シートの横幅</param>
		public static void LoadStage( int mapHeight , int mapWidth ) {
			spriteList = new List< Sprite >( mapHeight * mapWidth );
		}
		/// <summary>
		/// 生存するスプライトをコレクションで取得します。
		/// </summary>
		/// <returns></returns>
		public static IEnumerable<Sprite> GetCollection() {
			lock( spriteList ) {
				int count = spriteList.Count;
				foreach( Sprite sprite in spriteList ) {
					yield return sprite;
					if( spriteList.Count != count )
						yield break;
				}
			}
		}
		/// <summary>
		/// すべてのオブジェクトを削除します。
		/// </summary>
		public static void ClearCollection() {
			lock( spriteList ) {
				foreach( var sprite in spriteList )
					sprite.Unload();
				spriteList.Clear();
			}
		}

		private static List< Sprite > spriteList;
		#endregion
	}
}