/*
 * Copyright 2015 yoshikixxxx.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 */
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;

using Poderosa.Commands;
using Poderosa.Forms;
using Poderosa.Plugins;
using Poderosa.Preferences;
using Poderosa.Sessions;
using Poderosa.Terminal;
using Poderosa.Util;


/********* アセンブリ情報 *********/
[assembly: PluginDeclaration(typeof(Poderosa.ExtendPaste.ExtendPastePlugin))]
/**********************************/


namespace Poderosa.ExtendPaste {
    /********* プラグイン情報 *********/
    [PluginInfo(
        ID = PLUGIN_ID,
        Version = VersionInfo.PODEROSA_VERSION,
        Author = VersionInfo.PROJECT_NAME,
        //Dependencies = "org.poderosa.terminalsessions;org.poderosa.usability;org.poderosa.optiondialog"
        Dependencies = "org.poderosa.terminalsessions;org.poderosa.usability;org.poderosa.optiondialog"
    )]
    /**********************************/




    /// <summary>
    /// <ja>ExtendPasteプラグインメインクラス</ja>
    /// </summary>
    internal class ExtendPastePlugin : PluginBase {
        // メンバー変数
        public const string PLUGIN_ID = "org.poderosa.extendpaste";
        private static ExtendPastePlugin _instance;
        private static ICoreServices _coreServices;
        private static StringResource _stringResource;
        private static ExtendPasteOptionsSupplier _optionSupplier;

        /// <summary>
        /// <ja>初期化</ja>
        /// </summary>
        public override void InitializePlugin(IPoderosaWorld poderosa) {
            base.InitializePlugin(poderosa);
            _instance = this;

            // 文字列リソース読み込み
            _stringResource = new StringResource("Poderosa.ExtendPaste.strings", typeof(ExtendPastePlugin).Assembly);
            ExtendPastePlugin.Instance.PoderosaWorld.Culture.AddChangeListener(_stringResource);

            // コマンド登録
            IPluginManager pm = poderosa.PluginManager;
            IExtensionPoint extp_cmd = pm.FindExtensionPoint("org.poderosa.terminalsessions.pasteCommand");
            extp_cmd.RegisterExtension(new ExtendPasteCommand());

            // オプションパネル登録
            IExtensionPoint extp_opt = pm.FindExtensionPoint("org.poderosa.optionpanel");
            extp_opt.RegisterExtension(new ExtendPastePanelExtension());

            // オプションクラス登録
            _optionSupplier = new ExtendPasteOptionsSupplier();
            _coreServices = (ICoreServices)poderosa.GetAdapter(typeof(ICoreServices));
            _coreServices.PreferenceExtensionPoint.RegisterExtension(_optionSupplier);
        }

        /// <summary>
        /// <ja>プラグイン終了</ja>
        /// </summary>
        public override void TerminatePlugin() {
            base.TerminatePlugin();
        }

        /// <summary>
        /// <ja>インスタンス</ja>
        /// </summary>
        public static ExtendPastePlugin Instance {
            get { return _instance; }
        }

        /// <summary>
        /// <ja>文字列リソース</ja>
        /// </summary>
        public StringResource Strings {
            get { return _stringResource; }
        }

        /// <summary>
        /// <ja>オプション</ja>
        /// </summary>
        public ExtendPasteOptionsSupplier ExtendPasteOptionSupplier {
            get { return _optionSupplier; }
        }
    }




    /// <summary>
    /// <ja>プラグイン実行クラス</ja>
    /// </summary>
    internal class ExtendPasteCommand : IPoderosaCommand {
        /// <summary>
        /// <ja>プラグイン実行</ja>
        /// </summary>
        public CommandResult InternalExecute(ICommandTarget target, params IAdaptable[] args) {
            IPoderosaView view;
            ITerminalSession session;

            // ビュー/セッション取得
            if (!GetViewAndSession(target, out view, out session)) return CommandResult.Ignored;

            // クリップボードデータ取得
            var clipboardData = Clipboard.GetDataObject();
            if (!clipboardData.GetDataPresent("Text")) return CommandResult.Ignored;
            string data = (string)clipboardData.GetData("Text");
            if (data == null) return CommandResult.Ignored;

            // オプション取得
            ExtendPasteOptions opt = ExtendPastePlugin.Instance.ExtendPasteOptionSupplier.OriginalOptions;

            // 改行存在チェック
            bool newLineFlg = ((data.IndexOfAny(new char[] { '\r', '\n' }) >= 0) || (data.Contains(Environment.NewLine))) ? true : false;

            // セッション名取得
            //ITerminalSession sessionName = (ITerminalSession)view.Document.OwnerSession.GetAdapter(typeof(ITerminalSession));

            // 確認ダイアログ表示
            if (((opt.UseAction == UseAction.NewLine) && (newLineFlg)) || (opt.UseAction == UseAction.Always)) {
                IPoderosaForm poderosaForm = view.ParentForm;
                ExtendPasteDialog Form = new ExtendPasteDialog(data, newLineFlg, session.Caption);
                if (Form.ShowDialog(poderosaForm.AsForm()) != DialogResult.OK) return CommandResult.Ignored;
            }

            // クリップボードデータ送信
            StringReader reader = new StringReader(data);
            TerminalTransmission output = session.TerminalTransmission;
            output.SendTextStream(reader, data[data.Length - 1] == '\n');
            return CommandResult.Succeeded;
        }

