using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using FDK;

namespace StrokeStyleT
{
	abstract class CActGUIBase : CActivity
	{
		// プロパティ

		/// <summary>親要素。要素ツリーのルートなら null 。</summary>
		public CActGUIBase Parent
		{
			get
			{
				return this._Parent;
			}
			private set
			{
				this._Parent = value;
			}
		}

		/// <summary>この要素のクライアント座標（親内部における相対座標）。</summary>
		public Point Location 
		{
			get
			{
				return this._Location;
			}
			set
			{
				this._Location = value;
			}
		}
		
		/// <summary>この要素のサイズ。</summary>
		public Size Size 
		{
			get
			{
				return this._Size;
			}
			set
			{
				this._Size = value;
			}
		}


		// イベントハンドラ
		//
		// カーソルがコントロールの上にあるときにマウスボタンを押すと、通常はコントロールから次の順番でイベントが発生します。
		// （MSDN; http://msdn.microsoft.com/ja-jp/library/system.windows.forms.control.mouseclick%28v=vs.110%29.aspx ）
		//    1. MouseDown イベント
		//    2. Click イベント
		//    3. MouseClick イベント
		//    4. MouseUp イベント

		public event EventHandler MouseDown = null;
		public event EventHandler Click = null;
		public event EventHandler MouseClick = null;
		public event EventHandler MouseUp = null;
		public event EventHandler MouseHover = null;
		public event EventHandler MouseEnter = null;
		public event EventHandler MouseLeave = null;


		// コンストラクタ

		public CActGUIBase( CActGUIBase Parent = null )
		{
			this._Parent = Parent;
			this._Location = new Point( 0, 0 );
			this._Size = new Size( 0, 0 );
		}


		// CActivity オーバーライド

		protected override int On進行()
		{
			if( this.b活性化してない )
				return -1;

			// 子GUI の呼び出し｡
			foreach( var child in this.list子Activities )
				child.t進行();

			return 0;
		}
		protected override void On描画前()
		{
			if( this.b活性化してない )
				return;


			// 子GUIの呼び出し。

			foreach( var child in this.list子Activities )
				child.t描画前();


			// MouseHover, MouseEnter, MouseLeave イベントの発火有無の確認。

			if( this.bカーソルが上にある )
			{
				if( !this.bMouseHover中 && null != this.MouseEnter )
					this.MouseEnter( this, new EventArgs() );

				this.bMouseHover中 = true;

				if( null != this.MouseHover )
					this.MouseHover( this, new EventArgs() );
			}
			else
			{
				if( this.bMouseHover中 && null != this.MouseLeave )
					this.MouseLeave( this, new EventArgs() );

				this.bMouseHover中 = false;
			}
		}
		protected override void On描画( IntPtr hDevice )
		{
			if( this.b活性化してない )
				return;


			// 子GUIの呼び出し。後から Add したほど上に描画されることになる。

			foreach( var child in this.list子Activities )
				child.t描画( hDevice );
		}
		

		// ウィンドウから親に送られてくるイベント。MouseHover/Enter/Leave は自前で判定し発火する。
		public void OnWindowMouseDown()
		{
			this.tマウスが上にあるときのウィンドウイベントの再帰処理( ( act ) => { return act.MouseDown; } );
		}
		public void OnWindowClick()
		{
			this.tマウスが上にあるときのウィンドウイベントの再帰処理( ( act ) => { return act.Click; } );
		}
		public void OnWindowMouseClick()
		{
			this.tマウスが上にあるときのウィンドウイベントの再帰処理( ( act ) => { return act.MouseClick; } );
		}
		public void OnWindowMouseUp()
		{
			this.tマウスが上にあるときのウィンドウイベントの再帰処理( ( act ) => { return act.MouseUp; } );
		}


