using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Formatting;
//using Newtonsoft.Json;
//using Newtonsoft.Json.Serialization;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Ionic.Zip;

namespace JsonClient
{
    class ExtentsionCalendar : PictureBox
    {
        int _dtf;//指定年月の初日の曜日を取得
        int _dte;//指定年月の日数
        int _dtbei;//指定年月の前月の日数
        int _d;//今日の日付
        string _nowst;//今日の日付

        List<Label> arname = new List<Label>();//曜日名
        List<Label> ardt = new List<Label>();//日付のLabel
        List<Label> nowl = new List<Label>();//当月の日付のLabel
        const int PX_SIZE = 250;
        const int PY_SIZE = 165;
        const int TX_SIZE = 30;
        const int TY_SIZE = 18;
        Font fnt = new Font("MS UI Gothic", 13, FontStyle.Bold); //フォントサイズ
        Font fnts = new Font("MS UI Gothic", 13, FontStyle.Regular); //フォントサイズ

        private Thread IcThread;
        private Thread IcTeikiThread;
        private Thread CashThread;
        private Thread WaitThread;

        private delegate void GetStatusType(CalendarType type);
        // TODO ラベルを作成するメソッド作成、ラベルを作成するメソッド用のデリゲートを作成?
        private delegate void ShowPictureBoxEvent();

        private ManualResetEvent IcResetEvent;
        private ManualResetEvent IcTeikiResetEvent;
        private ManualResetEvent CashResetEvent;

        private object syncObject = new object();
        private bool isShowCal = false;

        private ProcType CalendarProcType { get; set; }

        private Dictionary<string, string> DateLabels = new Dictionary<string, string>();

        // TODO 更新中フラグ
        private bool isUpdating = false;

        private bool isShowCalendar 
        {
            get
            {
                return isShowCal;
            }
            set
            {
                lock(syncObject)
                {
                    isShowCal = value;
                }
            }
        }

        private TextBox textBox1;
        private TextBox textBox2;

        enum CalendarType
        {
            ICDtl,

            ICTeikiDtl,

            CashDtl,

            None
        }

        enum ProcType
        {
            Daily,

            Monthly,
        }

        enum StatusType
        {
            Disable,

            Available,

            Reserved
        }

        //指定年月の初日の曜日の読み取り
        public int dtf
        {
            get { return this._dtf; }
        }
        //指定年月の日数の読み取り
        public int dte
        {
            get { return this._dte; }
        }
        //指定年月の前月の日数の読み取り
        public int dtbei
        {
            get { return this._dtbei; }
        }
        //今日の日付の読み取り
        public int d
        {
            get { return this._d; }
        }
        //今日の日付 年 月
        public string nowst
        {
            get { return this._nowst; }
        }

        public ExtentsionCalendar()
        {
        }

        public void UpdateCalendar()
        {
            IcResetEvent = new ManualResetEvent(false);
            IcTeikiResetEvent = new ManualResetEvent(false);
            CashResetEvent = new ManualResetEvent(false);

            // 読み込み中状態に設定
            isShowCalendar = false;
            ShowPictureBox();


            WaitThread = new Thread(new ThreadStart(ModifyCalendarData));
            WaitThread.Start();

            //wait状態になるまで処理を続行しない
            while (!isUpdating) { Thread.Sleep(100); }


            IcThread = new Thread(new ParameterizedThreadStart(this.CalendarProc));
            IcThread.Start(CalendarType.ICDtl);

            IcTeikiThread = new Thread(new ParameterizedThreadStart(this.CalendarProc));
            IcTeikiThread.Start(CalendarType.ICTeikiDtl);

            CashThread = new Thread(new ParameterizedThreadStart(this.CalendarProc));
            CashThread.Start(CalendarType.CashDtl);
        }

        public void Init(TextBox yearTextBox, TextBox monthTextBox)
        {
            // 画面の初期表示時のラジオボタンで判断を変更できるようにする
            CalendarProcType = ProcType.Daily;

            // コントロールを取得
            textBox1 = yearTextBox;
            textBox2 = monthTextBox;
        }

