using MemoQ.Addins.Common.DataStructures;
using MemoQ.Addins.Common.Framework;
using MemoQ.TMInterfaces;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace TestClient
{
    public partial class MainForm : Form
    {
        private bool initializing;

        private List<string> languages;

        private List<PluginInfo> plugins;

        private IEngine tmEngine;

        private class TMPluginHitItem
        {
            public TMPluginHit PluginHit { get; set; }

            public override string ToString()
            {
                return PluginHit.TargetSegment.PlainText;
            }
        }

        public MainForm()
        {
            InitializeComponent();

            using (Stream stream = typeof(MainForm).Assembly.GetManifestResourceStream("TestClient.MemoQ.ico"))
            {
                this.Icon = new Icon(stream);
            }

            initializing = false;

            // initialize the languages known by the sample application
            languages = new List<string>() { "eng", "fin", "fre", "ger", "hun", "ita", "por", "spa" };

            // initialize the list of the plugins
            plugins = new List<PluginInfo>();

            // loading modules
            loadPlugins();

            if (plugins.Count > 0)
            {
                // set the environment for each plugin
                DummyEnvironment environment = new DummyEnvironment(this);
                foreach (PluginInfo pluginInfo in plugins)
                {
                    pluginInfo.Director.Environment = environment;
                    lbPlugins.Items.Add(pluginInfo);
                }

                lbPlugins.SelectedIndex = 0;

                populateLanguageSelectors();
            }
            else
            {
                gbPluginDetails.Enabled = false;
                tabControl.Enabled = false;
            }
        }

        /// <summary>
        /// Populates the language selector comboboxes.
        /// </summary>
        private void populateLanguageSelectors()
        {
            initializing = true;

            cbSourceLanguage.Items.Add("Select source language");
            cbTargetLanguage.Items.Add("Select target language");
            foreach (string language in languages)
            {
                cbSourceLanguage.Items.Add(language);
                cbTargetLanguage.Items.Add(language);
            }
            cbSourceLanguage.SelectedIndex = 0;
            cbTargetLanguage.SelectedIndex = 0;

            initializing = false;
        }

        private bool checkSelectedLanguages()
        {
            // not selected
            if (cbSourceLanguage.SelectedIndex == -1 || cbTargetLanguage.SelectedIndex == -1)
            {
                (tpLookup as Control).Enabled = (tpConcordance as Control).Enabled = false;
                return false;
            }

            // "Select language" is selected
            if (cbSourceLanguage.SelectedIndex == 0 || cbTargetLanguage.SelectedIndex == 0)
            {
                (tpLookup as Control).Enabled = (tpConcordance as Control).Enabled = false;

                return false;
            }

            // the same language is selected on both sides
            if (cbSourceLanguage.SelectedIndex.Equals(cbTargetLanguage.SelectedIndex))
            {
                (tpLookup as Control).Enabled = (tpConcordance as Control).Enabled = false;
                lblMessage.Text = "This source and the target languages are the same.";
                return false;
            }

            PluginDirectorBase director = (lbPlugins.SelectedItem as PluginInfo).Director;

            // the selected plugin does not support the selected language pair
            if (!director.IsLanguagePairSupported(cbSourceLanguage.SelectedItem.ToString(), cbTargetLanguage.SelectedItem.ToString()))
            {
                (tpLookup as Control).Enabled = (tpConcordance as Control).Enabled = false;
                lblMessage.Text = "This language pair is not supported by the selected plugin.";
                return false;
            }
            else
            {
                (tpLookup as Control).Enabled = director.SupportsSegmentLookup;
                (tpConcordance as Control).Enabled = director.SupportsConcordance;
                lblMessage.Text = string.Empty;
                return true;
            }
        }

        /// <summary>
        /// Updates the controls' states based on the selected plugin settings.
        /// </summary>
        public void UpdateControls()
        {
            initializing = true;

            PluginDirectorBase director = (lbPlugins.SelectedItem as PluginInfo).Director;
            lblNameValue.Text = director.FriendlyName;
            lblCopyrightValue.Text = director.CopyrightText;
            lblSegmentLookupValue.Text = director.SupportsSegmentLookup ? "True" : "False";
            lblConcordanceValue.Text = director.SupportsConcordance ? "True" : "False";
            pictureBox.Image = director.DisplayIcon;
            lblConfiguredValue.Text = director.PluginConfigured ? "Configured" : "Not configured";
            cbEnabled.Checked = director.PluginEnabled;
            tabControl.Enabled = director.PluginEnabled && director.PluginConfigured;
            (tpLookup as Control).Enabled = director.SupportsSegmentLookup && director.PluginEnabled && director.PluginConfigured && checkSelectedLanguages();
            (tpConcordance as Control).Enabled = director.SupportsConcordance && director.PluginEnabled && director.PluginConfigured && checkSelectedLanguages();
            gbTMHitDetails.Enabled = director.PluginEnabled && director.PluginConfigured;

            initializing = false;
        }

        /// <summary>
        /// Loads the available TM plugins from the plugins directory.
        /// </summary>
        private void loadPlugins()
        {
            string moduleDir = Directory.GetCurrentDirectory();

            DirectoryInfo di = new DirectoryInfo(moduleDir);
            if (!di.Exists)
                return;

            FileInfo[] files = di.GetFiles("*.dll");
            foreach (FileInfo fileInfo in files)
            {
                string assemblyPath = fileInfo.FullName;
                Assembly assembly = Assembly.LoadFrom(assemblyPath);

                bool isTMAddin;
                try
                {
                    isTMAddin = hasInterface(assembly, "MemoQ.TMInterfaces.IPluginDirector");
                }
                catch (Exception)
                {
                    // Addin interface may have changed: then nothing to do, skip this assembly
                    // (Won't load DLL that throws)
                    continue;
                }

                // only load TM plugins
                if (isTMAddin)
                {
                    ModuleAttribute moduleAttr;

                    foreach (Attribute attr in assembly.GetCustomAttributes(true))
                    {
                        moduleAttr = attr as ModuleAttribute;
                        if (moduleAttr != null)
                        {
                            try
                            {
                                PluginDirectorBase director = assembly.CreateInstance(moduleAttr.ClassName) as PluginDirectorBase;
                                director.Initialize(null);
                                plugins.Add(new PluginInfo(director));
                            }
                            catch (Exception e)
                            {
                                MessageBox.Show(string.Format("Module '{0}' failed to load: {1}", moduleAttr.ModuleName, e.Message));
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Checks whether any type in an assembly implements the given interface.
        /// </summary>
        /// <param name="assembly">The assembly to search for types in.</param>
        /// <param name="interfaceName">The name of the interface to find.</param>
        /// <returns>True if any types in the given assembly implements the interface, else false.</returns>
        private static bool hasInterface(Assembly assembly, string interfaceName)
        {
            Type[] types = assembly.GetExportedTypes();
            foreach (Type t in types)
            {
                Type implementedInterface = t.GetInterface(interfaceName);
                if (implementedInterface != null)
                {
                    return true;
                }
            }
            return false;
        }

        private void lbPlugins_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateControls();

            cbLanguages_SelectedIndexChanged(null, null);
        }

        private void cbEnabled_CheckedChanged(object sender, EventArgs e)
        {
            (lbPlugins.SelectedItem as PluginInfo).Director.PluginEnabled = cbEnabled.Checked;
        }

        private void lnkConfigure_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            (lbPlugins.SelectedItem as PluginInfo).Director.ShowOptionsForm(this);
        }

        private void cbLanguages_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (initializing) return;

            lbTMPluginHits.Items.Clear();
            dgvConcordanceItemDetails.Rows.Clear();
            dgvConcordanceItemDetails.PerformLayout();
            dgvConcordanceItems.Rows.Clear();
            dgvConcordanceItems.PerformLayout();
            updateTMHitDetails();

            if (!checkSelectedLanguages()) return;

            // dispose the existing TM engine if exists
            if (tmEngine != null)
            {
                tmEngine.Dispose();
                tmEngine = null;
            }

            // intialize the new TM engine based on the selected languages
            if (cbSearchInTarget.Checked)
                tmEngine = (lbPlugins.SelectedItem as PluginInfo).Director.CreateEngine(cbTargetLanguage.SelectedItem.ToString(), cbSourceLanguage.SelectedItem.ToString());
            else
                tmEngine = (lbPlugins.SelectedItem as PluginInfo).Director.CreateEngine(cbSourceLanguage.SelectedItem.ToString(), cbTargetLanguage.SelectedItem.ToString());
        }

        private void btnLookup_Click(object sender, EventArgs e)
        {
            if (tbSource.TextLength == 0) return;

            this.Enabled = false;
            this.Cursor = Cursors.WaitCursor;

            lbTMPluginHits.Items.Clear();

            using (ISession session = tmEngine.CreateSession())
            {
                SegmentBuilder srcSegBuilder = new SegmentBuilder();
                srcSegBuilder.AppendString(string.Join(" ", tbSource.Lines));

                TMPluginHit[] results = null;
                try
                {
                    results = session.Lookup(srcSegBuilder.ToSegment(), null, null, (int)numMatchThreshold.Value);
                }
                catch (Exception ex)
                {
                    if (ex is TMPluginException)
                        MessageBox.Show(this, string.Format("There was an error during lookup.\n\n{0}", (ex as TMPluginException).Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    else
                        MessageBox.Show(this, "The plugin has to throw TMException.", "Incorrect exception thrown", MessageBoxButtons.OK, MessageBoxIcon.Error);

                    this.Cursor = Cursors.Arrow;
                    this.Enabled = true;
                    return;
                }

                foreach (TMPluginHit result in results)
                {
                    lbTMPluginHits.Items.Add(new TMPluginHitItem { PluginHit = result });
                }

                if (lbTMPluginHits.Items.Count > 0)
                {
                    lbTMPluginHits.SelectedIndex = 0;
                }
                else
                {
                    updateTMHitDetails();
                }
            }

            this.Cursor = Cursors.Arrow;
            this.Enabled = true;
        }

        private List<string> fillReverseFilter()
        {
            int i = 0;
            bool insideQuotation = false;
            StringBuilder filterWord = new StringBuilder();

            List<string> targetFilterStrings = new List<string>();

            while (i < tbFilter.Text.Length)
            {
                char c = tbFilter.Text[i++];

                if (c == '"')
                {
                    insideQuotation = !insideQuotation;
                    c = ' ';    // forced separator
                }
                else if (insideQuotation || !(char.IsSeparator(c)))
                {
                    filterWord.Append(c);
                }

                if ((!insideQuotation && char.IsSeparator(c)) || (i == tbFilter.Text.Length))
                {
                    if (filterWord.Length > 0)
                    {
                        targetFilterStrings.Add(filterWord.ToString());
                        filterWord.Remove(0, filterWord.Length);
                    }
                }
            }

            return targetFilterStrings;
        }

        private void btnConcordance_Click(object sender, EventArgs e)
        {
            if (tbConcordanceText.TextLength == 0) return;

            this.Enabled = false;
            this.Cursor = Cursors.WaitCursor;

            lbTMPluginHits.Items.Clear();

            using (ISession session = tmEngine.CreateSession())
            {
                TMPluginConcordanceResult result = null;
                try
                {
                    TMPluginConcordanceSettings settings = new TMPluginConcordanceSettings(
                        new TMPluginConcordanceSettings.SortInfo(
                            TMPluginConcordanceSettings.PrimarySortColumn.Middle,
                            TMPluginConcordanceSettings.SecondaryMetaSortColumn.ModifTime,
                            TMPluginConcordanceSettings.SecondaryPrefixSuffixSortColumn.First,
                            true),
                        cbCaseSensitive.Checked,
                        cbNumericEquivalence.Checked,
                        (int)numLimit.Value,
                        fillReverseFilter(),
                        cbSearchInTarget.Checked);

                    result = session.Concordance(cbQuotes.Checked ?
                        new string[] { tbConcordanceText.Text.Replace("\"", "") } :
                        tbConcordanceText.Text.Replace("\"", "").Split(' '),
                        settings);
                }
                catch (Exception ex)
                {
                    if (ex is TMPluginException)
                        MessageBox.Show(this, string.Format("There was an error during concordance.\n\n{0}", (ex as TMPluginException).Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    else
                        MessageBox.Show(this, "The plugin has to throw TMException.", "Incorrect exception thrown", MessageBoxButtons.OK, MessageBoxIcon.Error);

                    this.Cursor = Cursors.Arrow;
                    this.Enabled = true;
                    return;
                }

                dgvConcordanceItems.Rows.Clear();
                foreach (var item in result.Results)
                {
                    foreach (var range in item.ConcordanceTextRanges)
                    {
                        dgvConcordanceItems.Rows.Add(
                            item.Entry.SourceSegment.ToString().Substring(0, range.Start),
                            item.Entry.SourceSegment.ToString().Substring(range.Start, range.Length),
                            item.Entry.SourceSegment.ToString().Substring(range.Start + range.Length),
                            item.Entry.SourceSegment,
                            item.Entry.TargetSegment,
                            range.Start,
                            range.Length,
                            item.Entry.UserName,
                            item.Entry.Modified,
                            item.Entry.Subject,
                            item.Entry.Domain,
                            item.Entry.ClientID,
                            item.Entry.ProjectID,
                            item.Entry.CreationDate,
                            item.Entry.Document);
                    }
                }
                dgvConcordanceItems.PerformLayout();

                lblTotalHitsValue.Text = result.TotalResultCount.ToString();
            }

            this.Cursor = Cursors.Arrow;
            this.Enabled = true;
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (tmEngine != null)
            {
                tmEngine.Dispose();
                tmEngine = null;
            }
        }

        private void lbTMPluginHits_SelectedIndexChanged(object sender, EventArgs e)
        {
            updateTMHitDetails();
        }

        /// <summary>
        /// Updates the TM hit details according to the currently selected hit.
        /// </summary>
        private void updateTMHitDetails()
        {
            if (this.lbTMPluginHits.SelectedItem == null)
            {
                this.gbTMHitDetails.Enabled = false;
                this.dgvTMHitDetails.Rows.Clear();
            }
            else
            {
                this.gbTMHitDetails.Enabled = true;
                this.dgvTMHitDetails.Rows.Clear();

                TMPluginHit ph = (this.lbTMPluginHits.SelectedItem as TMPluginHitItem).PluginHit;

                this.dgvTMHitDetails.Rows.Add("Match rate", string.Format("{0}%", ph.MatchRate));
                this.dgvTMHitDetails.Rows.Add("Source segment", ph.SourceSegment != null ? ph.SourceSegment.PlainText : string.Empty);
                this.dgvTMHitDetails.Rows.Add("Target segment", ph.TargetSegment != null ? ph.TargetSegment.PlainText : string.Empty);
                this.dgvTMHitDetails.Rows.Add("User name", ph.UserName);
                this.dgvTMHitDetails.Rows.Add("Last modified", ph.LastModified);
                this.dgvTMHitDetails.Rows.Add("Subject", ph.GetEntry().Subject);
                this.dgvTMHitDetails.Rows.Add("Domain", ph.GetEntry().Domain);
                this.dgvTMHitDetails.Rows.Add("Client ID", ph.GetEntry().ClientID);
                this.dgvTMHitDetails.Rows.Add("Project ID", ph.GetEntry().ProjectID);
                this.dgvTMHitDetails.Rows.Add("Creation date", ph.GetEntry().CreationDate);
                this.dgvTMHitDetails.Rows.Add("Creating user", ph.GetEntry().CreatingUser);
                this.dgvTMHitDetails.Rows.Add("Document", ph.GetEntry().Document);
            }

            dgvTMHitDetails.PerformLayout();
        }

        private string trimString(string str)
        {
            return Regex.Replace(str.Trim(), @"\s+", " ");
        }

        private void cbQuotes_CheckedChanged(object sender, EventArgs e)
        {
            if (tbConcordanceText.Text.Length == 0)
                return;

            tbConcordanceText.Text = tbConcordanceText.Text.Replace("\"", "");
            tbConcordanceText.Text = trimString(tbConcordanceText.Text);

            if (tbConcordanceText.Text.Split(' ').Length > 1 && cbQuotes.Checked)
                tbConcordanceText.Text = "\"" + tbConcordanceText.Text + "\"";
        }

        private void cbWildcards_CheckedChanged(object sender, EventArgs e)
        {
            if (tbConcordanceText.Text.Length == 0)
                return;

            tbConcordanceText.Text = tbConcordanceText.Text.Replace("*", "");
            tbConcordanceText.Text = tbConcordanceText.Text.Replace("\"", "");
            tbConcordanceText.Text = trimString(tbConcordanceText.Text);

            if (cbWildcards.Checked)
            {
                string[] words = tbConcordanceText.Text.Split(' ');
                for (int i = 0; i < words.Length; ++i)
                {
                    if (words[i].Length >= 3)
                        words[i] = words[i] + "*";
                }
                tbConcordanceText.Text = string.Join(" ", words);
            }

            if (tbConcordanceText.Text.Split(' ').Length > 1 && cbQuotes.Checked)
                tbConcordanceText.Text = "\"" + tbConcordanceText.Text + "\"";
        }

        private void dgvConcordanceItems_SelectionChanged(object sender, EventArgs e)
        {
            dgvConcordanceItemDetails.Rows.Clear();

            if (dgvConcordanceItems.SelectedRows.Count == 0)
                return;

            for (int i = 3; i < dgvConcordanceItems.ColumnCount; ++i)
            {
                dgvConcordanceItemDetails.Rows.Add(
                    dgvConcordanceItems.Columns[i].HeaderText,
                    dgvConcordanceItems.SelectedRows[0].Cells[i].Value);
            }

            dgvConcordanceItemDetails.PerformLayout();
        }

        private void cbSearchInTarget_CheckedChanged(object sender, EventArgs e)
        {
            // dispose the existing TM engine if exists
            if (tmEngine != null)
            {
                tmEngine.Dispose();
                tmEngine = null;
            }

            if (cbSearchInTarget.Checked)
            {
                // intialize the new TM engine with reversed language pairs
                tmEngine = (lbPlugins.SelectedItem as PluginInfo).Director.CreateEngine(cbTargetLanguage.SelectedItem.ToString(), cbSourceLanguage.SelectedItem.ToString());
                lblFilter.Text = "Filter source";
            }
            else
            {
                // initialize the new TM engine with the selected language pairs
                tmEngine = (lbPlugins.SelectedItem as PluginInfo).Director.CreateEngine(cbSourceLanguage.SelectedItem.ToString(), cbTargetLanguage.SelectedItem.ToString());
                lblFilter.Text = "Filter target";
            }
        }

        private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (!checkSelectedLanguages())
                return;

            if (tmEngine != null)
            {
                tmEngine.Dispose();
                tmEngine = null;
            }

            // If the lookup tab was selected, initialize a new TM engine with the selected language pair
            if (tabControl.SelectedIndex == 0)
            {
                tmEngine = (lbPlugins.SelectedItem as PluginInfo).Director.CreateEngine(cbSourceLanguage.SelectedItem.ToString(), cbTargetLanguage.SelectedItem.ToString());
            }
            // If the concordance tab was selected
            else if (tabControl.SelectedIndex == 1)
            {
                if (cbSearchInTarget.Checked)   // Initialize a new TM engine with reversed language pairs
                    tmEngine = (lbPlugins.SelectedItem as PluginInfo).Director.CreateEngine(cbTargetLanguage.SelectedItem.ToString(), cbSourceLanguage.SelectedItem.ToString());
                else    // Initialize a new TM engine with the selected language pair
                    tmEngine = (lbPlugins.SelectedItem as PluginInfo).Director.CreateEngine(cbSourceLanguage.SelectedItem.ToString(), cbTargetLanguage.SelectedItem.ToString());
            }
        }
    }
}
