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

namespace StrokeStyleT
{
	class Input : CInput管理, IDisposable
	{
		public Input( IntPtr hWnd )
			: base( hWnd )
		{
			this._listMIDI入力イベント = new List<CInputEvent>();
		}

		/// <summary>
		/// <para>MIDI入力イベントのリスト。ポーリングするたびに更新される。</para>
		/// <para>MIDI入力以外のデバイスについては、Input管理.Keyboard などを直接参照すること。</para>
		/// </summary>
		public CInputEvent[] arrMIDI入力イベント
		{
			get
			{
				// 配列に変換して返す。

				CInputEvent[] copyArray = null;

				lock( this._listMIDI入力イベント )
				{
					this.Disposed.tDispose済みまたは実行中なら例外発生( "Input" );

					copyArray = new CInputEvent[ this._listMIDI入力イベント.Count ];
					this._listMIDI入力イベント.CopyTo( copyArray );
				}
				return copyArray;
			}
		}

		public void tポーリング()
		{
			lock( this )
			{
				this.Disposed.tDispose済みまたは実行中なら例外発生( "Folders" );

				// 時刻補正用パラメータを更新。

				this.n前回のポーリングPCシステム時刻ms = this.n今回のポーリングPCシステム時刻ms;
				this.n今回のポーリングPCシステム時刻ms = this.tmポーリング用PCタイマ.nシステム時刻ms;
				this.n前回のポーリングST現在時刻ms = this.n今回のポーリングST現在時刻ms;
				this.n今回のポーリングST現在時刻ms = ( Global.SoundTimer != null ) ? Global.SoundTimer.n現在時刻ms : 0;
			}

			// ポーリングを行う。

			base.tポーリング( Global.bWindowがActive中, bバッファ入力を使用する: true );


			// ポーリングデータから MIDI入力 だけを拾い上げ、listMIDI入力イベント に格納する。

			lock( this._listMIDI入力イベント )
			{
				this._listMIDI入力イベント.Clear();	// ポーリングごとにクリアする。

				for( int nID = 0; nID < this.nMidiIn数; nID++ )
				{
					IInputDevice dev = this.MidiIn( nID );
					foreach( var ie in dev.list入力イベント )
					{
						ie.nTimeStamp = ( ie.nTimeStamp - n前回のポーリングPCシステム時刻ms ) + n前回のポーリングST現在時刻ms;	// PCシステム時刻をSoundTimerでの現在時刻に変換。
						this._listMIDI入力イベント.Add( ie );
					}
				}
			}
		}

		#region [ Dispose-Finalize パターン ]
		//-------------------------
		public new void Dispose()
		{
			if( this.Disposed.bDispose済みまたは処理中である )
				return;		// 例外を発出しない

			try
			{
				if( !this.Disposed.t開始を宣言する() )
					return;		// 例外を発出しない

				this.Dispose( true );
				GC.SuppressFinalize( this );
			}
			finally
			{
				base.Dispose();
			}
		}
		//~Input() { this.Dispose( false ); }		Unmanaged がないのでファイナライザは無し
		protected new void Dispose( bool bReleaseManaged )
		{
			if( bReleaseManaged )
			{
				// TODO: Managed リソースの解放

				lock( this._listMIDI入力イベント )
				{
					if( 0 < this._listMIDI入力イベント.Count )
					{
						foreach( var ev in this._listMIDI入力イベント )
							ev.Dispose();

						this._listMIDI入力イベント.Clear();
					}
				}

				if( null != this.tmポーリング用PCタイマ )
				{
					this.tmポーリング用PCタイマ.Dispose();
					this.tmポーリング用PCタイマ = null;
				}
			}

			// TODO: Unmanaged リソースの解放
		}
		//-------------------------
		#endregion

