#define MSSQL
#define SSAS
#define Sybase_ASE
#define Sybase_Anywhere
#define Sybase_UltraLite
#define Sybase_Advantage
#define Sap_Hana
#define Oracle
#define Firebird
#define PostgreSQL
#define MySQL
#define DB2
#define Ingres
#define SQL_Azure
#define Sqlite
#define Informix
#define Interbase
#define Vertica
#define MongoDb
#define Redis
#define Cassandra
#define Neo4j

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Threading;
using Microsoft.FSharp.Collections;
using Microsoft.Win32;
using SqlDynamite.Common;

namespace SqlDynamite
{
    /// <summary>
    /// Interaction logic for ConfigWindow.xaml
    /// </summary>
    public partial class ConfigWindow
    {
        public static SyntaxProvider SyntaxProvider => syntaxProvider;

        private readonly IEnumerable<string> odbcDrivers;
        private readonly IEnumerable<string> oleDbDrivers;
        private static readonly SyntaxProvider syntaxProvider = new SyntaxProvider();

        private static readonly string[] fileDbs = {"Firebird","SAP IQ","SAP Anywhere",
                                                    "SAP UltraLite","SAP Advantage",
                                                    "SQLite","Interbase"};

        public ConfigWindow()
        {
            InitializeComponent();
            odbcDrivers = DataUtil.GetOdbcDrivers();
            oleDbDrivers = DataUtil.GetOleDbDrivers();
#if MSSQL
            ServerType.Items.Add("MSSQL");
#endif
#if SSAS
            ServerType.Items.Add("SQL Analysis Services");
#endif
#if Oracle
            ServerType.Items.Add("Oracle");
#endif
#if PostgreSQL
            ServerType.Items.Add("PostgreSQL");
#endif
#if DB2
            ServerType.Items.Add("DB2");
#endif
#if Sybase_ASE
            ServerType.Items.Add("SAP ASE");
#endif
#if Sybase_Anywhere
            ServerType.Items.Add("SAP Anywhere");
            ServerType.Items.Add("SAP IQ");
#endif
#if Sybase_UltraLite
            ServerType.Items.Add("SAP UltraLite");
#endif
#if Sybase_Advantage
            ServerType.Items.Add("SAP Advantage");
#endif
#if Sap_Hana
            ServerType.Items.Add("SAP HANA");
#endif
#if MySQL
            ServerType.Items.Add("MySQL");
#endif
#if Firebird
            ServerType.Items.Add("Firebird");
#endif
#if Ingres
            ServerType.Items.Add("Ingres");
#endif
#if SQL_Azure
            ServerType.Items.Add("SQL Azure");
#endif
#if Sqlite
            ServerType.Items.Add("SQLite");
#endif
#if Informix
            ServerType.Items.Add("Informix");
#endif
#if Interbase
            ServerType.Items.Add("Interbase");
#endif
#if Vertica
            ServerType.Items.Add("Vertica");
#endif
#if MongoDb
            ServerType.Items.Add("MongoDb");
#endif
#if Redis
            ServerType.Items.Add("Redis");
#endif
#if Cassandra
            ServerType.Items.Add("Cassandra");
#endif
#if Neo4j
            ServerType.Items.Add("Neo4j");
#endif
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            SspiCheck();
            ServerType_SelectionChanged(this, null);
            ODBC_Checked(this, null);
            Dispatcher.Invoke(DispatcherPriority.Loaded, (Action)delegate { });
        }

