using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Diagnostics;
using SharpDX;
using FDK;

using Color = System.Drawing.Color;
using Rectangle = System.Drawing.Rectangle;

namespace DTXMania
{
	/// <summary>
	/// CAct演奏Drumsゲージ と CAct演奏Gutiarゲージ のbaseクラス。ダメージ計算やDanger/Failed判断もこのクラスで行う。
	/// </summary>
	internal class CAct演奏ゲージ共通 : CActivity
	{
		const double GAUGE_MAX = 1.0;
		const double GAUGE_INITIAL = 2.0 / 3;
		const double GAUGE_MIN = -0.1;
		const double GAUGE_ZERO = 0.0;
		const double GAUGE_DANGER = 0.3;

		private float[,] fDamageGaugeDelta = {
			// #23625 2011.1.10 ickw_284: tuned damage/recover factors
			// drums,   guitar,  bass
			{  0.004f,  0.006f,  0.006f  },
			{  0.002f,  0.003f,  0.003f  },
			{  0.000f,  0.000f,  0.000f  },
			{ -0.020f, -0.030f, -0.030f  },
			{ -0.050f, -0.050f, -0.050f  }
		};
		private float[] fDamageLevelFactor = {
			0.5f, 1.0f, 1.5f
		};

		private double db現在のゲージ値;
		private CCounter ct本体移動;
		private CCounter ct本体振動;
		private CTexture txゲージ;
		private CTextureAf txゲージ白;
		private ST白い星[] st白い星 = new ST白い星[24];

		private CActDigit actDigit;

		/// <summary>
		/// Riskyモードか否か
		/// </summary>
		public bool bRisky { get; private set; }

		/// <summary>
		/// Risky初期値
		/// </summary>
		public int nRiskyTimesInitial { get; private set; }

		/// <summary>
		/// 残Miss回数
		/// </summary>
		public int nRiskyTimes { get; private set; }

		/// <summary>
		/// 閉店状態になったかどうか
		/// </summary>
		/// <returns></returns>
		public bool IsFailed
		{
			get
			{
				if (bRisky)
				{
					return (nRiskyTimes <= 0);
				}
				return this.db現在のゲージ値 <= GAUGE_MIN;
			}
		}

		/// <summary>
		/// DANGERかどうか
		/// </summary>
		/// <returns></returns>
		public bool IsDanger
		{
			get
			{
				if (bRisky)
				{
					switch (nRiskyTimesInitial)
					{
						case 1:
							return false;
						case 2:
						case 3:
							return (nRiskyTimes <= 1);
						default:
							return (nRiskyTimes <= 2);
					}
				}
				return (this.db現在のゲージ値 <= GAUGE_DANGER);
			}
		}

		public CAct演奏ゲージ共通()
		{
			base.list子Activities.Add(actDigit = new CActDigit(Color.Red, Color.Black, 24));
		}

		public override void On非活性化()
		{
			if (base.b活性化してる)
			{
				this.ct本体振動 = null;
				this.ct本体移動 = null;
				for (int i = 0; i < 24; i++)
				{
					this.st白い星[i].ct進行 = null;
				}
				base.On非活性化();
			}
		}

		public override void OnUnmanagedリソースの作成()
		{
			if (b活性化してる)
			{
				this.txゲージ = TextureFactory.tテクスチャの生成(CSkin.Path(@"Graphics\ScreenPlayDrums gauge.png"));
				this.txゲージ白 = TextureFactory.tテクスチャの生成Af(CSkin.Path(@"Graphics\ScreenPlayDrums_gauge_white.png "));
				base.OnUnmanagedリソースの作成();
			}
		}

		public override void OnManagedリソースの解放()
		{
			if (b活性化してる)
			{
				TextureFactory.tテクスチャの解放(ref this.txゲージ);
				TextureFactory.tテクスチャの解放(ref this.txゲージ白);
				base.OnUnmanagedリソースの解放();
			}
		}