		// tポーリング() の直後に呼び出すこと。
		public void tドラムでFormを操作するためのポーリング( Form form )
		{
			this.Disposed.tDispose済みまたは実行中なら例外発生( "Folders" );

			// this._listMIDI入力イベントを長くlockしたくないので、現時点のリスト内容を配列で取得する。
			CInputEvent[] events = this.arrMIDI入力イベント;

			foreach( var ie in events )
			{
				if( !ie.b押された )
					continue;		// 押されていないキーは無視。

				if( this.bハイタム類のいずれかが押された )
				{
					#region [ 現在フォーカスされているコントロールを取得。]
					//-----------------
					Control active = form.ActiveControl;

					if( null == active )
						break;
					//-----------------
					#endregion
					#region [ (A) RadioButton … 前に移動する。]
					//-----------------
					if( null != ( active as RadioButton ) )
						SendKeys.SendWait( "{UP}" );
					//-----------------
					#endregion
					#region [ (B) CheckBox … チェックを反転する。]
					//-----------------
					else if( null != ( active as CheckBox ) )
						( (CheckBox) active ).Checked = !( (CheckBox) active ).Checked;
					//-----------------
					#endregion
					#region [ (C) NumericUpDown … 値を増やす。]
					//-----------------
					else if( null != ( active as NumericUpDown ) )
						( (NumericUpDown) active ).UpButton();
					//-----------------
					#endregion
					#region [ (D) TabControl … タブを次に移動。]
					//-----------------
					else if( null != ( active as TabControl ) )
					{
						var tabCtrl = active as TabControl;
						int nタブ番号 = tabCtrl.SelectedIndex + 1;

						if( nタブ番号 >= tabCtrl.TabPages.Count )
							nタブ番号--;

						tabCtrl.SelectedIndex = nタブ番号;
					}
					//-----------------
					#endregion
					#region [ (E) ListView … 次のアイテムにカーソルとフォーカスを移動。]
					//-----------------
					else if( null != ( active as ListView ) )
					{
						var listView = active as ListView;

						if( 0 < listView.Items.Count )	// 念のため
						{
							int n = listView.Items.IndexOf( listView.FocusedItem );

							if( 0 <= ( n - 1 ) )
							{
								listView.Items[ n - 1 ].Focused = true;
								listView.Items[ n - 1 ].Selected = true;
							}
						}
					}
					//-----------------
					#endregion
				}
				if( this.bロータム類のいずれかが押された )
				{
					#region [ 現在フォーカスされているコントロールを取得。]
					//-----------------
					Control active = form.ActiveControl;

					if( null == active )
						break;
					//-----------------
					#endregion
					#region [ (A) RadioButton … 次に移動する。]
					//-----------------
					if( null != ( active as RadioButton ) )
					{
						SendKeys.SendWait( "{DOWN}" );
					}
					//-----------------
					#endregion
					#region [ (B) CheckBox … チェックを反転する。]
					//-----------------
					else if( null != ( active as CheckBox ) )
					{
						( (CheckBox) active ).Checked = !( (CheckBox) active ).Checked;
					}
					//-----------------
					#endregion
					#region [ (C) NumericUpDown … 値を減らす。]
					//-----------------
					else if( null != ( active as NumericUpDown ) )
					{
						( (NumericUpDown) active ).DownButton();
					}
					//-----------------
					#endregion
					#region [ (D) TabControl … タブを前に移動 ]
					//-----------------
					else if( null != ( active as TabControl ) )
					{
						var tabCtrl = active as TabControl;
						int nタブ番号 = tabCtrl.SelectedIndex - 1;

						if( 0 > nタブ番号 )
							nタブ番号++;

						tabCtrl.SelectedIndex = nタブ番号;
					}
					//-----------------
					#endregion
					#region [ (E) ListView … 次のアイテムにカーソルとフォーカスを移動。]
					//-----------------
					else if( null != ( active as ListView ) )
					{
						var listView = active as ListView;

						if( 0 < listView.Items.Count )	// 念のため
						{
							int n = listView.Items.IndexOf( listView.FocusedItem );

							if( ( n + 1 ) < listView.Items.Count )
							{
								listView.Items[ n + 1 ].Focused = true;
								listView.Items[ n + 1 ].Selected = true;
							}
						}
					}
					//-----------------
					#endregion
				}
				if( this.bスネア類のいずれかが押された )
				{
					#region [ フォーカスを前に移動 ]
					//-----------------
					SendKeys.SendWait( "+{TAB}" );	// Shift+TAB
					//-----------------
					#endregion
				}
				if( this.bフロアタム類のいずれかが押された )
				{
					#region [ フォーカスを次に移動 ]
					//-----------------
					SendKeys.SendWait( "{TAB}" );
					//-----------------
					#endregion
				}
				if( this.bシンバル類のいずれかが押された )
				{
					#region [ アクティブコントロールを取得。]
					//-----------------
					Control active = form.ActiveControl;

					if( null == active )
						break;
					//-----------------
					#endregion
					#region [ (A) CheckBox … チェックを反転する。]
					//-----------------
					else if( null != ( active as CheckBox ) )
					{
						( (CheckBox) active ).Checked = !( (CheckBox) active ).Checked;
					}
					//-----------------
					#endregion
					#region [ (その他) 確定 ]
					//-----------------
					form.AcceptButton.PerformClick();
					//-----------------
					#endregion
				}
			}
		}
		