        private void ServerType_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (ServerType.SelectedIndex >= 0)
            {
                OdbcCheck();
                bool sspiAvailable = ServerType.SelectedItem.ToString().Contains("MSSQL") ||
                                 ServerType.SelectedItem.ToString().Contains("SQL Analysis Services") ||
                                 ServerType.SelectedItem.ToString().Contains("Oracle");
                SSPI.IsEnabled = sspiAvailable;
                Server.IsEnabled = !(ServerType.SelectedItem.ToString().Contains("SQLite") || 
                                     ServerType.SelectedItem.ToString().Contains("SAP UltraLite") || 
                                     ServerType.SelectedItem.ToString().Contains("SAP Advantage"));
                if (!sspiAvailable) SSPI.IsChecked = false;
                CombineConnectionString();
                button5.Content = Array.Exists(fileDbs, x => x == ServerType.SelectedItem.ToString()) ? "Select DB" : "DB List";
            }
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            string str = "";
            if (string.IsNullOrWhiteSpace(ConnectionName.Text)) str += "Connection name is empty" + Environment.NewLine;
            if (string.IsNullOrWhiteSpace(ServerType.Text)) str += "Database server type is empty" + Environment.NewLine;
            if (string.IsNullOrWhiteSpace(Server.Text) && ServerType.Text != "SQLite" && ServerType.Text != "SAP UltraLite" && ServerType.Text != "SAP Advantage")
                str += "Server name or IP address is empty";
            if (!string.IsNullOrWhiteSpace(str))
            {
                MessageBox.Show(this, str, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            else DialogResult = true;
        }

        private void SSPI_Checked(object sender, RoutedEventArgs e)
        {
            SspiCheck();
        }

        private void SspiCheck()
        {
            if (!SSPI.IsChecked.Value)
            {
                User.IsEnabled = true;
                Password.IsEnabled = true;
            }
            else
            {
                User.IsEnabled = false;
                Password.IsEnabled = false;
            }
            CombineConnectionString();
        }

        private void SSPI_Unchecked(object sender, RoutedEventArgs e)
        {
            SspiCheck();
        }

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            string str = "";
            if (string.IsNullOrWhiteSpace(ServerType.Text)) str += "Database server type is empty" + Environment.NewLine;
            if (string.IsNullOrWhiteSpace(Server.Text) && ServerType.Text != "SQLite" && ServerType.Text != "SAP UltraLite" && ServerType.Text != "SAP Advantage")
                str += "Server name or IP address is empty";
            if (!string.IsNullOrWhiteSpace(str))
            {
                MessageBox.Show(this, str, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }
            if (ServerType.SelectedIndex >= 0)
            {
                try
                {
                    MetadataFinder mf = CreateMetadataFinder(ServerType.SelectedItem.ToString(), false);
                    var cnType = GetConnectionType();
                    string cstring = mf.MakeConnectionString(((ComboBoxItem) Driver.SelectedItem)?.Content.ToString() ?? Driver.Text,
                        Server.Text, Database.Text, User.Text, Password.Password, SSPI.IsChecked.Value, cnType);
                    if (!string.IsNullOrWhiteSpace(cstring))
                    {
                        mf.EstablishConnection(cstring, cnType, Database.Text);
                        MessageBox.Show("Connection successfull", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
                    }
                    else
                    {
                        MessageBox.Show("Connection string generation is not implemented for " + ServerType.Text + " " + cnType + " drivers", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
                    }
                }
                catch (Exception exception)
                {
                    MessageBox.Show(exception.GetExceptionMessages(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
        
        private void ODBC_Checked(object sender, RoutedEventArgs e)
        {
            OdbcCheck();
        }

        private void OdbcCheck()
        {
            Driver.IsEnabled = ODBC.IsChecked.Value || OLEDB.IsChecked.Value;
            if (ODBC.IsChecked.Value)
            {
                OLEDB.IsChecked = false;
                button5.Visibility = Visibility.Hidden;
                Driver.Items.Clear();
                foreach (string driver in odbcDrivers)
                {
                    ComboBoxItem item = new ComboBoxItem { Content = driver };
                    Driver.Items.Add(item);
                }
            }
            else
            {
                Driver.SelectedIndex = -1;
                button5.Visibility = Visibility.Visible;
                if (!OLEDB.IsChecked.Value)
                {
                    Driver.Text = "";
                }
            }
            if (ServerType.SelectedIndex >= 0)
            {
                label4.Content = ServerType.SelectedItem.ToString().Contains("Informix") ? "Instance/DB" : "Database";
                CombineConnectionString();
            }
        }

        private void OleDbCheck()
        {
            Driver.IsEnabled = ODBC.IsChecked.Value || OLEDB.IsChecked.Value;
            if (OLEDB.IsChecked.Value)
            {
                ODBC.IsChecked = false;
                button5.Visibility = Visibility.Hidden;
                Driver.Items.Clear();
                foreach (string driver in oleDbDrivers)
                {
                    ComboBoxItem item = new ComboBoxItem { Content = driver };
                    Driver.Items.Add(item);
                }
            }
            else
            {
                Driver.SelectedIndex = -1;
                button5.Visibility = Visibility.Visible;
                if (!ODBC.IsChecked.Value)
                {
                    Driver.Text = "";
                }
            }
            if (ServerType.SelectedIndex >= 0)
            {
                CombineConnectionString();
            }
        }

        private void ODBC_Unchecked(object sender, RoutedEventArgs e)
        {
            OdbcCheck();
        }

        public static MetadataFinder CreateMetadataFinder(string serverType, bool loadWords)
        {
            MetadataFinder finder;
            Color functionColor = syntaxProvider.FunctionColor;
            switch (serverType)
            {
#if Sybase_ASE
                case "SAP ASE":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.sybase_ase_functions, functionColor);
                    }
                    finder = new SybaseMetadataFinder();
                    break;
#endif
#if Sybase_Anywhere
                case "SAP Anywhere":
                case "SAP IQ":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.sybase_anywhere_functions, functionColor);
                    }
                    finder = new SybaseAnywhereMetadataFinder();
                    break;
#endif
#if Sybase_Advantage
                case "SAP Advantage":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                    }
                    finder = new SybaseAdvantageMetadataFinder();
                    break;
#endif
#if Sap_Hana
                case "SAP HANA":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                    }
                    finder = new SapHanaMetadataFinder();
                    break;
#endif
#if Oracle
                case "Oracle":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.oracle_functions, functionColor);
                    }
                    finder = new OracleMetadataFinder();
                    break;
#endif
#if Firebird
                case "Firebird":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.firebird_functions, functionColor);
                    }
                    finder = new FirebirdMetadataFinder();
                    break;
#endif
#if Ingres
                case "Ingres":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.ingres_functions, functionColor);
                    }
                    finder = new IngresMetadataFinder();
                    break;
