using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Drawing;
using FDK;

namespace StrokeStyleT
{
	class Cチップ : CActivity, IComparable<Cチップ>
	{
		public Cチップ()
		{
			lock( this )
			{
				this._eチップ種別 = Eチップ種別.Unknown;
				this._n小節番号 = -1;
				this._n小節内位置 = 0;
				this._n小節解像度 = 1;
				this._n描画時刻ms = CTimer.n未使用;
				this._n発声時刻ms = CTimer.n未使用;
				this._n音量 = Cチップ.n最大音量;
				this._dbBPM = 120.0;

				this._n譜面内絶対位置grid = 0;
				this._bドラッグ操作により選択中 = false;
				this._b選択が確定している = false;
				this._b移動済 = true;
				this._stgチップ内文字列 = null;
				this._n枠外レーン数 = 0;

				this._b可視 = true;
				this._bヒット済み = false;
				this._b発声済み = false;
			}
		}
		public Cチップ( Cチップ chipコピー元 )
		{
			// static 以外のメンバをコピー。

			lock( this )
			{
				lock( chipコピー元 )
				{
					this._eチップ種別 = chipコピー元.eチップ種別;
					this._n小節番号 = chipコピー元.n小節番号;
					this._n小節内位置 = chipコピー元.n小節内位置;
					this._n小節解像度 = chipコピー元.n小節解像度;
					this._n描画時刻ms = chipコピー元.n描画時刻ms;
					this._n発声時刻ms = chipコピー元.n発声時刻ms;
					this._n音量 = chipコピー元.n音量;
					this._dbBPM = chipコピー元.dbBPM;

					this._n譜面内絶対位置grid = chipコピー元.n譜面内絶対位置grid;
					this._bドラッグ操作により選択中 = chipコピー元.bドラッグ操作により選択中;
					this._b選択が確定している = chipコピー元.b選択が確定している;
					this._b移動済 = chipコピー元.b移動済;
					this._stgチップ内文字列 = chipコピー元.stgチップ内文字列;
					this._n枠外レーン数 = chipコピー元.n枠外レーン数;

					this._b可視 = chipコピー元.b可視;
					this._bヒット済み = chipコピー元.bヒット済み;
					this._b発声済み = chipコピー元.b発声済み;
				}
			}
		}

		#region [ IComparable 実装 ]
		//-----------------
		// 概要:
		//     現在のインスタンスを同じ型の別のオブジェクトと比較して、並べ替え順序において、現在のインスタンスの位置が同じ型の別のオブジェクトの前、後ろ、または同じのいずれであるかを示す整数を返します。
		//
		// パラメータ:
		//   obj:
		//     このインスタンスと比較するオブジェクト。
		//
		// 戻り値:
		//     比較対象オブジェクトの相対順序を示す 32 ビット符号付き整数。戻り値の意味は次のとおりです。 
		//
		//     値		説明
		//     --------------------
		//     負数		this ＜ obj
		//     0		this ＝ obj
		//     正数		this ＞ obj
		//
		// 例外:
		//   System.ArgumentException:
		//     obj の型がこのインスタンスの型と異なります。
		//
		public int CompareTo( Cチップ other )
		{
			lock( this )
			{
				lock( other )
				{
					if( this.n小節番号 < other.n小節番号 ) { return -1; }
					if( this.n小節番号 > other.n小節番号 ) { return +1; }

					double dbThis = (double) this.n小節内位置 / (double) this.n小節解像度;
					double dbOther = (double) other.n小節内位置 / (double) other.n小節解像度;

					if( dbThis < dbOther ) { return -1; }
					if( dbThis > dbOther ) { return +1; }


					// グリッドが完全に等しいなら、チップの種類ごとに定義された深度で順序を決める。

					if( Cチップ.dicチップの深さ[ this.eチップ種別 ] > Cチップ.dicチップの深さ[ other.eチップ種別 ] ) { return -1; }
					if( Cチップ.dicチップの深さ[ this.eチップ種別 ] < Cチップ.dicチップの深さ[ other.eチップ種別 ] ) { return +1; }

					return 0;
				}
			}
		}

		private static readonly Dictionary<Eチップ種別, int> dicチップの深さ = new Dictionary<Eチップ種別, int>() {
			
			// 上に位置するほど浅いように並べている。
			
			{ Eチップ種別.LeftCymbalMute, 40},
			{ Eチップ種別.RightCymbalMute, 40},
			
			{ Eチップ種別.RideCup, 50},		// 念のため
			{ Eチップ種別.LeftRideCup, 50},
			{ Eチップ種別.RightRideCup, 50},
			
			{ Eチップ種別.HiHatOpen, 50},
			{ Eチップ種別.HiHatHalfOpen, 50},
			{ Eチップ種別.HiHatClose, 50},
			{ Eチップ種別.FootPedal, 50},
			
			{ Eチップ種別.Snare, 50},
			{ Eチップ種別.SnareOpenRim, 50},
			{ Eチップ種別.SnareClosedRim, 50},
			{ Eチップ種別.SnareGhost, 50},

			{ Eチップ種別.Tom1, 50},
			{ Eチップ種別.Tom1Rim, 50},
	
			{ Eチップ種別.BPM, 50},

			{ Eチップ種別.Ride, 60},		// 念のため
			{ Eチップ種別.LeftRide, 60},
			{ Eチップ種別.RightRide, 60},
	
			{ Eチップ種別.Splash, 60},		// 念のため
			{ Eチップ種別.LeftSplash, 60},
			{ Eチップ種別.RightSplash, 60},

			{ Eチップ種別.Tom2, 60},
			{ Eチップ種別.Tom2Rim, 60},
	
			{ Eチップ種別.LeftCrash, 70},

			{ Eチップ種別.China, 70},		// 念のため
			{ Eチップ種別.LeftChina, 70},
			{ Eチップ種別.RightChina, 70},
	
			{ Eチップ種別.Tom3, 70},
			{ Eチップ種別.Tom3Rim, 70},

			{ Eチップ種別.RightCrash, 70},
	
			{ Eチップ種別.Bass, 75},

			{ Eチップ種別.小節線, 80},
			{ Eチップ種別.拍線, 85},
			{ Eチップ種別.背景動画, 90},
			{ Eチップ種別.小節メモ, 99},
			{ Eチップ種別.Unknown, 99 },
		};
		//-----------------
		#endregion

