using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using MyScreenCap.Ctrl;

namespace MyScreenCap {

    /// <summary>
    /// メインフォーム
    /// </summary>
    public partial class FormMain : Form {

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        private static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);

        /// <summary>
        /// Rectangle Capture用
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct Rect {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        /// <summary>
        /// Settings
        /// </summary>
        private enum Settings {
            ModCtrl,
            ModAlt,
            ModShift,
            HotKey,
            CapMode,
            ToClipboard,
            ToFile,
            OutputDirPath,
            ImageFormat,
            ImageQuality,
            ImageColor,
            GifSaveMode,
        }

        /// <summary>
        /// Settings Value Type
        /// </summary>
        private enum ParamType {
            Bool,
            String,
            Int,
        }

        /// <summary>
        /// Capture Mode
        /// </summary>
        private enum CaptureMode {
            FullScreen = 0,
            ActiveWindow = 1,
            Rectangle = 2,
        }

        /// <summary>
        /// Capture Mode
        /// デフォルトはFullScreen
        /// </summary>
        private CaptureMode CapMode {
            get {
                if (this.radActiveWindow.Checked) {
                    return CaptureMode.ActiveWindow;
                } else if (this.radRectangle.Checked) {
                    return CaptureMode.Rectangle;
                } else {
                    return CaptureMode.FullScreen;
                }
            }
            set {
                switch (value) {
                    case CaptureMode.ActiveWindow:
                        this.radActiveWindow.Checked = true;
                        break;
                    case CaptureMode.Rectangle:
                        this.radRectangle.Checked = true;
                        break;
                    default:
                        this.radFullScreen.Checked = true;
                        break;
                }
            }
        }

        /// <summary>
        /// Copy To Clipboard
        /// </summary>
        private bool ToClipborad {
            get {
                return this.checkClipboard.Checked;
            }
            set {
                this.checkClipboard.Checked = value;
            }
        }

        /// <summary>
        /// Save To File
        /// </summary>
        private bool ToFile {
            get {
                return this.checkFile.Checked;
            }
            set {
                this.checkFile.Checked = value;
            }
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public FormMain() {
            InitializeComponent();
            LoadSettings();
            this.Icon = Icon.FromHandle(global::MyScreenCap.Properties.Resources.Icon.Handle);
            this.notifyIcon.Icon = this.Icon;
            this.notifyIcon.Visible = true;

        }

        /// <summary>
        /// checkFile CheckedChanged
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void checkFile_CheckedChanged(object sender, System.EventArgs e) {
            this.outputDirectory.Enabled = this.checkFile.Checked;
            this.saveImage.Enabled = this.checkFile.Checked;
        }

        /// <summary>
        /// btnCapNow Click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCapNow_Click(object sender, EventArgs e) {
            CapNow();
        }

        /// <summary>
        /// notifyIcon MouseClick
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void notifyIcon_MouseClick(object sender, MouseEventArgs e) {
            if (e.Button == System.Windows.Forms.MouseButtons.Left) {
                this.Visible = true;
                this.Activate();
            }
        }

        /// <summary>
        /// menuShow Click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuShow_Click(object sender, EventArgs e) {
            this.Visible = true;
            this.Activate();
        }

        /// <summary>
        /// menuExit Click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuExit_Click(object sender, EventArgs e) {