		public bool bパッドが押された( Eドラム入力 eドラム入力 )
		{
			lock( this._listMIDI入力イベント )
			{
				this.Disposed.tDispose済みまたは実行中なら例外発生( "Folders" );

				foreach( var ev in this._listMIDI入力イベント )
				{
					foreach( var inputNote in Global.Config.PadAssign.dicドラム入力to入力ノートリスト[ eドラム入力 ] )
					{
						if( ( ev.ID == inputNote.nデバイスID ) && ( ev.nKey == inputNote.nノート番号 ) )
							return true;
					}
				}
			}

			return false;
		}
		public bool bシンバル類のいずれかが押された
		{
			get
			{
				return
					this.bパッドが押された( Eドラム入力.China ) ||
					this.bパッドが押された( Eドラム入力.LeftCrash ) ||
					this.bパッドが押された( Eドラム入力.Ride ) ||
					this.bパッドが押された( Eドラム入力.RideCup ) ||
					this.bパッドが押された( Eドラム入力.RightCrash ) ||
					this.bパッドが押された( Eドラム入力.Splash );
			}
		}
		public bool bスネア類のいずれかが押された
		{
			get
			{
				return
					this.bパッドが押された( Eドラム入力.Snare ) ||
					this.bパッドが押された( Eドラム入力.SnareClosedRim ) ||
					this.bパッドが押された( Eドラム入力.SnareOpenRim );
			}
		}
		public bool bハイタム類のいずれかが押された
		{
			get
			{
				return
					this.bパッドが押された( Eドラム入力.Tom1 ) ||
					this.bパッドが押された( Eドラム入力.Tom1Rim );
			}
		}
		public bool bロータム類のいずれかが押された
		{
			get
			{
				return
					this.bパッドが押された( Eドラム入力.Tom2 ) ||
					this.bパッドが押された( Eドラム入力.Tom2Rim );
			}
		}
		public bool bフロアタム類のいずれかが押された
		{
			get
			{
				return
					this.bパッドが押された( Eドラム入力.Tom3 ) ||
					this.bパッドが押された( Eドラム入力.Tom3Rim );
			}
		}
		public bool bハイハット類のいずれかが押された
		{
			get
			{
				return
					this.bパッドが押された( Eドラム入力.HiHatClose ) ||
					this.bパッドが押された( Eドラム入力.FootPedal ) ||
					this.bパッドが押された( Eドラム入力.HiHatOpen );
			}
		}

		private CTimer tmポーリング用PCタイマ = new CTimer( CTimer.E種別.MultiMedia );
	
		private long n前回のポーリングPCシステム時刻ms = 0;
		private long n今回のポーリングPCシステム時刻ms = 0;
		private long n前回のポーリングST現在時刻ms = 0;
		private long n今回のポーリングST現在時刻ms = 0;

		private readonly CDisposed Disposed = new CDisposed();

		private readonly List<CInputEvent> _listMIDI入力イベント = new List<CInputEvent>( 32 );
	}
}