		// 譜面データ読み込み時に決定する項目。増減させたらコピー元付きコンストラクタを修正のこと。

		public const int n最大音量 = 4;
		public const int n最小音量 = 1;

		public Eチップ種別 eチップ種別
		{
			get
			{
				lock( this )
				{
					return this._eチップ種別;
				}
			}
			set
			{
				lock( this )
				{
					this._eチップ種別 = value;
				}
			}
		}

		/// <summary>
		/// <para>チップの存在する小節の番号（0～)。</para>
		/// </summary>
		public int n小節番号
		{
			get
			{
				lock( this )
				{
					return this._n小節番号;
				}
			}
			set
			{
				lock( this )
				{
					this._n小節番号 = value;
				}
			}
		}

		/// <summary>
		/// <para>チップの小節内における位置。</para>
		/// <para>0≦n小節内位置＜n小節解像度 を満たす整数。</para>
		/// </summary>
		public int n小節内位置
		{
			get
			{
				lock( this )
				{
					return this._n小節内位置;
				}
			}
			set
			{
				lock( this )
				{
					this._n小節内位置 = value;
				}
			}
		}

		/// <summary>
		/// <para>チップが存在する小節の解像度(1以上の整数)。</para>
		/// </summary>
		public int n小節解像度
		{
			get
			{
				lock( this )
				{
					return this._n小節解像度;
				}
			}
			set
			{
				lock( this )
				{
					this._n小節解像度 = value;
				}
			}
		}

		/// <summary>
		/// <para>チップの描画時刻。遅延を受ける発声時刻とは別に、チップが描画されるべき場所を示す。</para>
		/// <para>譜面の先頭（小節番号-1の先頭）からの時刻をミリ秒単位で表す。</para>
		/// </summary>
		public long n描画時刻ms
		{
			get
			{
				lock( this )
				{
					return this._n描画時刻ms;
				}
			}
			set
			{
				lock( this )
				{
					this._n描画時刻ms = value;
				}
			}
		}

		/// <summary>
		/// <para>チップの発声時刻。遅延が生じる分、描画時刻よりも遅く設定される。</para>
		/// <para>譜面の先頭（小節番号-1の先頭）からの時刻をミリ秒単位で表す。</para>
		/// </summary>
		public long n発声時刻ms
		{
			get
			{
				lock( this )
				{
					return this._n発声時刻ms;
				}
			}
			set
			{
				lock( this )
				{
					this._n発声時刻ms = value;
				}
			}
		}

		/// <summary>
		/// <para>チップの音量（小:1～4：大）。</para>
		/// </summary>
		public int n音量
		{
			get
			{
				lock( this )
				{
					return this._n音量;
				}
			}
			set
			{
				if( value < 1 || value > 4 )
					throw new ArgumentException( string.Format( "音量の値域(1～4)を超える値({0})が指定されました。", value ) );

				lock( this )
				{
					this._n音量 = value;
				}
			}
		}

		/// <summary>
		/// <para>eチップ が Eチップ.BPM の時のみ有効。</para>
		/// <para>チップの持つBPMの値。</para>
		/// </summary>
		public double dbBPM
		{
			get
			{
				lock( this )
				{
					return this._dbBPM;
				}
			}
			set
			{
				lock( this )
				{
					this._dbBPM = value;
				}
			}
		}


		// SSTFEditor用状態変数。増減させたらコピー元付きコンストラクタを修正のこと。

		public int n譜面内絶対位置grid
		{
			get
			{
				lock( this )
				{
					return this._n譜面内絶対位置grid;
				}
			}
			set
			{
				lock( this )
				{
					this._n譜面内絶対位置grid = value;
				}
			}
		}
		public bool bドラッグ操作により選択中
		{
			get
			{
				lock( this )
				{
					return this._bドラッグ操作により選択中;
				}
			}
			set
			{
				lock( this )
				{
					this._bドラッグ操作により選択中 = value;
				}
			}
		}
		public bool b選択が確定している
		{
			get
			{
				lock( this )
				{
					return this._b選択が確定している;
				}
			}
			set
			{
				lock( this )
				{
					this._b選択が確定している = value;
				}
			}
		}
		public bool b移動済
		{
			get
			{
				lock( this )
				{
					return this._b移動済;
				}
			}
			set
			{
				lock( this )
				{
					this._b移動済 = value;
				}
			}
		}
		public string stgチップ内文字列
		{
			get
			{
				lock( this )
				{
					return this._stgチップ内文字列;
				}
			}
			set
			{
				lock( this )
				{
					this._stgチップ内文字列 = value;
				}
			}
		}
		public int n枠外レーン数
		{
			get
			{
				lock( this )
				{
					return this._n枠外レーン数;
				}
			}
			set
			{
				lock( this )
				{
					this._n枠外レーン数 = value;
				}
			}
		}