        /// <summary>
        /// <ja>CanExecute</ja>
        /// </summary>
        public bool CanExecute(ICommandTarget target) {
            IPoderosaView view;
            ITerminalSession session;

            if (!GetViewAndSession(target, out view, out session)) return false;
            var clipboardData = Clipboard.GetDataObject();
            if (!clipboardData.GetDataPresent("Text")) return false;

            return true;
        }

        /// <summary>
        /// <ja>GetAdapter</ja>
        /// </summary>
        public IAdaptable GetAdapter(Type adapter) {
            return ExtendPastePlugin.Instance.PoderosaWorld.AdapterManager.GetAdapter(this, adapter);
        }

        /// <summary>
        /// <ja>GetViewAndSession</ja>
        /// </summary>
        private bool GetViewAndSession(ICommandTarget target, out IPoderosaView view, out ITerminalSession session) {
            view = (IPoderosaView)target.GetAdapter(typeof(IPoderosaView));
            if ((view != null) && (view.Document != null)) {
                session = (ITerminalSession)view.Document.OwnerSession.GetAdapter(typeof(ITerminalSession));
                if (!session.TerminalConnection.IsClosed) return true;
            } else {
                session = null;
            }
            return false;
        }
    }




    /// <summary>
    /// <ja>オプション本体定義クラス</ja>
    /// </summary>
    internal class ExtendPasteOptionsSupplier : IPreferenceSupplier {
        // メンバー変数
        private IPreferenceFolder _originalFolder;
        private ExtendPasteOptions _originalOptions;

        /// <summary>
        /// <ja>初期化</ja>
        /// </summary>
        public void InitializePreference(IPreferenceBuilder builder, IPreferenceFolder folder) {
            _originalFolder = folder;
            _originalOptions = new ExtendPasteOptions(_originalFolder);
            _originalOptions.DefineItems(builder);
        }

        /// <summary>
        /// <ja>QueryAdapter</ja>
        /// </summary>
        public object QueryAdapter(IPreferenceFolder folder, Type type) {
            Debug.Assert(_originalFolder.Id == folder.Id);
            if (type == typeof(IExtendPasteOptions)) {
                return _originalFolder == folder ? _originalOptions : new ExtendPasteOptions(folder).Import(_originalOptions);
            } else {
                return null;
            }
        }

        /// <summary>
        /// <ja>ValidateFolder</ja>
        /// </summary>
        public void ValidateFolder(IPreferenceFolder folder, IPreferenceValidationResult output) {
        }

        /// <summary>
        /// <ja>識別子</ja>
        /// </summary>
        public string PreferenceID {
            get { return ExtendPastePlugin.PLUGIN_ID; }
        }

        /// <summary>
        /// <ja>オプション値参照</ja>
        /// </summary>
        public ExtendPasteOptions OriginalOptions {
            get { return _originalOptions; }
        }
    }




    /// <summary>
    /// <ja>オプション項目定義クラス</ja>
    /// </summary>
    internal class ExtendPasteOptions : SnapshotAwarePreferenceBase, IExtendPasteOptions {
        // メンバー変数
        private const string DEFAULT_HIGHLIGHT_KEYWORD = "rm|kill|killall|shutdown|reboot|halt";
        private EnumPreferenceItem<UseAction> _useAction;
        private IStringPreferenceItem _highlightKeyword;
        private IBoolPreferenceItem _showConfirmCheck;
        private IBoolPreferenceItem _afterSpecifiedTimePaste;
        private IIntPreferenceItem _pasteTime;
        private IBoolPreferenceItem _ChangeDialogSize;

        /// <summary>
        /// <ja>コンストラクタ</ja>
        /// </summary>
        public ExtendPasteOptions(IPreferenceFolder folder)
            : base(folder) {
        }