#endif
#if SQL_Azure
                case "SQL Azure":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.mssql_functions, functionColor);
                    }
                    finder = new SqlAzureMetadataFinder();
                    break;
#endif
#if PostgreSQL
                case "PostgreSQL":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.postgresql_functions, functionColor);
                    }
                    finder = new PostgreSqlMetadataFinder();
                    break;
#endif
#if MySQL
                case "MySQL":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.mysql_functions, functionColor);
                    }
                    finder = new MySqlMetadataFinder();
                    break;
#endif
#if MSSQL
                case "MSSQL":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.mssql_functions, functionColor);
                    }
                    finder = new MsSqlMetadataFinder();
                    break;
#endif
#if MSSQL
                case "SQL Analysis Services":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.dax_functions, functionColor);
                        syntaxProvider.LoadWords(Properties.Resources.dmx_functions, functionColor);
                        syntaxProvider.LoadWords(Properties.Resources.mdx_functions, functionColor);
                    }
                    finder = new SsasMetadataFinder();
                    break;
#endif
#if DB2
                case "DB2":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.db2_functions, functionColor);
                    }
                    finder = new Db2MetadataFinder();
                    break;
#endif
#if Sqlite
                case "SQLite":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadWords(Properties.Resources.sqlite_functions, functionColor);
                    }

                    finder = new SqliteMetadataFinder();
                    break;
#endif
#if Informix
                case "Informix":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                    }

                    finder = new InformixMetadataFinder();
                    break;
#endif
#if Interbase
                case "Interbase":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                    }
                    finder = new InterbaseMetadataFinder();
                    break;
#endif
#if Sybase_UltraLite
                case "SAP UltraLite":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                    }
                    finder = new UltraliteMetadataFinder();
                    break;
#endif
#if Vertica
                case "Vertica":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                    }
                    finder = new VerticaMetadataFinder();
                    break;