		// 演奏用のワーク変数。増減させたらコピー元付きコンストラクタを修正のこと。

		public bool b可視
		{
			get
			{
				lock( this )
				{
					return this._b可視;
				}
			}
			set
			{
				lock( this )
				{
					this._b可視 = value;
				}
			}
		}
		public bool b不可視
		{
			get { return !this.b可視; }
			set { this.b可視 = !value; }
		}
		
		public bool bヒット済み
		{
			get
			{
				lock( this )
				{
					return this._bヒット済み;
				}
			}
			set
			{
				lock( this )
				{
					this._bヒット済み = value;
				}
			}
		}
		public bool bヒットしてない
		{
			get { return !this.bヒット済み; }
			set { this.bヒット済み = !value; }
		}
		
		public bool b発声済み
		{
			get
			{
				lock( this )
				{
					return this._b発声済み;
				}
			}
			set
			{
				lock( this )
				{
					this._b発声済み = value;
				}
			}
		}
		public bool b発声してない
		{
			get { return !this.b発声済み; }
			set { this.b発声済み = !value; }
		}


		// デバイス共通(static);
		
		public static void tチップの画像を描画する( IntPtr hLockedDevice, Cチップ chip, int n中心x, int n中心y, int nチップアニメのインデックス0to48 )
		{
			#region [ 実行条件チェック ]
			//-------------------------
			if( null == Cチップ.txチップ )
				return;		// エラーじゃなく単に無視

			Debug.Assert( ( 0 <= nチップアニメのインデックス0to48 ) && ( 48 >= nチップアニメのインデックス0to48 ) );
			//-------------------------
			#endregion

			lock( chip )
			{
				// 音量により縦方向の拡大率を変更。

				float f拡大率X = 1.0f;
				float f拡大率Y = 1.0f * (float) chip.n音量 * ( 1.0f / ( Cチップ.n最大音量 - Cチップ.n最小音量 + 1 ) );	// 0.0～1.0


				// 描画。

				switch( chip.eチップ種別 )
				{
					case Eチップ種別.BPM:
					case Eチップ種別.China:
					case Eチップ種別.Ride:
					case Eチップ種別.RideCup:
					case Eチップ種別.Splash:
					case Eチップ種別.Unknown:
					case Eチップ種別.小節メモ:
					case Eチップ種別.背景動画:
						break;

					case Eチップ種別.Bass:
					case Eチップ種別.HiHatClose:
					case Eチップ種別.Snare:
					case Eチップ種別.Tom1:
					case Eチップ種別.Tom2:
					case Eチップ種別.Tom3:
						Cチップ.tチップパーツを描画する( hLockedDevice, chip.eチップ種別, n中心x, n中心y, f拡大率X, f拡大率Y, Cチップ.dicチップ種別to画像範囲[ chip.eチップ種別 ], nチップアニメのインデックス0to48 );
						break;

					case Eチップ種別.HiHatHalfOpen:
						Cチップ.tチップパーツを描画する( hLockedDevice, Eチップ種別.HiHatClose, n中心x, n中心y, f拡大率X, f拡大率Y, Cチップ.dicチップ種別to画像範囲[ Eチップ種別.HiHatClose ], nチップアニメのインデックス0to48 );
						Cチップ.tチップパーツを描画する( hLockedDevice, Eチップ種別.HiHatHalfOpen, n中心x, n中心y, f拡大率X, f拡大率Y, Cチップ.dicチップ種別to画像範囲[ Eチップ種別.HiHatHalfOpen ] );
						break;

					case Eチップ種別.HiHatOpen:
						Cチップ.tチップパーツを描画する( hLockedDevice, Eチップ種別.HiHatClose, n中心x, n中心y, f拡大率X, f拡大率Y, Cチップ.dicチップ種別to画像範囲[ Eチップ種別.HiHatClose ], nチップアニメのインデックス0to48 );
						Cチップ.tチップパーツを描画する( hLockedDevice, Eチップ種別.HiHatOpen, n中心x, n中心y, f拡大率X, f拡大率Y, Cチップ.dicチップ種別to画像範囲[ Eチップ種別.HiHatOpen ] );
						break;

					case Eチップ種別.SnareOpenRim:
						Cチップ.tチップパーツを描画する( hLockedDevice, Eチップ種別.SnareOpenRim, n中心x, n中心y, f拡大率X, f拡大率Y, Cチップ.dicチップ種別to画像範囲[ Eチップ種別.SnareOpenRim ] );
						Cチップ.tチップパーツを描画する( hLockedDevice, Eチップ種別.Snare, n中心x, n中心y, f拡大率X, f拡大率Y, Cチップ.dicチップ種別to画像範囲[ Eチップ種別.Snare ], nチップアニメのインデックス0to48 );
						break;

					default:
						Cチップ.tチップパーツを描画する( hLockedDevice, chip.eチップ種別, n中心x, n中心y, f拡大率X, f拡大率Y, Cチップ.dicチップ種別to画像範囲[ chip.eチップ種別 ] );
						break;
				}
			}
		}
		private static void tチップパーツを描画する( IntPtr hLockedDevice, Eチップ種別 eチップ種別, int n中心x, int n中心y, float f拡大率X, float f拡大率Y, Rectangle rc描画元領域, int nチップアニメのインデックス0to48 = -1 )
		{
			#region [ アニメするチップはインデックス(0-48)に応じてrc描画元領域を修正。]
			//-------------------------
			if( -1 != nチップアニメのインデックス0to48 )
			{
				rc描画元領域.Y += nチップアニメのインデックス0to48 * 15;				// １チップの高さは18pxだが、下部3pxを下のチップと共有している。
				rc描画元領域.Height = 18;												//
			}
			//-------------------------
			#endregion

			int width = (int) ( rc描画元領域.Width * f拡大率X );
			int height = (int) ( rc描画元領域.Height * f拡大率Y );

			Debug.Assert( 0 < width && 0 < height );

			Cチップ.txチップ.vc拡大縮小倍率 = new Vector3( f拡大率X, f拡大率Y, 1.0f );
			Cチップ.txチップ.t2D描画( hLockedDevice, n中心x - width / 2, n中心y - height / 2, rc描画元領域 );
		}