            this.notifyIcon.Visible = false;
            this.Close();
        }

        /// <summary>
        /// WndProc
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m) {
            const int WM_SYSCOMMAND = 0x112;
            const int SC_RESTORE = 0xf120;
            const int SC_MINIMIZE = 0xf020;
            const int SC_MAXIMIZE = 0xf030;

            if (this.hotKeyRegister != null && this.hotKeyRegister.HotKeyPressed(m)) {
                /* ホットキーの処理 */
                CapNow();
            }

            if (m.Msg == WM_SYSCOMMAND) {
                int wparam = m.WParam.ToInt32() & 0xfff0;
                switch (wparam) {
                    case SC_MINIMIZE:
                        // 最小化する前
                        //base.WndProc(ref m); // ここをコメント化すると最小化されない
                        this.Visible = false;
                        // 最小化した後
                        return;

                    case SC_MAXIMIZE:
                        // 最大化する前
                        base.WndProc(ref m); // ここをコメント化すると最大化されない
                        // 最大化した後
                        return;

                    case SC_RESTORE:
                        // 元のサイズに戻す前
                        base.WndProc(ref m); // ここをコメント化すると元のサイズに戻されない
                        // 元のサイズに戻した後
                        return;

                }
            }

            base.WndProc(ref m);
        }

        /// <summary>
        /// Capture Now!
        /// </summary>
        private void CapNow() {
            Bitmap bmp = null;
            switch (CapMode) {
                case CaptureMode.FullScreen:
                    bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
                    using (Graphics g = Graphics.FromImage(bmp)) {
                        g.CopyFromScreen(new Point(0, 0), new Point(0, 0), bmp.Size);
                    }
                    break;

                case CaptureMode.ActiveWindow:
                    Rectangle bounds;
                    IntPtr foregroundWindowsHandle = GetForegroundWindow();
                    Rect rect = new Rect();
                    GetWindowRect(foregroundWindowsHandle, ref rect);
                    int left = rect.Left;
                    int top = rect.Top;
                    int width = rect.Right - rect.Left;
                    int height = rect.Bottom - rect.Top;
                    if (left < 0) {
                        width += left;
                        left = 0;
                    }
                    if (top < 0) {
                        height += top;
                        top = 0;
                    }
                    if (left + width > Screen.PrimaryScreen.Bounds.Width) {
                        width += (Screen.PrimaryScreen.Bounds.Width - left - width);
                    }
                    if (top + height > Screen.PrimaryScreen.Bounds.Height) {
                        height += (Screen.PrimaryScreen.Bounds.Height - top - height);
                    }
                    bounds = new Rectangle(left, top, width, height);
                    bmp = new Bitmap(bounds.Width, bounds.Height);

                    using (Graphics g = Graphics.FromImage(bmp)) {
                        g.CopyFromScreen(new Point(bounds.Left, bounds.Top), Point.Empty, bounds.Size);
                    }
                    break;

                case CaptureMode.Rectangle:
                    FormRectCap formRectCap = new FormRectCap();
                    if (formRectCap.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
                        bmp = formRectCap.BmpRect;
                    }
                    break;
            }
            if (bmp != null) {
                if (ToClipborad) {
                    Clipboard.SetImage(bmp);
                }

                if (ToFile) {
                    System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
                    stopWatch.Start();
                    if (this.saveImage.SaveImageToFile(this.outputDirectory.DirectoryPath, "MyScreenCap_", bmp)) {
                        stopWatch.Stop();
                        CustomMsgBox.Show(this, "画像を保存しました！" + Environment.NewLine + "処理時間:" + stopWatch.ElapsedMilliseconds.ToString() + "[msec]", "Infomation", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    } else {
                        stopWatch.Stop();
                        CustomMsgBox.Show(this, "画像の保存に失敗！", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }

                bmp.Dispose();
            }

            // Save Settings
            this.SaveSettings();
        }

        /// <summary>
        /// 設定のSave
        /// </summary>
        private void SaveSettings() {
            Dictionary<string, string> settingsDic = new Dictionary<string, string> { };
            settingsDic.Add(Settings.ModCtrl.ToString(), this.hotKeyRegister.ModCtrl.ToString());
            settingsDic.Add(Settings.ModAlt.ToString(), this.hotKeyRegister.ModAlt.ToString());
            settingsDic.Add(Settings.ModShift.ToString(), this.hotKeyRegister.ModShift.ToString());
            settingsDic.Add(Settings.HotKey.ToString(), ((int)this.hotKeyRegister.HotKey).ToString());
            settingsDic.Add(Settings.CapMode.ToString(), ((int)CapMode).ToString());
            settingsDic.Add(Settings.ToClipboard.ToString(), ToClipborad.ToString());
            settingsDic.Add(Settings.ToFile.ToString(), ToFile.ToString());
            settingsDic.Add(Settings.OutputDirPath.ToString(), this.outputDirectory.DirectoryPath);
            settingsDic.Add(Settings.ImageFormat.ToString(), ((int)this.saveImage.ImgFormat).ToString());
            settingsDic.Add(Settings.ImageQuality.ToString(), this.saveImage.ImgQuality.ToString());
            settingsDic.Add(Settings.ImageColor.ToString(), ((int)this.saveImage.ImgColor).ToString());
            settingsDic.Add(Settings.GifSaveMode.ToString(), this.saveImage.GifSaveMode.ToString());

            Ctrl.SaveSettings.Save(settingsDic);
        }

        /// <summary>
        /// 設定のLoad
        /// </summary>
        private void LoadSettings() {
            Dictionary<string, string> settingsDic = Ctrl.SaveSettings.Load();

            object val;
            if ((val = GetSetting(settingsDic, Settings.ModCtrl.ToString(), ParamType.Bool)) != null) {
                this.hotKeyRegister.ModCtrl = (bool)val;
            }
            if ((val = GetSetting(settingsDic, Settings.ModAlt.ToString(), ParamType.Bool)) != null) {
                this.hotKeyRegister.ModAlt = (bool)val;
            }
            if ((val = GetSetting(settingsDic, Settings.ModShift.ToString(), ParamType.Bool)) != null) {
                this.hotKeyRegister.ModShift = (bool)val;
            }
            if ((val = GetSetting(settingsDic, Settings.HotKey.ToString(), ParamType.Int)) != null) {
                this.hotKeyRegister.HotKey = (Ctrl.HotKeyRegister.HotKeys)val;
            }
            if ((val = GetSetting(settingsDic, Settings.CapMode.ToString(), ParamType.Int)) != null) {
                CapMode = (CaptureMode)val;
            }
            if ((val = GetSetting(settingsDic, Settings.ToClipboard.ToString(), ParamType.Bool)) != null) {
                ToClipborad = (bool)val;
            }
            if ((val = GetSetting(settingsDic, Settings.ToFile.ToString(), ParamType.Bool)) != null) {
                ToFile = (bool)val;
            }
            if ((val = GetSetting(settingsDic, Settings.OutputDirPath.ToString(), ParamType.String)) != null) {
                this.outputDirectory.DirectoryPath = (string)val;
            }
            if ((val = GetSetting(settingsDic, Settings.ImageFormat.ToString(), ParamType.Int)) != null) {
                this.saveImage.ImgFormat = (SaveImage.SaveImageFormat)val;
            }
            if ((val = GetSetting(settingsDic, Settings.ImageQuality.ToString(), ParamType.Int)) != null) {
                this.saveImage.ImgQuality = uint.Parse(val.ToString());
            }
            if ((val = GetSetting(settingsDic, Settings.ImageColor.ToString(), ParamType.Int)) != null) {
                this.saveImage.ImgColor = (SaveImage.SaveImageColor)val;
            }
            if ((val = GetSetting(settingsDic, Settings.GifSaveMode.ToString(), ParamType.Bool)) != null) {
                this.saveImage.GifSaveMode = (bool)val;
            }
        }

        /// <summary>
        /// Settingの取得
        /// </summary>
        /// <param name="settingsDic">設定情報Dic</param>
        /// <param name="paramName">パラメータ名</param>
        /// <param name="paramType">パラメータのタイプ</param>
        /// <returns>null:取得失敗,その他:取得成功</returns>
        private object GetSetting(Dictionary<string, string> settingsDic, string paramName, ParamType paramType) {
            object setting = null;

            if (settingsDic == null) {
                return setting;
            }

            if (!settingsDic.ContainsKey(paramName)) {
                return setting;
            }

            switch (paramType) {
                case ParamType.Bool:
                    bool boolVal;
                    if (bool.TryParse(settingsDic[paramName], out boolVal)) {
                        setting = boolVal;
                    }
                    break;
                case ParamType.String:
                    setting = settingsDic[paramName];
                    break;
                case ParamType.Int:
                    int intVal;
                    if (int.TryParse(settingsDic[paramName], out intVal)) {
                        setting = intVal;
                    }
                    break;
                default:
                    break;
            }

            return setting;
        }

    }
}
