///////////////////////////////////////////////////////////
// CardTable.cs
// Copyright (c) by Ysama September, 2024
// Free Images come from:
// https://www.civillink.netfsozaitramp.html （表面） and
// https://depositphotos.com/jp/vectorsトランプの裏面.html
///////////////////////////////////////////////////////////
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;		//Assemblyを使う為
using System.Resources;			//リソース関係クラス等の使用の為

namespace cardtable
{
	////////////////////////////////
	//CardTable（トランプ台）クラス
	////////////////////////////////
	public class CardTable : Panel					//ピクチャー（ボックス）の枠となるPanel
	{
		//メンバーフィールド（変数）
		PictureBox picBox;							//ピクチャーボックスコントロール
		Bitmap pbCanvas;							//貼り付ける描画用ビットマップ
		Graphics cvsGr;								//pbCanvasの描画ハンドル（hDC）

		//メンバープロパティ
		public int cvsWidth {get; private set;}		//pbCanvasの幅
		public int cvsHeight {get; private set;}	//pbCanvasの高さ
		public int msX {get; private set;}			//マウスが押された時のx座標
		public int msY {get; private set;}			//マウスが押された時のx座標
		public MouseButtons msButton {get; private set;}	//押されたマウスボタン（MouseButtonEventArgs.ChangedButton）
		public bool smallCards {get; set;}			//カードサイズ（初期値 false）
		public Card[] Deck {get; private set;}		//シャッフルされた後の「山」（Cardインスタンス配列）
		public int DealtCards {get; private set;}	//「山」から配られたカードの数

		public CardTable(int W = 988, int H = 714, bool Joker = true)	//コンストラクター
		{
			//メンバープロパティの初期化（自動実装はC# 6.0から）
			cvsWidth = W;							//pbCanvasの幅（初期値暫定－(幅72 + 4) x 13枚）
			cvsHeight = H;							//pbCanvasの高さ（初期値暫定－(高115 + 4) x 6段）
			Deck = new Card[53];					//Card配列の添字配列の初期化（数札52枚 + Joker）
			//CardTable(Panel)の初期化
			this.Size = new Size(640, 480);			//初期値は描画範囲よりも小さく設定
			this.MinimumSize = new Size(640, 480);	//初期値を最小値とする（暫定）
			this.MaximumSize = new Size(cvsWidth + 8, cvsHeight + 8);	//"+8"はスクロールバーマージン
			this.BorderStyle = BorderStyle.Fixed3D;
			this.AutoScroll = true;					//スクロールバー付
			this.BackColor = Color.Green;
			//描画ビットマップとデバイスコンテキストの生成
			pbCanvas = new Bitmap(cvsWidth, cvsHeight);
			//参考：画面サイズ（System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width,
			//					System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height);
			cvsGr = Graphics.FromImage(pbCanvas);
			//ピクチャーボックスコントロール
			picBox = new PictureBox();
			picBox.Location = new Point(0, 0);		//枠内の位置
			picBox.SizeMode = PictureBoxSizeMode.AutoSize;	//画像サイズでPictureBoxの大きさが変更される
			picBox.Image = pbCanvas;				//pbCanvasを貼る
			picBox.Click += Panel_Click;			//picBox（即ちそのコンテナーであるthis）のクリックイベント
			this.Controls.Add(picBox);				//picBoxを子にする
			//Cradクラスインスタンスの初期化とシャッフル
			Shuffle(Joker);
		}

		~CardTable()								//デストラクター
		{
			cvsGr.Dispose();						//pbCanvasの描画ハンドル廃棄
			pbCanvas.Dispose();						//描画用ビットマップ廃棄
		}

		//CardTable上のクリックイベント（picBox→Panelへ伝達）
		private void Panel_Click(object sender, EventArgs e)
		{
			MouseEventArgs mouseArgs = e as MouseEventArgs;
			Point p = this.PointToClient(Cursor.Position);
			msX = p.X;						//クリックされた際のX座標を記録
			msY = p.Y;						//クリックされた際のY座標を記録
			msButton = mouseArgs.Button;	//クリックされたボタンを記録
			this.OnClick(e);				//picBoxからPanelのClickイベントを呼び出す
		}