		// チップ共通(static); lock(Cチップ.lockAllChips) で保護
		public static void t全チップのデバイスリソースの作成()
		{
			lock( Cチップ.lockAllChips )
			{
				if( null != Cチップ.txチップ )
					Cチップ.txチップ.Dispose();

				Global.tデバイスをロックして処理を行う( ( hDevice ) => {
					Cチップ.txチップ = new CTexture( hDevice, Path.Combine( Global.Folders.stgシステムフォルダパス, @"Images\Drum Chips.png" ) );
				} );
			}
		}
		public static void t全チップのデバイスリソースの解放()
		{
			lock( Cチップ.lockAllChips )
			{
				if( null != Cチップ.txチップ )
				{
					Cチップ.txチップ.Dispose();
					Cチップ.txチップ = null;
				}
			}
		}

		private static CTexture txチップ = null;

		private static readonly Dictionary<Eチップ種別, Rectangle> dicチップ種別to画像範囲 = new Dictionary<Eチップ種別, Rectangle>() {
			#region [ *** ]
			//-------------------------
			{ Eチップ種別.LeftCrash,		new Rectangle( x:0,   y:955, width:181, height:69  ) },
			{ Eチップ種別.LeftChina,		new Rectangle( x:0,   y:886, width:181, height:69  ) },
			{ Eチップ種別.LeftSplash,		new Rectangle( x:0,   y:817, width:181, height:69  ) },
			{ Eチップ種別.LeftRide,			new Rectangle( x:0,   y:748, width:181, height:69  ) },
			{ Eチップ種別.LeftRideCup,		new Rectangle( x:0,   y:748, width:181, height:69  ) },
			{ Eチップ種別.RightCrash,		new Rectangle( x:181, y:955, width:181, height:69  ) },
			{ Eチップ種別.RightChina,		new Rectangle( x:181, y:886, width:181, height:69  ) },
			{ Eチップ種別.RightSplash,		new Rectangle( x:181, y:817, width:181, height:69  ) },
			{ Eチップ種別.RightRide,		new Rectangle( x:181, y:748, width:181, height:69  ) },
			{ Eチップ種別.RightRideCup,		new Rectangle( x:181, y:748, width:181, height:69  ) },
			{ Eチップ種別.LeftCymbalMute,	new Rectangle( x:362, y:748, width:45 , height:45  ) },
			{ Eチップ種別.RightCymbalMute,	new Rectangle( x:407, y:748, width:45 , height:45  ) },
			{ Eチップ種別.SnareClosedRim,	new Rectangle( x:362, y:793, width:45 , height:45  ) },
			{ Eチップ種別.SnareGhost,		new Rectangle( x:407, y:793, width:45 , height:45  ) },
			{ Eチップ種別.SnareOpenRim,		new Rectangle( x:362, y:838, width:90 , height:22  ) },
			{ Eチップ種別.Tom1Rim,			new Rectangle( x:362, y:793, width:45 , height:45  ) },
			{ Eチップ種別.Tom2Rim,			new Rectangle( x:362, y:793, width:45 , height:45  ) },
			{ Eチップ種別.Tom3Rim,			new Rectangle( x:362, y:793, width:45 , height:45  ) },
			{ Eチップ種別.HiHatOpen,		new Rectangle( x:362, y:860, width:45 , height:45  ) },
			{ Eチップ種別.HiHatHalfOpen,	new Rectangle( x:407, y:860, width:45 , height:45  ) },
			{ Eチップ種別.FootPedal,		new Rectangle( x:362, y:905, width:45 , height:45  ) },

			{ Eチップ種別.小節線,			new Rectangle( x:0,   y:738, width:300, height:2   ) },	// 描画は600x2
			{ Eチップ種別.拍線,				new Rectangle( x:0,   y:740, width:300, height:1   ) },	// 描画は600x1
	
			{ Eチップ種別.Bass,				new Rectangle( x:0,   y:0,   width:87 , height:738 ) },	// 縦に49枚、チップの高さは18pxだが、下部3pxを下のチップと共有している。
			{ Eチップ種別.HiHatClose,		new Rectangle( x:84,  y:0,   width:69 , height:738 ) },	// 縦に49枚、同上
			{ Eチップ種別.Snare,			new Rectangle( x:150, y:0,   width:70 , height:738 ) },	// 縦に49枚、同上
			{ Eチップ種別.Tom1,				new Rectangle( x:217, y:0,   width:70 , height:738 ) },	// 縦に49枚、同上
			{ Eチップ種別.Tom2,				new Rectangle( x:284, y:0,   width:69 , height:738 ) },	// 縦に49枚、同上
			{ Eチップ種別.Tom3,				new Rectangle( x:350, y:0,   width:70 , height:738 ) },	// 縦に49枚、同上
			
			{ Eチップ種別.Unknown,			new Rectangle( x:0,   y:0,   width:0,   height:0   ) },	// 以下、描画無し
			{ Eチップ種別.BPM,				new Rectangle( x:0,   y:0,   width:0,   height:0   ) },
			{ Eチップ種別.背景動画,			new Rectangle( x:0,   y:0,   width:0,   height:0   ) },
			{ Eチップ種別.小節メモ,			new Rectangle( x:0,   y:0,   width:0,   height:0   ) },
			{ Eチップ種別.Ride,				new Rectangle( x:0,   y:0,   width:0,   height:0   ) },
			{ Eチップ種別.RideCup,			new Rectangle( x:0,   y:0,   width:0,   height:0   ) },
			{ Eチップ種別.China,			new Rectangle( x:0,   y:0,   width:0,   height:0   ) },
			{ Eチップ種別.Splash,			new Rectangle( x:0,   y:0,   width:0,   height:0   ) },
			//-------------------------
			#endregion
		};


