using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data.SqlClient;
using System.Data.Sql;

namespace Solar.Util.DUpdater
{
    public sealed class DatabaseUpdater
    {
        public enum DatabaseTypes
        {
            Login,
            World,
        }

        private EntitySetting _setting { get; set; }
        private DatabaseTypes _type { get; set; }

        public DatabaseUpdater(EntitySetting pSettings, DatabaseTypes pType)
        {
            this._setting = pSettings;
            this._type = pType;
        }

        public void Update()
        {
            Log.WriteLine(LogLevel.Info, "Looking for database updates...");
            int version = 0;
            int patch = 0;
            using (SqlConnection connection = new SqlConnection(ConnectionStringbuilder.CreateConnectionString(this._setting)))
            {
                connection.Open();
                // Check DB version
                try
                {
                    using (var reader = new SqlCommand("SELECT [Version] FROM [SolarVersion]", connection).ExecuteReader())
                    {
                        if (reader.Read())
                        {
                            version = reader.GetInt32(0);
                        }
                    }
                }
                catch { } // default is set to 0 :D

                Log.WriteLine(LogLevel.Debug, "Current Database version is {0}", version);

                // Now we have the function, lets see which files there are...

                if (Directory.Exists("SQL"))
                {
                    string type = _type == DatabaseTypes.Login ? "login" : "world";
                    foreach (string filename in Directory.GetFiles("SQL", type + "_*.sql"))
                    {
                        try
                        {
                            string[] pieces = filename.Split('_'); // login_XX_dat-a-lawl.sql
                            int p = int.Parse(pieces[1]);

                            if (p <= version) continue; // Already ran this one!

                            if (p < patch)
                            {
                                Log.WriteLine(LogLevel.Warn, "Patch ID out of order O.o. Using last patch ID instead: {0}", patch);
                            }
                            else
                            {
                                patch = p;
                            }
                            string message = pieces[2].Replace(".sql", "");

                            Log.WriteLine(LogLevel.Info, "Trying to update {0} database with patch {1}. Message:", type, patch);
                            Log.WriteLine(LogLevel.Info, message);
                            RunFile(filename, connection);
                        }
                        catch (Exception ex)
                        {
                            Log.WriteLine(LogLevel.Exception, "Could not parse file {0}: {1}", filename, ex.ToString());
                            Console.ReadLine();
                            Environment.Exit(400);
                        }
                    }

                    if (version < patch)
                    {
                        Log.WriteLine(LogLevel.Info, "Database updated!");
                        version = patch;
                        // Try to update table to new version
                        using (SqlCommand cmd = new SqlCommand("DELETE FROM [SolarVersion];", connection))
                            cmd.ExecuteNonQuery();
                        using (SqlCommand cmd = new SqlCommand("INSERT INTO [SolarVersion] VALUES (" + patch.ToString() + ");", connection))
                            cmd.ExecuteNonQuery();
                    }
                    else
                    {
                        Log.WriteLine(LogLevel.Info, "Database up-to-date!");
                    }
                }
                else
                {
                    Log.WriteLine(LogLevel.Error, "Couldn't find SQL dir. Cannot update db.");
                }

                connection.Close();
            }
        }

        private void RunFile(string pFilename, SqlConnection pConnection)
        {
            using (StreamReader sr = new StreamReader(pFilename))
            {
                string line;
                string query = "";
                bool commit = false;
                bool add = true;

                while (!sr.EndOfStream)
                {
                    line = sr.ReadLine();
                    if (line.StartsWith("/*") || line.StartsWith("#") || line.Trim() == "") continue;
                    if (!commit && line.ToUpper().StartsWith("BEGIN TRANSACTION"))
                    {
                        commit = true;
                        add = false;
                    }
                    if (commit && line.ToUpper().StartsWith("COMMIT"))
                    {
                        commit = false;
                        // Small override.
                        line = "GO";
                        add = false;
                    }
                    if (line.ToUpper().StartsWith("GO"))
                    {
                        if (!commit)
                        {
                            RunQuery(query, pConnection);
                            query = "";
                        }
                        add = false;

                    }
                    if (add)
                    {
                        query += line + Environment.NewLine;
                    }
                    add = true;
                }
                if (query != "")
                {
                    // Try to run last one too.
                    RunQuery(query, pConnection);
                }
            }
        }

        private void RunQuery(string pQuery, SqlConnection pConnection)
        {
            using (SqlCommand cmd = new SqlCommand(pQuery, pConnection))
            {
                int changed = cmd.ExecuteNonQuery();
                if (changed != -1)
                {
                    Log.WriteLine(LogLevel.Info, "DB query changed {0} rows.", changed);
                }
            }
        }
    }
}