        private void CalendarProc(object calType)
        {
            CalendarType type = (CalendarType)calType;

            switch (type)
            {
                case CalendarType.ICDtl:
                case CalendarType.ICTeikiDtl:
                case CalendarType.CashDtl:
                    int iLoopCount = (CalendarProcType == ProcType.Daily ? 4 : 2);
                    //int iLoopCount = 1;

                    string path = "";

                    try
                    {
                        for (int i = 0; i < iLoopCount; i++)
                        {
                            path = @"http://localhost:65339/" + Enum.GetName(typeof(CalendarType), calType) + Enum.GetName(typeof(ProcType), CalendarProcType) + (i + 1).ToString() + ".ashx";

                            using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromMinutes(10) })
                            {
                                // Get
                                var response = client.GetAsync(path).Result;
                                response.EnsureSuccessStatusCode();

                                response.Content.LoadIntoBufferAsync();

                                switch (type)
                                {
                                    case CalendarType.ICDtl:
                                        SingletonStatusData.Instance().IcStatusList.AddRange(response.Content.ReadAsAsync<ReductionStatusData>().Result.Result);
                                        break;
                                    case CalendarType.ICTeikiDtl:
                                        SingletonStatusData.Instance().IcTeikiStatusList.AddRange(response.Content.ReadAsAsync<ReductionStatusData>().Result.Result);
                                        break;
                                    case CalendarType.CashDtl:
                                        SingletonStatusData.Instance().CashStatusList.AddRange(response.Content.ReadAsAsync<ReductionStatusData>().Result.Result);
                                        break;
                                }
                            }
                        }
                    }
                    catch (Exception)
                    {

                    }
                    finally
                    {
                        switch (type)
                        {
                            case CalendarType.ICDtl:
                                IcResetEvent.Set();
                                break;
                            case CalendarType.ICTeikiDtl:
                                IcTeikiResetEvent.Set();
                                break;
                            case CalendarType.CashDtl:
                                CashResetEvent.Set();
                                break;
                        }
                    }