		// サウンド共通(static); lock(Cチップ.lockAllChips) で保護（Cチップ.dic～ を使うため）

		private const int n多重度 = 2;

		public static void tチップサウンドを再生する( Eチップ種別 eチップ種別, int n音量0to100 )
		{
			lock( Cチップ.lockAllChips )
			{
				Debug.Assert( null != Cチップ.dicチップ種別toサウンド );
				Debug.Assert( Cチップ.dicチップ種別toサウンド.ContainsKey( eチップ種別 ) );

				// 再生サウンドの決定
				int index = Cチップ.dicチップ種別to次に再生するサウンドのインデックス[ eチップ種別 ];
				CSound sd = Cチップ.dicチップ種別toサウンド[ eチップ種別 ][ index ];

				// ハイハット系なら直前の音を停止（ただしハイハットオープン系の再生時は除く）
				if( eチップ種別 == Eチップ種別.FootPedal ||
					eチップ種別 == Eチップ種別.HiHatClose )
				{
					Cチップ.t再生中のハイハットサウンドを停止する();
				}

				// 再生
				sd.n音量 = Math.Min( Math.Max( n音量0to100, 0 ), 100 );
				sd.t先頭から再生する();

				// 0～n多重度-1 をループ
				Cチップ.dicチップ種別to次に再生するサウンドのインデックス[ eチップ種別 ] = ( index + 1 ) % Cチップ.n多重度;
			}
		}
		public static void t再生中のシンバルサウンドを停止する( bool b右側 )
		{
			lock( Cチップ.lockAllChips )
			{
				foreach( var obj in Enum.GetValues( typeof( Eチップ種別 ) ) )
				{
					var eChip = (Eチップ種別) obj;

					bool b停止対象である = false;

					if( b右側 )
					{
						// 右側のシンバル類を止める
						if( eChip == Eチップ種別.RightChina ||
							eChip == Eチップ種別.RightCrash ||
							eChip == Eチップ種別.RightRide ||
							eChip == Eチップ種別.RightRideCup ||
							eChip == Eチップ種別.RightSplash )
						{
							b停止対象である = true;
						}
					}
					else
					{
						// 左側のシンバル類を止める
						if( eChip == Eチップ種別.LeftChina ||
							eChip == Eチップ種別.LeftCrash ||
							eChip == Eチップ種別.LeftRide ||
							eChip == Eチップ種別.LeftRideCup ||
							eChip == Eチップ種別.LeftSplash )
						{
							b停止対象である = true;
						}
					}

					if( b停止対象である )
					{
						for( int i = 0; i < Cチップ.n多重度; i++ )
							Cチップ.dicチップ種別toサウンド[ eChip ][ i ].t停止する();	// 再生中かどうかにかかわらず全部止める
					}
				}
			}
		}
		public static void t再生中のハイハットサウンドを停止する()
		{
			lock( Cチップ.lockAllChips )
			{
				foreach( var obj in Enum.GetValues( typeof( Eチップ種別 ) ) )
				{
					var eChip = (Eチップ種別) obj;

					bool b停止対象である = false;

					if( eChip == Eチップ種別.FootPedal ||
						eChip == Eチップ種別.HiHatClose ||
						eChip == Eチップ種別.HiHatHalfOpen ||
						eChip == Eチップ種別.HiHatOpen )
					{
						b停止対象である = true;
					}

					if( b停止対象である )
					{
						for( int i = 0; i < Cチップ.n多重度; i++ )
							Cチップ.dicチップ種別toサウンド[ eChip ][ i ].t停止する();	// 再生中かどうかにかかわらず全部止める
					}
				}
			}
		}