		public override int On進行描画()
		{
			if (b活性化してる && CDTXMania.Instance.ConfigIni.eDark == EDark.Off)
			{
				#region [ 初めての進行描画 ]
				if (base.b初めての進行描画)
				{
					for (int k = 0; k < 24; k++)
					{
						this.st白い星[k].x = 6 + CDTXMania.Instance.Random.Next(12);
						this.st白い星[k].fScale = 0.2f + (CDTXMania.Instance.Random.Next(2) * 0.05f);
						this.st白い星[k].ct進行 = new CCounter(0, CDTXMania.Instance.Coordinates.GaugeMax, 8 + CDTXMania.Instance.Random.Next(4), CDTXMania.Instance.Timer);
						this.st白い星[k].ct進行.n現在の値 = CDTXMania.Instance.Random.Next(CDTXMania.Instance.Coordinates.GaugeMax);
					}
					this.ct本体移動 = new CCounter(0, 0x1a, 20, CDTXMania.Instance.Timer);
					this.ct本体振動 = new CCounter(0, 360, 4, CDTXMania.Instance.Timer);
					base.b初めての進行描画 = false;
				}
				#endregion
				this.ct本体移動.t進行Loop();
				this.ct本体振動.t進行Loop();


				#region [ 緑orオレンジのゲージ表示 ]
				double dbゲージ最大値 = CDTXMania.Instance.Coordinates.GaugeMax;
				int n表示するゲージの高さ = (this.db現在のゲージ値 == 1.0) ?
					((int)(dbゲージ最大値 * this.db現在のゲージ値)) :
					((int)((dbゲージ最大値 * this.db現在のゲージ値) + (2.0 * Math.Sin(Math.PI * 2 * (((double)this.ct本体振動.n現在の値) / 360.0)))));

				if (n表示するゲージの高さ <= 0)
				{
					return 0;
				}
				if (this.txゲージ != null)
				{
					this.txゲージ.vc拡大縮小倍率 = new Vector3(1f, 1f, 1f);
					this.txゲージ.n透明度 = 0xff;
					this.txゲージ.b加算合成 = false;
				}
				int nCtゲージ内部上昇スクロール現在値 = (int)(this.ct本体移動.n現在の値 * Scale.Y);
				int n表示ゲージ最大値 = CDTXMania.Instance.Coordinates.Gauge.Y;
				int ybar = (n表示ゲージ最大値 - n表示するゲージの高さ) - nCtゲージ内部上昇スクロール現在値;
				int height = n表示するゲージの高さ + nCtゲージ内部上昇スクロール現在値;
				while (height > 0)
				{
					Rectangle rect = (this.db現在のゲージ値 == 1.0) ?
						CDTXMania.Instance.Coordinates.ImgGaugeOrange :
						CDTXMania.Instance.Coordinates.ImgGaugeNormal;
					#region [ clipping ]
					if (ybar < (n表示ゲージ最大値 - n表示するゲージの高さ))
					{
						int d = (n表示ゲージ最大値 - n表示するゲージの高さ) - ybar;
						rect.Y += d;
						rect.Height -= d;
						ybar += d;
					}
					if ((ybar + rect.Height) > n表示ゲージ最大値)
					{
						int num7 = (ybar + rect.Height) - n表示ゲージ最大値;
						rect.Height -= num7;
					}
					if (rect.Top >= rect.Bottom)
					{
						break;
					}
					#endregion
					if (this.txゲージ != null)
					{
						this.txゲージ.t2D描画(
							CDTXMania.Instance.Device,
							CDTXMania.Instance.Coordinates.Gauge.X,
							ybar,
							rect
						);
					}
					height -= rect.Height;
					ybar += rect.Height;
				}
				#endregion
				#region [ 光彩 ]
				if (this.txゲージ白 != null)
				{
					this.txゲージ白.vc拡大縮小倍率 = new Vector3(1f, 1f, 1f);
					this.txゲージ白.n透明度 = 180;
					this.txゲージ白.b加算合成 = true;
				}
				{
					Rectangle rect = CDTXMania.Instance.Coordinates.ImgGaugeLight;
					int ylight = (n表示ゲージ最大値 - n表示するゲージの高さ);
					if (ylight < n表示ゲージ最大値)
					{
						if ((ylight + rect.Height) > n表示ゲージ最大値)
						{
							int d = (ylight + rect.Height) - n表示ゲージ最大値;
							rect.Height -= d;
						}
						if ((rect.Top < rect.Bottom) && (this.txゲージ白 != null))
						{
							this.txゲージ白.t2D描画(
								CDTXMania.Instance.Device,
								CDTXMania.Instance.Coordinates.Gauge.X,
								ylight,
								rect
							);
						}
					}
				}
				#endregion
				#region [ ゲージ頂上の光源 ]
				if (this.txゲージ != null)
				{
					this.txゲージ.n透明度 = 0xff;
					this.txゲージ.b加算合成 = false;
				}
				{
					Rectangle rect = CDTXMania.Instance.Coordinates.ImgGaugeTopLight;
					int yゲージ頂上 = n表示ゲージ最大値 - n表示するゲージの高さ;
					if (yゲージ頂上 < n表示ゲージ最大値)
					{
						#region [ clipping ]
						if ((yゲージ頂上 + rect.Height) > n表示ゲージ最大値)
						{
							int d = (yゲージ頂上 + rect.Height) - n表示ゲージ最大値;
							rect.Height -= d;
						}
						#endregion
						if ((rect.Top < rect.Bottom) && (this.txゲージ != null))
						{
							this.txゲージ.t2D描画(
								CDTXMania.Instance.Device,
								CDTXMania.Instance.Coordinates.Gauge.X,
								yゲージ頂上,
								rect
							);
						}
					}
				}
				#endregion
				#region [ 泡 ]
				if (this.txゲージ != null)
				{
					this.txゲージ.b加算合成 = true;
				}
				for (int j = 0; j < 24; j++)
				{
					this.st白い星[j].ct進行.t進行Loop();
					int x = CDTXMania.Instance.Coordinates.Gauge.X + this.st白い星[j].x;
					int y泡 = (n表示ゲージ最大値 - n表示するゲージの高さ) + CDTXMania.Instance.Coordinates.GaugeMax - this.st白い星[j].ct進行.n現在の値;
					if (y泡 + CDTXMania.Instance.Coordinates.ImgGaugeStar.H / 2 < n表示ゲージ最大値)
					{
						int n透明度 = (this.st白い星[j].ct進行.n現在の値 < 176) ? 0 : ((int)(255.0 * (((double)(this.st白い星[j].ct進行.n現在の値 - 176)) / 176.0)));
						if ((n透明度 != 0) && (y泡 < CDTXMania.Instance.Coordinates.Gauge.Y))
						{
							Rectangle rect = CDTXMania.Instance.Coordinates.ImgGaugeStar;
							if (this.txゲージ != null)
							{
								this.txゲージ.vc拡大縮小倍率 = new Vector3(this.st白い星[j].fScale, this.st白い星[j].fScale, 1f);
								this.txゲージ.n透明度 = n透明度;
								this.txゲージ.t2D描画(
									CDTXMania.Instance.Device,
									x,
									y泡,
									rect
								);
							}
						}
					}
				}
				#endregion

				#region [ Risky残りMiss回数表示 ]
				if (this.bRisky)    // #23599 2011.7.30 yyagi Risky残りMiss回数表示
				{
					int w = actDigit.Measure(nRiskyTimes);
					actDigit.Draw(nRiskyTimes,
						CDTXMania.Instance.Coordinates.Gauge.X + (CDTXMania.Instance.Coordinates.ImgGaugeNormal.W / 2 - w / 2),
						CDTXMania.Instance.Coordinates.Gauge.Y - actDigit.MaximumHeight);
				}
				#endregion
			}
			return 0;
		}