		// Windowを使うため、描画スレッドからしか呼び出せないので注意。
		protected bool bカーソルが上にある
		{
			get
			{
				Point ptウィンドウ内位置 = Program.App.Window.PointToClient( Cursor.Position );
				Point pt親GUI内位置 = ptウィンドウ内位置;

				if( null != this._Parent )
					pt親GUI内位置 -= new Size( this._Parent.Location );

				return new Rectangle( this._Location.X, this._Location.Y, this.Size.Width, this.Size.Height ).Contains( pt親GUI内位置 );
			}
		}
		protected bool bMouseHover中 = false;


		private delegate EventHandler DGEventHandler( CActGUIBase act );
		private void tマウスが上にあるときのウィンドウイベントの再帰処理( DGEventHandler dgEventHandler )
		{
			Debug.Assert( null == this.Parent );
			Debug.Assert( this.b活性化してる );


			// 子クラスについてイベントチェックを行う。

			bool b子にイベントを伝搬した = false;

			for( int i = this.list子Activities.Count - 1; i >= 0; i-- )		// チェックは後ろ（＝上）から。
			{
				var child = this.list子Activities[ i ] as CActGUIBase;
				Debug.Assert( child != null );

				if( child.bカーソルが上にある )
				{
					// 子のイベントハンドラを呼び出す。

					if( null != dgEventHandler( child ) )
						dgEventHandler( child )( child, new EventArgs() );


					// さらに、sender を child にして親（＝自分）のイベントハンドラも呼び出す。

					if( null != dgEventHandler( this ) )
						dgEventHandler( this )( child, new EventArgs() );


					// 子や親のイベントハンドラを呼び出せたか否かに関係無く、ここでチェック終了。

					b子にイベントを伝搬した = true;
					break;
				}
			}

			// 子にイベントを伝搬しなかったら、自分についてイベントチェックする｡

			if( !b子にイベントを伝搬した )
			{
				if( this.bカーソルが上にある && null != dgEventHandler( this ) )
					dgEventHandler( this )( this, new EventArgs() );		// sender を this にして自分のイベントハンドラを呼び出す。
			}
		}


		// 描画ユーティリティ

		public static void t角の丸い四角を描画する( Graphics g, Brush brush, int x, int y, int width, int height, int margin )
		{
			if( width < margin * 2 || height < margin * 2 )
			{
				// 小さすぎたら角は丸めない
				g.FillRectangle( brush, x, y, width, height );
			}
			else
			{
				g.FillRectangle( brush, x + margin, y + margin, width - margin * 2, height - margin * 2 );
				g.FillRectangle( brush, x, y + margin, margin, height - margin * 2 );
				g.FillRectangle( brush, x + width - margin, y + margin, margin, height - margin * 2 );
				g.FillRectangle( brush, x + margin, y, width - margin * 2, margin );
				g.FillRectangle( brush, x + margin, y + height - margin, width - margin * 2, margin );
				g.FillPie( brush, new Rectangle( x, y, margin * 2, margin * 2 ), 180f, 90f );											// 左上
				g.FillPie( brush, new Rectangle( x + width - margin * 2, y, margin * 2, margin * 2 ), 270f, 90f );						// 右上
				g.FillPie( brush, new Rectangle( x, y + height - margin * 2, margin * 2, margin * 2 ), 90f, 90f );						// 左下
				g.FillPie( brush, new Rectangle( x + width - margin * 2, y + height - margin * 2, margin * 2, margin * 2 ), 0f, 90f );	// 右下
			}
		}
		public static void t親パネル相対位置で描画する( IntPtr hLockedDevice, CActGUIBase child, CTexture texture )
		{
			if( null == texture )
				return;

			Point pt = child.Location;

			if( null != child.Parent )
				pt += new Size( child.Parent.Location );

			texture.t2D描画( hLockedDevice, pt );
		}


		#region [ private ]
		//-------------------------
		private CActGUIBase _Parent;
		private Point _Location;
		private Size _Size;
		//-------------------------
		#endregion
	}
}