		//Cardインスタンス配列の初期化
		private void InitCards()
		{
			//数札
			for(int i = 0; i < 4; i++)
			{
				for(int j = 0; j < 13; j++)
				{
					Deck[i * 13 + j] = new Card();
					Deck[i * 13 + j].Suit = i;			//"Clover" - 0, "Diamond" - 1, "Heart" - 2, "Spade" - 3
					Deck[i * 13 + j].Number = j + 1;	//1 - 13（BackとJokerでは0）
					Deck[i * 13 + j].Owner = 0;			//0は一人ゲームまたは未配布を意味する
					Deck[i * 13 + j].Shown = false;
					Deck[i * 13 + j].Discarded = false;
				}
			}
			//Joker
			Deck[52] = new Card();
			Deck[52].Suit = 4;							//"Joker" - 4
			Deck[52].Number = 0;
			Deck[52].Owner = 0;
			Deck[52].Shown = false;
			Deck[52].Discarded = false;
			DealtCards = 0;
		}

		//Cardインスタンス配列のシャッフル
		public void Shuffle(bool Joker)
		{
			int n = Joker ? 53 : 52;	//Jokerを入れるなら53枚、入れないなら52枚でシャッフル
			//カード配列の初期化
			InitCards();
			//乱数の初期化（発生パターンが時間により変化する）
			Random rand = new Random((int)DateTime.Now.Ticks & 0x0000FFFF);
			while(n > 1)	//カードが2枚になった回が最後
			{
				n--;						//'0'ベースの配列の最後を指す
				int m = rand.Next(n + 1);	//n + 1より小さい0 以上の整数
				Card temp = Deck[m];
				Deck[m] = Deck[n];
				Deck[n] = temp;
			}
		}

		//CardTable（picBox）を緑色でクリアする
		public void Clear()
		{
			cvsGr.Clear(Color.Green);
		}

		//Cardインスタンスの表示面指定
		public void TurnFace(int cardNo, bool face)
		{
			Deck[cardNo].Shown = face;
		}

		//デフォルトサイズ（cvsWidth = 76 x 13, cvsHeight = 119 x 6）のグリッド用
		//コンストラクターでサイズを変更する場合には変更を要する
		public int GeGridtNo(int x, int y)
		{
			int offset = smallCards? 11 : 2;
			if((x - offset < 0 || (x - offset) > 76 * 13)	//グリッドサイズ：W = 76dots (72 + 4)
			 || (y - offset < 0 || (y - offset) > 119 * 6))	//グリッドサイズ：H = 119dots (115 + 4)
			 	return -1;									//エラー
			 else
				return ((y - offset) / 119) * 13 + ((x - offset) / 76);
		}