		public static void t全チップサウンドの作成()
		{
			lock( Cチップ.lockAllChips )
			{
				Cチップ.dicチップ種別toサウンド = new Dictionary<Eチップ種別, List<CSound>>();

				foreach( var kvp in Cチップ.dicチップ種別toサウンドファイル名 )
				{
					if( !string.IsNullOrEmpty( kvp.Value ) )
					{
						Cチップ.dicチップ種別toサウンド.Add( kvp.Key, new List<CSound>( Cチップ.n多重度 ) );

						for( int n = 0; n < n多重度; n++ )
							Cチップ.dicチップ種別toサウンド[ kvp.Key ].Add( Global.Sound.tサウンドを作成する( Path.Combine( Global.Folders.stgシステムフォルダパス, kvp.Value ) ) );
					}
				}

				Cチップ.dicチップ種別to次に再生するサウンドのインデックス = new Dictionary<Eチップ種別, int>();

				foreach( var obj in Enum.GetValues( typeof( Eチップ種別 ) ) )
					Cチップ.dicチップ種別to次に再生するサウンドのインデックス.Add( (Eチップ種別) obj, 0 );

				Trace.TraceInformation( "全チップのサウンドを作成しました。" );
			}
		}
		public static void t全チップサウンドの解放()
		{
			lock( Cチップ.lockAllChips )
			{
				foreach( var kvp in Cチップ.dicチップ種別toサウンド )
				{
					foreach( var sound in kvp.Value )
						sound.Dispose();

					kvp.Value.Clear();
				}

				Cチップ.dicチップ種別toサウンド.Clear();
				Cチップ.dicチップ種別toサウンド = null;

				Cチップ.dicチップ種別to次に再生するサウンドのインデックス.Clear();
				Cチップ.dicチップ種別to次に再生するサウンドのインデックス = null;

				Trace.TraceInformation( "全チップのサウンドを解放しました。" );
			}
		}

		private static Dictionary<Eチップ種別, List<CSound>> dicチップ種別toサウンド = null;
		private static Dictionary<Eチップ種別, int> dicチップ種別to次に再生するサウンドのインデックス = null;
		private static readonly Dictionary<Eチップ種別, string> dicチップ種別toサウンドファイル名 = new Dictionary<Eチップ種別, string>() {
			#region [ *** ]
			//-------------------------
			{ Eチップ種別.Unknown, null },
			{ Eチップ種別.HiHatOpen, @"Sounds\HHOpen.wav" },
			{ Eチップ種別.HiHatHalfOpen, @"Sounds\HHHalfOpen.wav" },
			{ Eチップ種別.HiHatClose, @"Sounds\HHClose.wav" },
			{ Eチップ種別.FootPedal, @"Sounds\HHFoot.wav" },
			{ Eチップ種別.Snare, @"Sounds\Snare.wav" },
			{ Eチップ種別.SnareOpenRim, @"Sounds\SnareOpenRimShot.wav" },
			{ Eチップ種別.SnareClosedRim, @"Sounds\RimShot.wav" },
			{ Eチップ種別.SnareGhost, @"Sounds\SnareGhost.wav" },
			{ Eチップ種別.Bass, @"Sounds\Bass.wav" },
			{ Eチップ種別.Tom1, @"Sounds\Tom1.wav" },
			{ Eチップ種別.Tom1Rim, @"Sounds\RimShot.wav" },
			{ Eチップ種別.Tom2, @"Sounds\Tom2.wav" },
			{ Eチップ種別.Tom2Rim, @"Sounds\RimShot.wav" },
			{ Eチップ種別.Tom3, @"Sounds\Tom3.wav" },
			{ Eチップ種別.Tom3Rim, @"Sounds\RimShot.wav" },
			{ Eチップ種別.LeftCrash, @"Sounds\LeftCrash.wav" },
			{ Eチップ種別.RightCrash, @"Sounds\RightCrash.wav" },
			{ Eチップ種別.LeftChina, @"Sounds\LeftChina.wav" },
			{ Eチップ種別.LeftSplash, @"Sounds\LeftSplash.wav" },
			{ Eチップ種別.LeftRide, @"Sounds\LeftRide.wav" },
			{ Eチップ種別.LeftRideCup, @"Sounds\LeftRideCup.wav" },
			{ Eチップ種別.RightChina, @"Sounds\RightChina.wav" },
			{ Eチップ種別.RightSplash, @"Sounds\RightSplash.wav" },
			{ Eチップ種別.RightRide, @"Sounds\RightRide.wav" },
			{ Eチップ種別.RightRideCup, @"Sounds\RightRideCup.wav" },
			{ Eチップ種別.LeftCymbalMute, null },
			{ Eチップ種別.RightCymbalMute, null },
			{ Eチップ種別.BPM, null },
			{ Eチップ種別.小節線, null },
			{ Eチップ種別.拍線, null },
			{ Eチップ種別.背景動画, null },
			{ Eチップ種別.小節メモ, null },
			{ Eチップ種別.Ride, null },
			{ Eチップ種別.RideCup, null },
			{ Eチップ種別.China, null },
			{ Eチップ種別.Splash, null },
			//-------------------------
			#endregion
		};