#endif
#if MongoDb
                case "MongoDb":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadLanguage("JavaScript");
                    }
                    finder = new MongoDbMetadataFinder();
                    break;
#endif
#if Redis
                case "Redis":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                    }
                    finder = new RedisMetadataFinder();
                    break;
#endif
#if Cassandra
                case "Cassandra":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadLanguage("CQL");
                        syntaxProvider.LoadLanguage("JavaScript");
                    }
                    finder = new CassandraMetadataFinder();
                    break;
#endif
#if Neo4j
                case "Neo4j":
                    if (loadWords)
                    {
                        syntaxProvider.UnloadWords();
                        syntaxProvider.LoadLanguage("Cypher");
                    }
                    finder = new Neo4jMetadataFinder();
                    break;
#endif
                default:
                    finder = null;
                    break;
            }
            return finder;
        }

        public ConnectionType GetConnectionType()
        {
            if (ODBC.IsChecked.Value)
                return ConnectionType.ODBC;
            if (OLEDB.IsChecked.Value)
                return ConnectionType.OLEDB;
            return ConnectionType.NET;
        }
        
        private void button5_Click(object sender, RoutedEventArgs e)
        {
            string str = "";
            if (string.IsNullOrWhiteSpace(ServerType.Text)) str += "Database server type is empty" + Environment.NewLine;
            if (string.IsNullOrWhiteSpace(Server.Text) && ServerType.Text != "Oracle" && (ServerType.Text == null || !Array.Exists(fileDbs, x => x == ServerType.SelectedItem?.ToString()))) 
                str += "Server name or IP address is empty";
            if (!string.IsNullOrWhiteSpace(str))
            {
                MessageBox.Show(this, str, "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }
            if (ServerType.SelectedIndex >= 0 && ServerType.SelectedIndex != 2)
            {
                try
                {
                    MetadataFinder mf = CreateMetadataFinder(ServerType.SelectedItem.ToString(), false);
                    var cnType = GetConnectionType();
                    string cstring = mf.MakeConnectionString(((ComboBoxItem) Driver.SelectedItem)?.Content.ToString() ?? Driver.Text,
                        Server.Text, Database.Text, User.Text, Password.Password, SSPI.IsChecked.Value, cnType);
                    if (!string.IsNullOrWhiteSpace(cstring) && !Array.Exists(fileDbs, x => x == ServerType.SelectedItem.ToString()))
                    {
                        mf.EstablishConnection(cstring, cnType, Database.Text);
                    }
                    else if (!Array.Exists(fileDbs, x => x == ServerType.SelectedItem.ToString()))
                    {
                        MessageBox.Show("Connection string generation is not implemented for " + ServerType.Text + " " + cnType + " drivers");
                    }
                    if (Array.Exists(fileDbs, x => x == ServerType.SelectedItem.ToString()))
                    {
                        OpenFileDialog ofd = new OpenFileDialog();
                        if (ServerType.SelectedItem.ToString() == "SQLite")
                        {
                            ofd.Filter = "SQLite databases (*.sqlite)|*.sqlite|All files (*.*)|*.*";
                        }
                        else if (ServerType.SelectedItem.ToString() == "Interbase")
                        {
                            ofd.Filter = "Interbase databases (*.gdb, *.ib)|*.gdb;*.ib|All files (*.*)|*.*";
                        }
                        else if (ServerType.SelectedItem.ToString() == "Firebird")
                        {
                            ofd.Filter = "Firebird databases (*.fdb)|*.fdb|All files (*.*)|*.*";
                        }
                        else if (ServerType.SelectedItem.ToString() == "SAP UltraLite")
                        {
                            ofd.Filter = "UltraLite databases (*.udb)|*.udb|All files (*.*)|*.*";
                        }
                        else if (ServerType.SelectedItem.ToString() == "SAP Anywhere" || ServerType.SelectedItem.ToString() == "SAP IQ")
                        {
                            ofd.Filter = "SQL Anywhere/SAP IQ databases (*.db)|*.db|All files (*.*)|*.*";
                        }
                        else if (ServerType.SelectedItem.ToString() == "SAP Advantage")
                        {
                            ofd.Filter = "SAP Advantage data dictionaries (*.add)|*.add|All files (*.*)|*.*";
                        }
                        
                        bool? dialogResult = ofd.ShowDialog(this);
                        if (dialogResult == true)
                        {
                            Database.Text = ofd.FileName;
                        }
                    }
                    else
                    { 
                        FSharpList<string> list = mf.GetDatabaseList("Databases");
                        Database.Items.Clear();
                        foreach (string s in list)
                        {
                            Database.Items.Add(s);
                        }
                    }
                }
                catch (Exception exception)
                {
                    MessageBox.Show(exception.GetExceptionMessages(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            else
            {
                string[] text =
                    new ConsoleWorker().StartConsoleProgram("tnsping",
                                                            string.IsNullOrWhiteSpace(Server.Text)
                                                                ? "localhost"
                                                                : Server.Text).Split('\n');
                foreach (string s in text)
                {
                    if (s.Contains("sqlnet.ora"))
                    {
                        try
                        {
                            string tnsFile = s.Replace("sqlnet.ora", "tnsnames.ora");
                            var dbs = OracleMetadataFinder.GetDatabases(tnsFile);
                            Database.Items.Clear();
                            foreach (string db in dbs)
                            {
                                Database.Items.Add(db);
                            }
                        }
                        catch (Exception exception)
                        {
                            MessageBox.Show(exception.GetExceptionMessages(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
							return;
                        }
                    }
                }
            }
        }

        private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
            OleDbCheck();
        }

        private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            OleDbCheck();
        }

        private void CombineConnectionString()
        {
            MetadataFinder mf = CreateMetadataFinder(ServerType.SelectedItem?.ToString() ?? ServerType.Text, false);
            if (mf == null) return;
            var cnType = GetConnectionType();
            string cstring = mf.MakeConnectionString(((ComboBoxItem) Driver.SelectedItem)?.Content.ToString() ?? Driver.Text, Server.Text, 
                Database.SelectedItem?.ToString() ?? Database.Text, User.Text, Password.Password, SSPI.IsChecked.Value, cnType);

            if (string.IsNullOrWhiteSpace(cstring) && !cnType.Equals(ConnectionType.NET))
            {
                cstring = "Connection string generation is not implemented for " + ServerType.Text + " " + cnType + " drivers";
            }

            cstring = HidePswd(cstring);

            FlowDocument document = new FlowDocument();
            Paragraph paragraph = new Paragraph();
            paragraph.Inlines.Add(cstring);
            document.Blocks.Add(paragraph);
            ConnectionStringEntry.Document = document;
        }

        private static string HidePswd(string cstring)
        {
            if (string.IsNullOrWhiteSpace(cstring))
            {
                return string.Empty;
            }
            int pwdIndex = cstring.IndexOf("Password=", StringComparison.OrdinalIgnoreCase);
            if (pwdIndex == -1)
            {
                pwdIndex = cstring.IndexOf("Pwd=", StringComparison.OrdinalIgnoreCase);
                if (pwdIndex != -1)
                {
                    pwdIndex += 4;
                }
            }
            else
            {
                pwdIndex += 9;
            }
            if (pwdIndex != -1)
            {
                int scIndex = cstring.IndexOf(';', pwdIndex);
                string pswd = scIndex != -1 ? cstring.Substring(pwdIndex, scIndex - pwdIndex) : cstring.Substring(pwdIndex);
                if (!string.IsNullOrWhiteSpace(pswd))
                {
                    cstring = cstring.Replace(pswd, new string('*', pswd.Length));
                }
            }
            return cstring;
        }

        private void Driver_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            CombineConnectionString();
        }

        private void Server_TextChanged(object sender, TextChangedEventArgs e)
        {
            CombineConnectionString();
        }

        private void User_TextChanged(object sender, TextChangedEventArgs e)
        {
            CombineConnectionString();
        }

        private void Database_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            CombineConnectionString();
        }

        private void Database_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
            CombineConnectionString();
        }
    }
}