                    break;
            }

        }

        private void ModifyCalendarData()
        {
            isUpdating = true;


            IcResetEvent.WaitOne();
            IcTeikiResetEvent.WaitOne();
            CashResetEvent.WaitOne();

            // TODO エラーがあれば処理
            // TODO どうやってエラーメッセージにする?
            if(!isUpdating)
            {
                isShowCalendar = false;
                this.Invoke(new MethodInvoker(ShowPictureBox));
            }

            List<ReductionStatusData.ResultData> result = new List<ReductionStatusData.ResultData>();
            result.AddRange(SingletonStatusData.Instance().IcStatusList);
            result.AddRange(SingletonStatusData.Instance().IcTeikiStatusList);
            result.AddRange(SingletonStatusData.Instance().CashStatusList);

            List<int> result2 = new List<int>();
            DateTime now = DateTime.Now;
            DateLabels.Clear();
            if (CalendarProcType == ProcType.Monthly)
            {
                now = new DateTime(now.Year - 1, 1, 1);
                // 二年分のデータを取得
                for (int i = 0; i < 24; i++)
                {
                    result2 = result.Where(x => x.Date == now.AddMonths(i).ToString("yyyy/MM")).Select(o => o.StatusCd).ToList();

                    if (result2.Count == 0)
                    {
                        // TODO 何か処理を実施
                        //throw new Exception("データなし");
                    }
                    else
                    {
                        // TODO 今のところ、取得した値の中で一番小さい値を取得しているけど、仕様書をみて正しい値を見る
                        DateLabels.Add(now.AddMonths(i).ToString("yyyyMMdd"), result2.Distinct().Min().ToString());
                    }
                }
            }
            else
            {
                now = new DateTime(now.Year, now.Month - 3, 1);

                int iLoopDateCount = 0;

                for (int iLoop = 0; iLoop < 4; iLoop++)
                {
                    iLoopDateCount += DateTime.DaysInMonth(now.Year, now.Month + iLoop);

                }


                // 今月+3か月分のデータを取得
                for (int i = 0; i < iLoopDateCount; i++)
                {
                    result2 = result.Where(x => x.Date == now.AddDays(i).ToString("yyyy/MM/dd")).Select(o => o.StatusCd).ToList();

                    if (result2.Count == 0)
                    {
                        // TODO 何か処理を実施
                        //throw new Exception("データなし");
                    }
                    else
                    {
                        // TODO 今のところ、取得した値の中で一番小さい値を取得しているけど、仕様書をみて正しい値を見る
                        DateLabels.Add(now.AddDays(i).ToString("yyyyMMdd"), result2.Distinct().Min().ToString());
                    }
                }
            }

            isShowCalendar = true;
            this.Invoke(new MethodInvoker(ShowPictureBox));
        }

        private void ShowPictureBox()
        {
            lock (syncObject)
            {
                if (!isShowCalendar)
                {
                    this.Image = null;

                    ////200x100サイズのImageオブジェクトを作成する
                    //Bitmap img = new Bitmap(200, 100);

                    ////ImageオブジェクトのGraphicsオブジェクトを作成する
                    //Graphics g = Graphics.FromImage(img);

                    ////全体を黒で塗りつぶす
                    //g.FillRectangle(Brushes.Black, g.VisibleClipBounds);
                    ////黄色い扇形を描画する
                    //g.DrawPie(Pens.Yellow, 60, 10, 80, 80, 30, 300);
                    //g.DrawString("読み込み中です...")
                    ////リソースを解放する
                    //g.Dispose();

                    ////作成した画像を表示する
                    //this.Image = img;
                    this.Size = new Size(PX_SIZE, PY_SIZE);
                    this.Image = new Bitmap(PX_SIZE,PY_SIZE);
                    using (Font font = new Font("メイリオ", 15))
                    using (Graphics graphics = Graphics.FromImage(this.Image))
                    {
                        graphics.DrawString("更新中です...",
                        font,
                        new SolidBrush(Color.Black),
                        new Point(PX_SIZE / 2, PY_SIZE / 2),
                        new StringFormat() {  LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center }
                        );
                    }

                    //this.Image = null;
                }
                else
                {
                    this.Image = null;

                    nowdt();
                    this.BackColor = Color.Aqua;
                    this.Size = new Size(PX_SIZE, PY_SIZE);
                    //曜日のLabelの作成
                    for (int k1 = 0; k1 < 7; ++k1)
                    {
                        Label namelb = new Label();
                        namelb.BackColor = Color.White;
                        namelb.Size = new Size(TX_SIZE, TY_SIZE);
                        namelb.Location = new Point(5 + (TX_SIZE + 5) * k1, 5);
                        namelb.Font = fnt;
                        namelb.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
                        arname.Add(namelb);
                        this.Controls.Add(namelb);
                    }
                    arname[0].Text = "日";
                    arname[1].Text = "月";
                    arname[2].Text = "火";
                    arname[3].Text = "水";
                    arname[4].Text = "木";
                    arname[5].Text = "金";
                    arname[6].Text = "土";
                    //日付のLabelの作成
                    for (int i2 = 0; i2 < 6; ++i2)
                    {
                        for (int i1 = 0; i1 < 7; ++i1)
                        {
                            Label lb = new Label();
                            lb.BackColor = Color.White;
                            lb.Size = new Size(TX_SIZE, TY_SIZE);
                            lb.Location = new Point(5 + (TX_SIZE + 5) * i1, TY_SIZE + 9 + (TY_SIZE + 5) * i2);
                            lb.Font = fnt;
                            lb.TextAlign = ContentAlignment.MiddleCenter;
                            ardt.Add(lb);
                            this.Controls.Add(lb);
                            lb.Click += new EventHandler(LB_CLICK);//イベント
                        }
                    }
                    //日曜日を赤くします
                    for (int fr = 0; fr < 6; ++fr)
                    {
                        ardt[7 * fr].ForeColor = Color.Red;
                    }

                    button1_Click(null, null);
                }
            }
        }

        //初期のカレンダーデータ
        public void nowdt()
        {
            DateTime ndt = DateTime.Now;
            int y = ndt.Year;
            int m = ndt.Month;
            _d = ndt.Day;
            txtdt(y.ToString(), m.ToString());
            _nowst = y.ToString() + "年" + m.ToString() + "月";
        }
        //Class Form1からTextBox1,TextBox2の値を取得
        public void txtdt(string txt1, string txt2)
        {
            string ntxt2 = (Convert.ToInt32(txt2) - 1).ToString();
            // '年月日を"2004/02/01"の形式にする
            string dtstring = txt1 + "/" + txt2 + "/" + "01";//指定日の日にち
            string dtstbe = txt1 + "/" + ntxt2 + "/" + "01";//指定年月の前月の日にち
            //指定日の曜日を取得する
            DateTime dt1;//指定年月
            DateTime dt2;//指定年月の前月
            //指定年月の月が1月の場合は前年の12月にする
            int ctxt1;
            if (txt2 == "1")
            {
                ctxt1 = Convert.ToInt32(txt1) - 1;
                dtstbe = ctxt1.ToString() + "/12/31";
            }
            if (DateTime.TryParse(dtstring, out dt1))
            {
                //何もしない
            }
            else
            {
                MessageBox.Show("日付に変換できない数字が入力されました。");
            }
            if (DateTime.TryParse(dtstbe, out dt2))
            {
                //何もしない
            }
            else
            {
                MessageBox.Show("日付に変換できない数字が入力されました。");
            }
            calk(dt1, dt2);

        }
        //
        private void calk(DateTime d1, DateTime d2)
        {
            //指定年月の初日の曜日を取得する
            _dtf = Convert.ToInt32(d1.DayOfWeek);

            //指定年月の日数を取得する
            _dte = DateTime.DaysInMonth(d1.Year, d1.Month);

            //指定年月の前月の日数を取得する
            _dtbei = DateTime.DaysInMonth(d2.Year, d2.Month);
        }

        //Labelをクリックすると枠が付きます。
        private void LB_CLICK(object sender, EventArgs e)
        {
            Label lb = sender as Label;
            int px = lb.Location.X;
            int py = lb.Location.Y;
            int lsw = lb.Width;
            int lsh = lb.Height;
            Pen pn = new Pen(Color.Blue, 5);
            Bitmap space = new Bitmap(this.Width, this.Height);
            Graphics g = Graphics.FromImage(space);
            g.DrawRectangle(pn, px - 3, py - 1, lsw + 3, lsh + 1);
            g.Dispose();
            this.Image = space;
        }

        public void button1_Click(object sender, EventArgs e)
        {
            nowl.Clear();
            txtdt(textBox1.Text, textBox2.Text);
            //一度カレンダーを空にする
            foreach (Label lbst in ardt)
            {
                lbst.Text = "";
            }

            int p = 0;//日にち
            for (int d = dtf; d < dte + dtf; ++d)
            {
                p += 1;
                ardt[d].Text = p.ToString();
                nowl.Add(ardt[d]);//当月の日付のLabelをAddする
            }
            //前月のカレンダー
            int be = 0;
            for (int bef = 0; bef < dtf; ++bef)
            {
                ardt[bef].Font = fnts;
                be += 1;
                ardt[bef].Text = (dtbei - dtf + be).ToString();
            }
            //翌月のカレンダー
            int af = 0;
            for (int aft = be + dte; aft < ardt.Count; ++aft)
            {
                ardt[aft].Font = fnts;
                af += 1;
                ardt[aft].Text = af.ToString();
            }
        }
    }
}