using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Media;
using System.Windows.Forms;
using System.Diagnostics;
using ProtoBuf;

namespace NetVision {
    public partial class FormMain : Form {

        #region メンバ


        /// <summary>
        /// カードリスト
        /// </summary>
        public CardList List;
    
        /// <summary>
        /// 裏向きカードリスト
        /// </summary>
        public CardList BackList;

        /// <summary>
        /// Socket通信
        /// </summary>
        public Connection Socket { get; set; }

        /// <summary>
        /// 接続が確立中
        /// </summary>
        public bool IsConnected { get; set; }

        public List<Command> CommandLog { get; set; }

        /// <summary>
        /// 受信用のデリゲート
        /// </summary>
        private delegate void ReceiveDelegate( Command com );

        private SoundPlayer Sound = new SoundPlayer();

        #endregion

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public FormMain() {
            InitializeComponent();
            FormMain.CheckForIllegalCrossThreadCalls = false;
            CommandLog = new List<Command>();
        }

        #region 基本的なイベント

        /// <summary>
        /// ロードイベント
        /// </summary>
        private void Form1_Load( object sender, EventArgs e ) {


            Size = Properties.Settings.Default.WindowSize;
//            if (!Properties.Settings.Default.VisualMode) { BackgroundImage = null; }

            // カードリストのロード
            if (CardList.IsIniNew(Properties.Settings.Default.CardListIni, Properties.Settings.Default.CardListJson, Properties.Settings.Default.CardListBuf)) {
                MessageBox.Show("cardlist.iniが新しくなっているため更新します。\nこの変換処理には 60秒 程度かかります。\n[ OK ] を押してそのままお待ちください。", "Iniファイルの変換", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            List = CardList.Load(Properties.Settings.Default.CardListIni, Properties.Settings.Default.CardListJson, Properties.Settings.Default.CardListBuf);
            BackList = CardList.Load(null, Properties.Settings.Default.BackCardListJson, Properties.Settings.Default.BackCardListBuf);
            MyPlayer.AllList = List;
            MyPlayer.BackList = BackList;
            YourPlayer.AllList = List;
            YourPlayer.BackList = BackList;

            // 画像の設定
            string back_img = Application.StartupPath + Properties.Settings.Default.ImagePath + Properties.Settings.Default.BackImage;
            if (!File.Exists(back_img)) { throw new FileNotFoundException("裏向き画像ファイルが見つかりません。"); }
            CardBox.BackImage = Image.FromFile(back_img);
            string generic_img = Application.StartupPath + Properties.Settings.Default.ImagePath + Properties.Settings.Default.GenericImage;
            if (!File.Exists(generic_img)) { throw new FileNotFoundException("汎用画像ファイルが見つかりません。"); }
            CardBox.GenericImage = Image.FromFile(generic_img);
            labelMyPlayerName.Text = Properties.Settings.Default.PlayerName;

            buttonChatUseful1.Text = Properties.Settings.Default.ChatUseful1;
            buttonChatUseful2.Text = Properties.Settings.Default.ChatUseful2;
            buttonChatUseful3.Text = Properties.Settings.Default.ChatUseful3;

            // 接続コネクタ
            Socket = new Connection();
            Socket.ReceiveMessage += new EventHandler<MessageEventArgs>(OnReceiveMessage);
            Socket.SystemMessage += new EventHandler<MessageEventArgs>(OnSystemMessage);
            Socket.Connected += new EventHandler<MessageEventArgs>(OnConnected);
            Socket.UnConnected += new EventHandler<MessageEventArgs>(OnUnConnected);


        }

        /// <summary>
        /// フォーム閉じるとき
        /// </summary>
        private void FormMain_FormClosed( object sender, FormClosedEventArgs e ) {
            if (MyPlayer != null) { MyPlayer.Clear(); }
            if (YourPlayer != null) { YourPlayer.Clear(); }
            if (Socket != null) { Socket.Close(); }
            panelCardInfo.Clear();
            Properties.Settings.Default.Save();
        }

        /// <summary>
        /// フォームのリサイズ
        /// </summary>
        private void FormMain_SizeChanged( object sender, EventArgs e ) {
            MyPlayer.OnResize();
            YourPlayer.OnResize();
            Properties.Settings.Default.WindowSize = Size;
            Properties.Settings.Default.Save();
        }

        #endregion

        #region メニューバー

        private void 接続ToolStripMenuItem_Click( object sender, EventArgs e ) {

            FormConnect con = new FormConnect();
            DialogResult rs = con.ShowDialog();
            if (rs != DialogResult.OK) { return; }

            Properties.Settings.Default.PlayerName = con.PlayerName;
            Properties.Settings.Default.Port = con.Port;
            Properties.Settings.Default.IpAddr = con.IpAddr;
            MyPlayer.PlayerName = Properties.Settings.Default.PlayerName;
            labelMyPlayerName.Text = Properties.Settings.Default.PlayerName;

            if(con.ConnectionType == ServerType.Server){
                Socket.ServerStart(con.Port);
            }
            else if (con.ConnectionType == ServerType.Client) {
                Socket.ClientStart(con.IpAddr, con.Port);
            }
            else { return; }

            buttonYourStart.Visible = false;
            buttonYourTurn.Visible = false;
        }

        private void デッキを開くOToolStripMenuItem_Click( object sender, EventArgs e ) {
            if (!LoadDeckByHDD(MyPlayer)) { return; }
            buttonMyStart.Visible = false;
            MyPlayer.SendCommand( new Command { Type = CommandType.DeckLoad, Player = MyPlayer.GetShadow() });
            SendToRemote(new Command { Type = CommandType.UpdateAll, Player = MyPlayer.GetShadow(), Remote = true });
        }

        private void 一人対戦ToolStripMenuItem_Click( object sender, EventArgs e ) {
            if (!LoadDeckByHDD(YourPlayer)) { return; }
            YourPlayer.SendCommand(new Command { Type = CommandType.DeckLoad, Player = YourPlayer.GetShadow() });
            buttonYourStart.Visible = false;
            buttonYourTurn.Text = "ゲーム開始";
        }

        private void 終了EToolStripMenuItem_Click( object sender, EventArgs e ) {
            Application.Exit();
        }
        private void 現在の状態を保存ToolStripMenuItem_Click( object sender, EventArgs e ) {
            if (MyPlayer.State == PlayerState.UnLoad) { return; }
            saveFileDialogShadow.InitialDirectory = Properties.Settings.Default.SaveFolder;
            DialogResult rs = saveFileDialogShadow.ShowDialog();
            if (rs != DialogResult.OK) { return; }
            Properties.Settings.Default.SaveFolder = saveFileDialogShadow.InitialDirectory;
            using (FileStream fs = new FileStream(saveFileDialogShadow.FileName, FileMode.Create)) {
                Serializer.Serialize<PlayerShadow>(fs, MyPlayer.GetShadow());
            }
        }

        private void 以前の状態を復元ToolStripMenuItem_Click( object sender, EventArgs e ) {
            openFileDialogShadow.InitialDirectory = Properties.Settings.Default.SaveFolder;
            saveFileDialogShadow.Filter = "リプレイファイル|*.vissav|すべてのファイル|*.*";
            DialogResult rs = openFileDialogShadow.ShowDialog();
            if (rs != DialogResult.OK) { return; }
            Properties.Settings.Default.SaveFolder = openFileDialogShadow.InitialDirectory;
            using (FileStream fs = new FileStream(openFileDialogShadow.FileName, FileMode.Open)) {
                PlayerShadow pl = Serializer.Deserialize<PlayerShadow>(fs);
                MyPlayer.Type = PlayerType.Control;
                MyPlayer.LoadFromShadow(pl);
            }
            SendToRemote(new Command { Type = CommandType.UpdateAll, Player = MyPlayer.GetShadow() });
            buttonMyStart.Visible = false;
        }

        private void 場のクリアDToolStripMenuItem_Click( object sender, EventArgs e ) {
            if (MessageBox.Show("本当に全ての場をクリアしてよろしいですか？", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return; }
            MyPlayer.SendCommand(new Command { Type = CommandType.Clear });
            buttonMyStart.Visible = true;
            buttonMyTurn.Text = "ゲーム開始";
            buttonYourStart.Visible = true;
            buttonYourTurn.Text = "ゲーム開始";
        }

        private void オプションOToolStripMenuItem_Click( object sender, EventArgs e ) {
            FormConfig config = new FormConfig();
            if (config.ShowDialog() != DialogResult.OK) { return; }
            if (Size != Properties.Settings.Default.WindowSize) { Size = Properties.Settings.Default.WindowSize; }
            if (config.DisplayChanged) { Redraw(); }
            buttonChatUseful1.Text = Properties.Settings.Default.ChatUseful1;
            buttonChatUseful2.Text = Properties.Settings.Default.ChatUseful2;
            buttonChatUseful3.Text = Properties.Settings.Default.ChatUseful3;
            panelCardInfo.Height = Properties.Settings.Default.UseToolTip ? 390 : 680;
        }
        private void 切断QToolStripMenuItem_Click( object sender, EventArgs e ) {
            Socket.Close();
        }

        private void 場を同期させるUToolStripMenuItem_Click( object sender, EventArgs e ) {
            if (Socket.IsServer != ServerType.Undef) {
                MyPlayer.SendCommand(new Command { Type = CommandType.UpdateAll, Player = MyPlayer.GetShadow() });
            }
        }
        private void デッキエディタEToolStripMenuItem_Click( object sender, EventArgs e ) {
            FormDeckEdit edit = new FormDeckEdit();
            edit.List = List;
            edit.Show();
/*
            string path = Properties.Settings.Default.DeckEditerPath;
            try {
                if (path != "" && File.Exists(path)) { Process.Start(path); }
                else { MessageBox.Show("オプションから外部デッキエディタを指定して下さい", "デッキエディタの実行", MessageBoxButtons.OK, MessageBoxIcon.Information); }
            }
            catch (Exception ex) {
                MessageBox.Show(ex.Message);
            }
 */ 
        }
        private void バージョン情報VToolStripMenuItem_Click( object sender, EventArgs e ) {
            MessageBox.Show(Application.ProductName + " ver." + Application.ProductVersion , "バージョン情報", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void リプレイを保存RToolStripMenuItem_Click( object sender, EventArgs e ) {
            saveFileDialogShadow.Filter = "リプレイファイル|*.visrep|すべてのファイル|*.*";
            if (saveFileDialogShadow.ShowDialog() != DialogResult.OK) { return; }
            using(FileStream fs = new FileStream(saveFileDialogShadow.FileName, FileMode.Create, FileAccess.Write)){
                Serializer.Serialize<List<Command>>(fs, CommandLog);
            }
        }
        private void 乱数を発生RToolStripMenuItem_Click( object sender, EventArgs e ) {
            Random rand = new Random();
            MyPlayer.SendCommand(new Command { Type = CommandType.Rand, Message = rand.Next(100).ToString() });
        }
        private void マニュアルMToolStripMenuItem_Click( object sender, EventArgs e ) {
            if (!File.Exists(Application.StartupPath + Properties.Settings.Default.ManualPath)) {
                MessageBox.Show("ファイルが見つかりません"); return;
            }
            System.Diagnostics.Process.Start(@"file:///" + Application.StartupPath + Properties.Settings.Default.ManualPath);
        }

        #endregion


        #region 各種処理

        /// <summary>
        /// ファイルからデッキをロード
        /// </summary>
        /// <param name="player">プレイヤーコントロール</param>
        private bool LoadDeckByHDD(Player player) {

            openFileDialogDeck.FileName = Properties.Settings.Default.DeckFolder;
            // ファイルダイアログ
            if (openFileDialogDeck.ShowDialog() != DialogResult.OK) { return false; }
            string file = openFileDialogDeck.FileName;
            if (!File.Exists(file)) { return false; }
            Properties.Settings.Default.DeckFolder = openFileDialogDeck.FileName;

            player.Clear();

            player.Type = PlayerType.Control;

            ShowStatus( "デッキレシピ読み込み中" );
            Application.DoEvents();
            player.LoadRecipe(file);
            ShowStatus( "山札をセットアップ" );
            Application.DoEvents();
            player.SetupDeck();
            ShowStatus( "デッキ読み込み完了" );
            buttonMyTurn.Text = "ゲーム開始";
            player.State = PlayerState.UnStart;
            return true;
        }

        /// <summary>
        /// チャットにログを表示
        /// </summary>
        public void WriteChatLog(string message) {
            if (!IsDisposed) {
                try {
                    richTextChatLog.AppendText(message + "\n");
                    richTextChatLog.ScrollToCaret();
                }
                catch { }
            }
        }

        /// <summary>
        /// ステータスバーに表示
        /// </summary>
        /// <param name="message">表示するメッセージ</param>
        public void ShowStatus( string message ) {
            toolStripStatusState.Text = message;
        }

        /// <summary>
        /// wavを再生
        /// </summary>
        /// <param name="filename">ファイル名</param>
        public void PlaySound( string filename ) {
            Sound.SoundLocation = Application.StartupPath + Properties.Settings.Default.WavPath + filename;
            if (!File.Exists(Sound.SoundLocation)) { return; }
            Sound.Play();
        }

        /// <summary>
        /// ログを書かされるイベント
        /// </summary>
        private void panelMyInfo_LogMessage( object sender, LogMessageArgs e ) {
            Player player = (Player)sender;
            if (e.Sound != null) { PlaySound(e.Sound); }
            if (e.Message == "") { return; }
            richTextChatLog.AppendText((player.IsUpSide) ? "■" : "◇");
            richTextChatLog.AppendText(e.Message + "\n");
            if (e.Color != null) { 
                richTextChatLog.Select(richTextChatLog.Text.Length - e.Message.Length - 1, e.Message.Length + 1);
                richTextChatLog.SelectionColor = e.Color;
            }
            if (e.Card != null) {
                int name_head = richTextChatLog.Find(e.Card, richTextChatLog.Text.Length - e.Message.Length - 1, RichTextBoxFinds.Reverse);
                if (name_head >= 0) { richTextChatLog.SelectionFont = new Font(richTextChatLog.Font, FontStyle.Bold); }
            }
            richTextChatLog.ScrollToCaret();
            ShowStatus(e.Message);

        }
        /// <summary>
        /// チャット便利ボタン
        /// </summary>
        private void buttonChatUseful1_Click( object sender, EventArgs e ) {
            Button bt = (Button)sender;
            WriteChatLog("◇「" + bt.Text + "」");
            SendToRemote(new Command { Type = CommandType.Talk, Message = bt.Text });
        }

        /// <summary>
        /// ステータスバーのガイド
        /// </summary>
        private void MyPlayer_CardHover( object sender, MessageEventArgs e ) {
            String[] strs = e.Message.Split('|');
            if (strs.Length == 2) {
                toolStripStatusLabelDouble.Text = strs[0];
                toolStripStatusLabelWheel.Text = strs[1];
            }
        }
        /// <summary>
        /// チャットログのリンクをクリック
        /// </summary>
        private void richTextChatLog_LinkClicked( object sender, LinkClickedEventArgs e ) {
            System.Diagnostics.Process.Start(e.LinkText);
        }

        /// <summary>
        /// コントロールを全て再描画
        /// </summary>
        public void Redraw() {
//            if (Properties.Settings.Default.VisualMode) { BackgroundImage = ( (System.Drawing.Image)( ( new System.ComponentModel.ComponentResourceManager(typeof(FormMain)) ).GetObject("$this.BackgroundImage") ) ); }
//            else { BackgroundImage = null; }
            Player[] pls = { MyPlayer, YourPlayer };
            foreach (Player pl in pls) {
                foreach (AreaControl area in pl.Areas.Values) {
                    area.RedrawCards();
                    foreach (CardBox card in area.Cards) {
                        card.Redraw();
                    }
                }
            }
        }

        #endregion


        #region 通信関連

        /// <summary>
        /// メッセージを受けた時
        /// </summary>
        private void OnReceiveMessage(object sender, MessageEventArgs e) {
            Command com = e.Command;
            this.Invoke(new ReceiveDelegate(ExecCommand), com);

        }

        /// <summary>
        /// こちらのプレイヤーがコマンドを実行したとき
        /// </summary>
        private void panelMyInfo_CommandSending( object sender, MessageEventArgs e ) {
            SendToRemote(e.Command);
        }

        /// <summary>
        /// コマンド実行時に記録
        /// </summary>
        private void MyPlayer_CommandReceiving( object sender, MessageEventArgs e ) {
            Command com = e.Command;
            com.Mine = !( (Player)sender ).IsUpSide;
            CommandLog.Add(com);
        }

        /// <summary>
        /// リモートのコマンドを実行
        /// </summary>
        /// <param name="com">コマンド</param>
        private void ExecCommand(Command com) {
            switch (com.Type) {
                case CommandType.Connect:
                    WriteChatLog("「" + com.Message + "」さんとの試合を始めます");
                    labelYourPlayerName.Text = com.Message;
                    YourPlayer.PlayerName = com.Message;
                    break;
                case CommandType.Talk:
                    WriteChatLog("■「" + com.Message + "」");
                    PlaySound("talk.wav");
                    break;
                default:
                    YourPlayer.ReceiveCommand(com);
                    break;
            }
        }

        /// <summary>
        /// システムメッセージを受けた時
        /// </summary>
        private void OnSystemMessage(object sender, MessageEventArgs e) {
            WriteChatLog(e.Message);
        }

        /// <summary>
        /// 接続が確立したとき
        /// </summary>
        private void OnConnected( object sender, MessageEventArgs e ) {
            PlaySound("connect.wav");
            IsConnected = true;
            YourPlayer.Type = PlayerType.Network;
            SendToRemote(new Command { Type = CommandType.Connect, Message = MyPlayer.PlayerName });
            if (MyPlayer.State != PlayerState.UnLoad) {
                SendToRemote(new Command { Type = CommandType.UpdateAll, Player = MyPlayer.GetShadow(), Remote = true }); // 場の内容を送りつける
            }
        }

        /// <summary>
        /// 接続が切れたとき
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnUnConnected( object sender, MessageEventArgs e ) {
            IsConnected = false;
        }


        /// <summary>
        /// リモートにコマンドを送信
        /// </summary>
        public void SendToRemote(Command command) {
            if (!IsConnected) { return; }
            Socket.Send(command);
        }

        /// <summary>
        /// チャット発言
        /// </summary>
        private void buttonChatSubmit_Click( object sender, EventArgs e ) {
            if (textBoxChatMessage.Text == "") { return; }
            WriteChatLog("◇「" + textBoxChatMessage.Text + "」");
            SendToRemote(new Command { Type = CommandType.Talk, Message = textBoxChatMessage.Text });
            textBoxChatMessage.Text = "";
        }

        /// <summary>
        /// Enterキーでも発言
        /// </summary>
        private void textBoxChatMessage_KeyDown( object sender, KeyEventArgs e ) {
            if (e.KeyCode == Keys.Enter) { buttonChatSubmit_Click(sender, e); }
        }


        #endregion



































    }
}