		// その他

		public static readonly Dictionary<Eチップ種別, List<Eドラム入力>> dicチップ種別toドラム入力 = new Dictionary<Eチップ種別, List<Eドラム入力>>() {
			#region [ *** ]
			//-------------------------
			{ Eチップ種別.Unknown, new List<Eドラム入力>() },
			{ Eチップ種別.HiHatOpen, new List<Eドラム入力>() { Eドラム入力.HiHatOpen, Eドラム入力.HiHatClose, Eドラム入力.FootPedal } },
			{ Eチップ種別.HiHatHalfOpen, new List<Eドラム入力>() { Eドラム入力.HiHatOpen, Eドラム入力.HiHatClose, Eドラム入力.FootPedal } },
			{ Eチップ種別.HiHatClose, new List<Eドラム入力>() { Eドラム入力.HiHatOpen, Eドラム入力.HiHatClose, Eドラム入力.FootPedal } },
			{ Eチップ種別.FootPedal, new List<Eドラム入力>() { Eドラム入力.HiHatOpen, Eドラム入力.HiHatClose, Eドラム入力.FootPedal } },
			{ Eチップ種別.Snare, new List<Eドラム入力>() { Eドラム入力.Snare, Eドラム入力.SnareOpenRim, Eドラム入力.SnareClosedRim } },
			{ Eチップ種別.SnareOpenRim, new List<Eドラム入力>() { Eドラム入力.Snare, Eドラム入力.SnareOpenRim, Eドラム入力.SnareClosedRim } },
			{ Eチップ種別.SnareClosedRim, new List<Eドラム入力>() { Eドラム入力.Snare, Eドラム入力.SnareOpenRim, Eドラム入力.SnareClosedRim } },
			{ Eチップ種別.SnareGhost, new List<Eドラム入力>() { Eドラム入力.Snare, Eドラム入力.SnareOpenRim, Eドラム入力.SnareClosedRim } },
			{ Eチップ種別.Bass, new List<Eドラム入力>() { Eドラム入力.Bass } },
			{ Eチップ種別.Tom1, new List<Eドラム入力>() { Eドラム入力.Tom1, Eドラム入力.Tom1Rim } },
			{ Eチップ種別.Tom1Rim, new List<Eドラム入力>() { Eドラム入力.Tom1, Eドラム入力.Tom1Rim } },
			{ Eチップ種別.Tom2, new List<Eドラム入力>() { Eドラム入力.Tom2, Eドラム入力.Tom2Rim } },
			{ Eチップ種別.Tom2Rim, new List<Eドラム入力>() { Eドラム入力.Tom2, Eドラム入力.Tom2Rim } },
			{ Eチップ種別.Tom3, new List<Eドラム入力>() { Eドラム入力.Tom3, Eドラム入力.Tom3Rim } },
			{ Eチップ種別.Tom3Rim, new List<Eドラム入力>() { Eドラム入力.Tom3, Eドラム入力.Tom3Rim } },
			{ Eチップ種別.LeftCrash, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.RightCrash, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.LeftChina, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.LeftSplash, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.LeftRide, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.LeftRideCup, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.RightChina, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.RightSplash, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.RightRide, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.RightRideCup, new List<Eドラム入力>() { Eドラム入力.LeftCrash, Eドラム入力.RightCrash, Eドラム入力.Ride, Eドラム入力.RideCup, Eドラム入力.China, Eドラム入力.Splash } },
			{ Eチップ種別.LeftCymbalMute, new List<Eドラム入力>() },
			{ Eチップ種別.RightCymbalMute, new List<Eドラム入力>() },
			{ Eチップ種別.BPM, new List<Eドラム入力>() },
			{ Eチップ種別.小節線, new List<Eドラム入力>() },
			{ Eチップ種別.拍線, new List<Eドラム入力>() },
			{ Eチップ種別.背景動画, new List<Eドラム入力>() },
			{ Eチップ種別.小節メモ, new List<Eドラム入力>() },
			{ Eチップ種別.Ride, new List<Eドラム入力>() },
			{ Eチップ種別.RideCup, new List<Eドラム入力>() },
			{ Eチップ種別.China, new List<Eドラム入力>() },
			{ Eチップ種別.Splash, new List<Eドラム入力>() },
			//-------------------------
			#endregion
		};
		public static readonly Dictionary<Eドラム入力, List<Eチップ種別>> dicドラム入力toチップ種別 = new Dictionary<Eドラム入力, List<Eチップ種別>>() {
			#region [ *** ]
			//-------------------------
			{ Eドラム入力.HiHatOpen, new List<Eチップ種別>() { Eチップ種別.HiHatOpen, Eチップ種別.HiHatHalfOpen, Eチップ種別.HiHatClose } },
			{ Eドラム入力.HiHatClose, new List<Eチップ種別>() { Eチップ種別.HiHatOpen, Eチップ種別.HiHatHalfOpen, Eチップ種別.HiHatClose } },
			{ Eドラム入力.FootPedal, new List<Eチップ種別>() { Eチップ種別.HiHatOpen, Eチップ種別.HiHatHalfOpen, Eチップ種別.HiHatClose } },
			{ Eドラム入力.Snare, new List<Eチップ種別>() { Eチップ種別.Snare, Eチップ種別.SnareOpenRim, Eチップ種別.SnareClosedRim, Eチップ種別.SnareGhost } },
			{ Eドラム入力.SnareOpenRim, new List<Eチップ種別>() { Eチップ種別.Snare, Eチップ種別.SnareOpenRim, Eチップ種別.SnareClosedRim, Eチップ種別.SnareGhost } },
			{ Eドラム入力.SnareClosedRim, new List<Eチップ種別>() { Eチップ種別.Snare, Eチップ種別.SnareOpenRim, Eチップ種別.SnareClosedRim, Eチップ種別.SnareGhost } },
			{ Eドラム入力.Bass, new List<Eチップ種別>() { Eチップ種別.Bass } },
			{ Eドラム入力.Tom1, new List<Eチップ種別>() { Eチップ種別.Tom1, Eチップ種別.Tom1Rim } },
			{ Eドラム入力.Tom1Rim, new List<Eチップ種別>() { Eチップ種別.Tom1, Eチップ種別.Tom1Rim } },
			{ Eドラム入力.Tom2, new List<Eチップ種別>() { Eチップ種別.Tom2, Eチップ種別.Tom2Rim } },
			{ Eドラム入力.Tom2Rim, new List<Eチップ種別>() { Eチップ種別.Tom2, Eチップ種別.Tom2Rim } },
			{ Eドラム入力.Tom3, new List<Eチップ種別>() { Eチップ種別.Tom3, Eチップ種別.Tom3Rim } },
			{ Eドラム入力.Tom3Rim, new List<Eチップ種別>() { Eチップ種別.Tom3, Eチップ種別.Tom3Rim } },
			{ Eドラム入力.LeftCrash, new List<Eチップ種別>() { Eチップ種別.LeftCrash, Eチップ種別.RightCrash, Eチップ種別.LeftChina, Eチップ種別.LeftSplash, Eチップ種別.LeftRide, Eチップ種別.LeftRideCup, Eチップ種別.RightChina, Eチップ種別.RightSplash, Eチップ種別.RightRide, Eチップ種別.RightRideCup } },
			{ Eドラム入力.RightCrash, new List<Eチップ種別>() { Eチップ種別.LeftCrash, Eチップ種別.RightCrash, Eチップ種別.LeftChina, Eチップ種別.LeftSplash, Eチップ種別.LeftRide, Eチップ種別.LeftRideCup, Eチップ種別.RightChina, Eチップ種別.RightSplash, Eチップ種別.RightRide, Eチップ種別.RightRideCup } },
			{ Eドラム入力.Ride, new List<Eチップ種別>() { Eチップ種別.LeftCrash, Eチップ種別.RightCrash, Eチップ種別.LeftChina, Eチップ種別.LeftSplash, Eチップ種別.LeftRide, Eチップ種別.LeftRideCup, Eチップ種別.RightChina, Eチップ種別.RightSplash, Eチップ種別.RightRide, Eチップ種別.RightRideCup } },
			{ Eドラム入力.RideCup, new List<Eチップ種別>() { Eチップ種別.LeftCrash, Eチップ種別.RightCrash, Eチップ種別.LeftChina, Eチップ種別.LeftSplash, Eチップ種別.LeftRide, Eチップ種別.LeftRideCup, Eチップ種別.RightChina, Eチップ種別.RightSplash, Eチップ種別.RightRide, Eチップ種別.RightRideCup } },
			{ Eドラム入力.China, new List<Eチップ種別>() { Eチップ種別.LeftCrash, Eチップ種別.RightCrash, Eチップ種別.LeftChina, Eチップ種別.LeftSplash, Eチップ種別.LeftRide, Eチップ種別.LeftRideCup, Eチップ種別.RightChina, Eチップ種別.RightSplash, Eチップ種別.RightRide, Eチップ種別.RightRideCup } },
			{ Eドラム入力.Splash, new List<Eチップ種別>() { Eチップ種別.LeftCrash, Eチップ種別.RightCrash, Eチップ種別.LeftChina, Eチップ種別.LeftSplash, Eチップ種別.LeftRide, Eチップ種別.LeftRideCup, Eチップ種別.RightChina, Eチップ種別.RightSplash, Eチップ種別.RightRide, Eチップ種別.RightRideCup } },
			//-------------------------
			#endregion
		};

		private static readonly Object lockAllChips = new Object();


		// プロパティ実体; lock(this) で保護

		private Eチップ種別 _eチップ種別;
		private int _n小節番号;
		private int _n小節内位置;
		private int _n小節解像度;
		private long _n描画時刻ms;
		private long _n発声時刻ms;
		private int _n音量;
		private double _dbBPM;

		private int _n譜面内絶対位置grid;
		private bool _bドラッグ操作により選択中;
		private bool _b選択が確定している;
		private bool _b移動済;
		private string _stgチップ内文字列;
		private int _n枠外レーン数;

		private bool _b可視;
		private bool _bヒット済み;
		private bool _b発声済み;
	}
}