		//Cardインスタンスの指定位置表示
		//位置指定はクライアント座標、または横13 x 縦6のグリッド座標
		//グリッドサイズ：W = 76dots (72 + 4)、H = 119dots (115 + 4)
		//offsetサイズ  ：大カード－2、小カード－11
		public void ShowCard(int cardNo, int x, int y, bool grid = false)
		{
			//本プログラムの埋め込みリソースのリソースマネージャーを作成
			Assembly asm = Assembly.GetExecutingAssembly();
			ResourceManager rm = new ResourceManager("Cards", asm);
			//ピクチャーボックス描画用ビットマップ
			Bitmap bm;
			//リソースID名の作成
			string[] suit = new string[] {"Clover", "Diamond", "Heart", "Spade", "Joker"};
			string name = "Joker";
			if(Deck[cardNo].Suit != 4)
				name = suit[Deck[cardNo].Suit] + String.Format("{0:00}", Deck[cardNo].Number);
			if(Deck[cardNo].Shown)
				//Cardインスタンスのビットマップを読み込む
				bm = (Bitmap)rm.GetObject(name);
			else
				//裏面のビットマップを読み込む
				bm = (Bitmap)rm.GetObject("Back");
			bm.MakeTransparent();
			//【参考】オリジナルサイズ：cvsGr.DrawImage(bm, x, y, bm.Width, bm.Height);	//108 x 172
			if(grid)
			{
				int offset = smallCards? 11 : 2;
				if(smallCards)
					cvsGr.DrawImage(bm, x * 76 + offset, y * 119 + offset, bm.Width / 2, bm.Height / 2);			//54 x 86
				else
					cvsGr.DrawImage(bm, x * 76 + offset, y * 119 + offset, bm.Width * 2 / 3, bm.Height * 2 / 3);	//72 x 115（114.67）
			}
			else
			{
				if(smallCards)
					cvsGr.DrawImage(bm, x, y, bm.Width / 2, bm.Height / 2);			//54 x 86
				else
					cvsGr.DrawImage(bm, x, y, bm.Width * 2 / 3, bm.Height * 2 / 3);	//72 x 115（114.67）
			}
			//ビットマップを廃棄
			bm.Dispose();
			//pbCanvasを貼る
			picBox.Image = pbCanvas;
		}

		//Cardインスタンスの消去
		//位置指定はクライアント座標、または横13 x 縦6のグリッド座標
		//グリッドサイズ：W = 76dots (72 + 4)、H = 119dots (115 + 4)
		//offsetサイズ  ：大カード－2、小カード－11
		public void RemoveCard(int cardNo, int x, int y, bool grid = false)
		{
			int Width = 108;
			int Height = 172;
			//smallCard - true (54 x 86), false (72 x 115（114.67）)
			//【参考】オリジナルサイズ：Width x Height = 108 x 172
			if(grid)
			{
				int offset = smallCards? 11 : 2;
				if(smallCards)
					cvsGr.FillRectangle(Brushes.Green, x * 76 + offset, y * 119 + offset, Width / 2, Height / 2);
				else
					cvsGr.FillRectangle(Brushes.Green, x * 76 + offset, y * 119 + offset, Width * 2 / 3, Height * 2 / 3);
			}
			else
			{
				if(smallCards)
					cvsGr.FillRectangle(Brushes.Green, x, y, Width / 2, Height / 2);
				else
					cvsGr.FillRectangle(Brushes.Green, x, y, Width * 2 / 3, Height * 2 / 3);
			}
			//Discardedサインを立てる
			Deck[cardNo].Discarded = true;
			//pbCanvasを貼る
			picBox.Image = pbCanvas;
		}

		//Cardインスタンス配列からカードを配る
		public void Deal(int no_of_players, int no_of_cards)
		{
			for(int i = 0; i < no_of_cards; i++)
				for(int j = 0; j < no_of_players; j++)
					Deck[i * no_of_players + j].Owner = j + 1;
			DealtCards += no_of_players * no_of_cards;
		}

		//Cardインスタンス配列からカードを一枚引く
		public int Draw(int player)
		{
			Deck[DealtCards++].Owner = player;
			return (53 - DealtCards);		//戻り値は残カード数（0ならゲーム終了）
		}
	}

	/////////////////////////
	//Card（トランプ）構造体
	/////////////////////////
	public struct Card						//合計16バイト
	{
		public int Suit {get; set;}			//"Clover" - 0, "Diamond" - 1, "Heart" - 2, "Spade" - 3, "Joker" - 4
		public int Number {get; set;}		//1 - 13（BackとJokerでは0）
		public int Owner {get; set;}		//1から始まるオーナー（プレーヤー）番号－"0"は一人ゲームまたは未配布を意味する
		public bool Shown {get; set;}		//表示を表にするか、裏にするかのフラグ（裏の場合は常にBackを表示）
		public bool Discarded {get; set;}	//捨てられた（以降はゲームで使われない）が否か
	}
}