        /// <summary>
        /// <ja>オプション項目定義</ja>
        /// </summary>
        public override void DefineItems(IPreferenceBuilder builder) {
            _useAction = new EnumPreferenceItem<UseAction>(builder.DefineStringValue(_folder, "useAction", "WhenNewLine", null), UseAction.NewLine);
            _highlightKeyword = builder.DefineStringValue(_folder, "highlightKeyword", DEFAULT_HIGHLIGHT_KEYWORD, null);
            _showConfirmCheck = builder.DefineBoolValue(_folder, "showConfirmed", false, null);
            _afterSpecifiedTimePaste = builder.DefineBoolValue(_folder, "afterSpecifiedTimePaste", false, null);
            _pasteTime = builder.DefineIntValue(_folder, "pasteTime", 0, null);
            _ChangeDialogSize = builder.DefineBoolValue(_folder, "allowChangeDialogSize", false, null);
        }

        /// <summary>
        /// <ja>設定ファイルからインポート</ja>
        /// </summary>
        public ExtendPasteOptions Import(ExtendPasteOptions src) {
            _useAction = ConvertItem(src._useAction);
            _highlightKeyword = ConvertItem(src._highlightKeyword);
            _showConfirmCheck = ConvertItem(src._showConfirmCheck);
            _afterSpecifiedTimePaste = ConvertItem(src._afterSpecifiedTimePaste);
            _pasteTime = ConvertItem(src._pasteTime);
            _ChangeDialogSize = ConvertItem(src._ChangeDialogSize);
            return this;
        }

        /// <summary>
        /// <ja>使用アクション</ja>
        /// </summary>
        public UseAction UseAction {
            get { return _useAction.Value; }
            set { _useAction.Value = value; }
        }

        /// <summary>
        /// <ja>強調キーワード</ja>
        /// </summary>
        public string HighlightKeyword {
            get { return _highlightKeyword.Value; }
            set { _highlightKeyword.Value = value; }
        }

        /// <summary>
        /// <ja>確認チェックボックスを表示</ja>
        /// </summary>
        public bool ShowConfirmCheck {
            get { return _showConfirmCheck.Value; }
            set { _showConfirmCheck.Value = value; }
        }

        /// <summary>
        /// <ja>指定秒数経過後にペーストを実行</ja>
        /// </summary>
        public bool AfterSpecifiedTimePaste {
            get { return _afterSpecifiedTimePaste.Value; }
            set { _afterSpecifiedTimePaste.Value = value; }
        }

        /// <summary>
        /// <ja>ペーストを実行する秒数</ja>
        /// </summary>
        public int PasteTime {
            get { return _pasteTime.Value; }
            set { _pasteTime.Value = value; }
        }

        /// <summary>
        /// <ja>ダイアログサイズの変更を許可</ja>
        /// </summary>
        public bool ChangeDialogSize {
            get { return _ChangeDialogSize.Value; }
            set { _ChangeDialogSize.Value = value; }
        }
    }




    /// <summary>
    /// <ja>オプション項目定義インターフェイス</ja>
    /// </summary>
    internal interface IExtendPasteOptions {
        /// <summary>
        /// <ja>使用アクション</ja>
        /// </summary>
        UseAction UseAction {
            get;
            set;
        }

        /// <summary>
        /// <ja>強調キーワード</ja>
        /// </summary>
        string HighlightKeyword {
            get;
            set;
        }

        /// <summary>
        /// <ja>確認チェックボックスを表示</ja>
        /// </summary>
        bool ShowConfirmCheck {
            get;
            set;
        }

        /// <summary>
        /// <ja>指定秒数経過後にペーストを実行</ja>
        /// </summary>
        bool AfterSpecifiedTimePaste {
            get;
            set;
        }

        /// <summary>
        /// <ja>ペーストを実行する秒数</ja>
        /// </summary>
        int PasteTime {
            get;
            set;
        }

        /// <summary>
        /// <ja>ダイアログサイズの変更を許可</ja>
        /// </summary>
        bool ChangeDialogSize {
            get;
            set;
        }
    }




    /// <summary>
    /// <ja>使用アクション定義リスト</ja>
    /// </summary>
    internal enum UseAction {
        /// <summary>
        /// <ja>改行を含む場合に使用</ja>
        /// </summary>
        [EnumValue(Description = "Enum.UseAction.NewLine")]
        NewLine,
        /// <summary>
        /// <ja>常に使用</ja>
        /// </summary>
        [EnumValue(Description = "Enum.UseAction.Always")]
        Always,
        /// <summary>
        /// <ja>使用しない</ja>
        /// </summary>
        [EnumValue(Description = "Enum.UseAction.NotUse")]
        NotUse
    }
}