package game.net.test2;

import static game.constant.GameConstant.DOWN_CNT;
import static game.constant.GameConstant.NEXT_MAX;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

import com.haru.component.GameField;
import com.haru.component.Mino;
import com.haru.component.NextNextField;
import com.haru.component.TetraMino;
import com.haru.component.gui.MainPanel;
import com.haru.net.OutData2;
import com.haru.player.Player;
import com.haru.util.ArrayRandom;



public class GameServer {

	public GameServer(MainPanel mainPanel, int playerNo) {
		this.mainPanel = mainPanel;
		this.playerNo = playerNo;
	}


	//--------------------------------------------------------------------------//
	// ソケット
	//--------------------------------------------------------------------------//

	// 自身のソケット
	private ObjectOutputStream out;
	private BufferedReader in;

	// クライアントと接続後、ストリームの取得
	public ServerSocket create(int port) {
		ServerSocket svsock;
		try {
			svsock = new ServerSocket(port);
		} catch (IOException e1) {
			e1.printStackTrace();
			System.out.println("ポート重複？");
			return null;
		}

		return svsock;
	}

	/**
	 *
	 * @param svsock
	 * @return 取得したSocketは、きちんとcloseしろ
	 */
	public Socket listen(ServerSocket svsock) {
		Socket sock;
		try {
			sock = svsock.accept();
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("クライアントへの接続失敗");
			return null;
		}

		try {
			in = new BufferedReader(new InputStreamReader(
					sock.getInputStream()
					));
			out = new ObjectOutputStream(sock.getOutputStream());

		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("ストリーム作成失敗");
			return null;
		}

		return sock;
	}
	// クローズ
//	public void close() {
//		try {
//			in.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		try {
//			out.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}

	//--------------------------------------------------------------------------//
	// 他のソケットの連携
	//--------------------------------------------------------------------------//
	private int playerNo;
	private GameServer other;
	private OutData2 otherData;	 // 別のスレッドから渡してもらう

	public void setOther(GameServer s) {
		this.other = s;
	}
	public void setOutData(OutData2 data) {
		this.otherData = data;
	}





	//--------------------------------------------------------------------------//
	//--------------------------------------------------------------------------//
	//--------------------------------------------------------------------------//


	private MainPanel mainPanel;

	public MainPanel getMainPanel() {
		return mainPanel;
	}

	public GameServer(MainPanel mainPanel) {
		this.mainPanel = mainPanel;
	}



	//--------------------------------------------------------------------------//
	private Player player;
	private GameField gameField;		// mainPanel内に隠蔽されているけど、get()を定義していない
	private NextNextField nextField;

	//--------------------------------------------------------------------------//
	// ネクスト用
	//--------------------------------------------------------------------------//
	private int next[] = new int[NEXT_MAX];


	private ArrayRandom r =new ArrayRandom( new int[] {
				Mino.T_I,
				Mino.T_O,
				Mino.T_S,
				Mino.T_Z,
				Mino.T_L,
				Mino.T_J,
				Mino.T_T
			}
		);
	private void next(NextNextField nextField) {

		// 前のmapの情報を消す
		for (int i = 0; i < next.length; i++)
			nextField.putBlock(i, Mino.T_EMPTY, TetraMino.getBlock(next[i]));

		// ネクストを新たに取得
		for (int i = 0; i < next.length - 1; i++)
			next[i] = next[i+1];
		next[next.length - 1] = r.getNext();

		// 配置
		for (int i = 0; i < next.length; i++)
			nextField.putBlock(i, next[i], TetraMino.getBlock(next[i]));
	}



	void gameinit() {
		// フィールドの準備
		gameField = new GameField();
		nextField = new NextNextField();
		getMainPanel().init(gameField, nextField);

		// プレイヤーの定義
		player = new Player(gameField);

		// ネクストの準備
		for (int i = 0; i < next.length; i++) {
			next[i] = r.getNext();
		}
		// ブロックの出現
		player.setCur( TetraMino.getBlock(next[0]) );
		next(nextField);

	}


	boolean gameloop(int loop) {

		// クライアントへ IN + OUT + OUT

		//--------------------------------------------------------------------------//
		// IN : socketから入力
		//--------------------------------------------------------------------------//
		try {
			int length;
			int[] ret;


			length = Integer.parseInt(in.readLine());
			ret = new int[length];

			for (int i = 0; i < length; i ++) {
				ret[i] = Integer.parseInt(in.readLine());
			}


			//--------------------------------------------------------------------------//

			// ブロックの位置を更新
			boolean isUpdate = player.update(ret, (loop % DOWN_CNT == 0));		// int[]の受け渡しではなくしていく
			// 位置が確定した場合
			if (!isUpdate) {

				// 次のブロックを準備する
				player.setCur( TetraMino.getBlock( next[0] ) );
				next(nextField);
				loop = 0;
			}


		} catch (NumberFormatException e1) {
			e1.printStackTrace();
			System.out.println("read失敗（数字でない）");
			return false;
		} catch (IOException e1) {
			e1.printStackTrace();
			System.out.println("read失敗（ストリームがいなくなった？）");
			return false;
		}


		// DRAW : INの確認画面
		getMainPanel().repaint();



		//--------------------------------------------------------------------------//
		// OUT : socketへ出力
		//--------------------------------------------------------------------------//
		try {

			OutData2 data = new OutData2(playerNo, gameField, nextField);
			out.writeObject(data);
			out.flush();
			System.out.println(playerNo + ":OutData (" + data.getNo() + ")");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("write失敗(ストリームがいない？)");
			return false;
		}

		//otherへ出力予約
		if (other != null) {
			OutData2 data = new OutData2(playerNo, gameField, nextField);
			other.setOutData(data);
		}
		//--------------------------------------------------------------------------//
		// OUT2: 対戦相手の情報をsocketへ送る
		//--------------------------------------------------------------------------//

		if (otherData == null) { // 最初にここを実行するスレッド(片方のスレッドのみ)

			// ダミーの情報をクライアントに送る
			try {
				out.writeObject(new OutData2(0, new GameField(), new NextNextField()));
				out.flush();
				System.out.println(playerNo + ":ダミーを送る");
			} catch (IOException e) {
				e.printStackTrace();

				return false;

			}


		} else if (otherData.isReady()) {
			try {
				out.writeObject(otherData);
				out.flush();
				System.out.println(playerNo + ":OutData (" + otherData.getNo() + ")");
			} catch (IOException e) {
				e.printStackTrace();

				return false;
			}

			otherData.init();
		}





		return true;
	}


	void gameend() {

		// 次のゲームのための準備

//		try {
//			in.reset();
//			out.reset();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
	}
}