		/// <summary>
		/// ゲージの初期化
		/// </summary>
		/// <param name="nRiskyTimes_Initial_">Riskyの初期値(0でRisky未使用)</param>
		public void Init(int nRiskyTimes_InitialVal)
		{
			nRiskyTimesInitial = nRiskyTimes_InitialVal;
			nRiskyTimes = nRiskyTimes_InitialVal;
			bRisky = (this.nRiskyTimes > 0);

			if (!bRisky)
			{
				this.db現在のゲージ値 = GAUGE_INITIAL;
			}
			else
			{
				this.db現在のゲージ値 = GAUGE_MAX;
			}
		}

		public void Damage(EPart part, EJudge e今回の判定)
		{
			double fDamage;

			// DAMAGELEVELTUNING
			// before applying #23625 modifications
			switch (e今回の判定)
			{
				case EJudge.Perfect:
				case EJudge.Great:
				case EJudge.Good:
					fDamage = bRisky ? 0 : fDamageGaugeDelta[(int)e今回の判定, (int)part];
					break;
				case EJudge.Poor:
				case EJudge.Miss:
					if (bRisky)
					{
						// Risky=1のときは1Miss即閉店なのでダメージ計算しない
						fDamage = (nRiskyTimes == 1) ? 0 : -GAUGE_MAX / (nRiskyTimesInitial - 1);

						if (nRiskyTimes >= 0)
						{
							// 念のため-1未満には減らないようにしておく
							nRiskyTimes--;
						}
					}
					else
					{
						fDamage = fDamageGaugeDelta[(int)e今回の判定, (int)part];
					}
					if (e今回の判定 == EJudge.Miss && !bRisky)
					{
						fDamage *= fDamageLevelFactor[(int)CDTXMania.Instance.ConfigIni.eDamageLevel.Value];
					}
					break;

				default:
					fDamage = 0.0f;
					break;
			}

			this.db現在のゲージ値 += fDamage;

			if (this.db現在のゲージ値 > GAUGE_MAX)
			{
				// RISKY時は決してゲージが増加しないので、上限チェックはしなくて良い
				this.db現在のゲージ値 = GAUGE_MAX;
			}
		}

		[StructLayout(LayoutKind.Sequential)]
		private struct ST白い星
		{
			public int x;
			public float fScale;
			public CCounter ct進行;
		}
	}
}
