using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sdaleo.Internal;

namespace Sdaleo
{
    /// <summary>
    /// Specify what type of update/install to do.
    /// All_IF_EXISTS, will check for script existence and only run the ones that exist
    /// </summary>
    public enum InstallOrUpdateType
    {
        ALL_IF_EXISTS,
        TABLES,
        VIEWS,
        SPROCS,
    }

    /// <summary>
    /// Helper class to Install or Update a Database,
    /// Before using this class set via DBResources a DBResource that points to SQL Script Files
    /// to use in the Install/Update Process, or pass the DBResource in via one of the Constructors   
    /// ~SQL Script Files, contain SQL Snippets that are broken down to Commands and Identifiers as 
    /// specified in the internal class SQLParser. Using these commands and Identifiers,
    /// you can Install/Update a SQL Server or SQLCE Database with ease
    /// </summary>
    public class DBInstallOrUpdate
    {
        #region Public and Private Members

        // Public
        public uint CurMajorVersion { get; private set; }
        public uint CurMinorVersion { get; private set; }
        public uint CurRevisionNumber { get; private set; }

        // Private
        private IConnectDb _DBCredential { get; set; }
        private string _uniqueName { get; set; }
        private DBResource _DBResouce { get; set; }
        private string _VersioningTableName { get; set; }

        // SQLScripts
        private string[] _TableScripts = null;
        private string[] _ViewScripts = null;
        private string[] _SprocScripts = null;

        #endregion

        #region Constructors

        /// <summary>
        /// Construct a DBInstallOrUpdate Object in order to install tables, views, sprocs, or
        /// update an existing database
        /// </summary>
        /// <param name="DBCredential">Pass in a DBCredential Object to use in this class</param>
        /// <param name="uniqueName">specify a unique name, that was used when registering a DBResource via DBResources</param>
        /// <param name="CurMajorVersion">Running Application's Current Major Product Version Number (for use in installs/upgrades)</param>
        /// <param name="CurMinorVersion">Running Application's Current Minor Product Version Number (for use in installs/upgrades)</param>
        /// <param name="CurRevisionNumber">Running Application's Current Revision Number (for use in installs/upgrades)</param>
        /// <param name="VersioningTableName">Pass in a Table Name that will be created/updated to contain the latest Application Version Information *used for Update*</param>
        public DBInstallOrUpdate(IConnectDb DBCredential, string uniqueName, uint CurMajorVersion, uint CurMinorVersion, uint CurRevisionNumber, string VersioningTableName = "Versioning")
        {
            if (!DBCredential.IsValid || String.IsNullOrEmpty(uniqueName))
                throw new ArgumentException("DBCredential or uniqueName is Invalid");

            // Make sure DBResource exists
            DBResources.GetDBResource(uniqueName);

            // Set all Private Members
            ConstructorSetsPrivateMembers(DBCredential, uniqueName, CurMajorVersion, CurMinorVersion, CurRevisionNumber, VersioningTableName);
        }

        /// <summary>
        /// Construct a DBInstallOrUpdate Object in order to install tables, views, sprocs, or
        /// update an existing database
        /// </summary>
        /// <param name="DBCredential">Pass in a DBCredential Object to use in this class</param>
        /// <param name="dbResource">pass in a valid DBResource that points to valid SQL Script Locations</param>
        /// <param name="uniqueName">specify a unique name to use, that describes the dbResource used to register the resource</param>
        /// <param name="CurMajorVersion">Running Application's Current Major Product Version Number (for use in installs/upgrades)</param>
        /// <param name="CurMinorVersion">Running Application's Current Minor Product Version Number (for use in installs/upgrades)</param>
        /// <param name="CurRevisionNumber">Running Application's Current Revision Number (for use in installs/upgrades)</param>
        /// <param name="VersioningTableName">Pass in a Table Name that will be created/updated to contain the latest Application Version Information *used for Update*</param>
        public DBInstallOrUpdate(IConnectDb DBCredential, DBResource dbResource, string uniqueName, uint CurMajorVersion, uint CurMinorVersion, uint CurRevisionNumber, string VersioningTableName = "Versioning")
        {
            if (!DBCredential.IsValid || String.IsNullOrEmpty(uniqueName))
                throw new ArgumentException("DBCredential or uniqueName is Invalid");

            // Add the DBResource
            DBResources.AddDBResource(uniqueName, dbResource);

            // Set all Private Members
            ConstructorSetsPrivateMembers(DBCredential, uniqueName, CurMajorVersion, CurMinorVersion, CurRevisionNumber, VersioningTableName);
        }

        /// <summary>
        /// Broken out of the Constructors, to centralize member initialization
        /// </summary>       
        private void ConstructorSetsPrivateMembers(IConnectDb DBCredential, string uniqueName, uint CurMajorVersion, uint CurMinorVersion, uint CurRevisionNumber, string VersioningTableName)
        {
            this._DBCredential = DBCredential;
            this._uniqueName = uniqueName;
            this.CurMajorVersion = CurMajorVersion;
            this.CurMinorVersion = CurMinorVersion;
            this.CurRevisionNumber = CurRevisionNumber;
            this._VersioningTableName = VersioningTableName;

            // Retrieve Scripts
            this._TableScripts = DBResources.GetTableDBResourceScripts(_uniqueName);
            this._ViewScripts = DBResources.GetViewsDBResourceScripts(_uniqueName);
            this._SprocScripts = DBResources.GetSprocsDBResourceScripts(_uniqueName);
        }

        #endregion 

        #region Public Methods and Properties

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public DBError InstallOrUpdate(InstallOrUpdateType type)
        {            
            DBError dbError;
            if (!HasVersioningTable)
                dbError = InstallDatabase(type);
            else
                dbError = UpdateDatabase(type);
            return dbError;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private bool HasVersioningTable
        {
            get
            {
                return true; // for now
            }
        }

        #endregion

        #region private Helper Functions

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private DBError InstallDatabase(InstallOrUpdateType type)
        {
            DBError dbError = new DBError();

            // Install Tables first
            if ((type == InstallOrUpdateType.TABLES || type == InstallOrUpdateType.ALL_IF_EXISTS) && 
                (_TableScripts != null))
                dbError = ExecuteSpecificMySQLCommandsTypesInSQLScripts(null, _TableScripts);
            if (dbError.ErrorOccured) return dbError;

            // Install Views Next
            if ((type == InstallOrUpdateType.VIEWS || type == InstallOrUpdateType.ALL_IF_EXISTS) &&
                (_TableScripts != null))
                dbError = ExecuteSpecificMySQLCommandsTypesInSQLScripts(null, _ViewScripts);
            if (dbError.ErrorOccured) return dbError;

            // Install Sprocs Next
            if ((type == InstallOrUpdateType.VIEWS || type == InstallOrUpdateType.ALL_IF_EXISTS) &&
                (_SprocScripts != null))
                dbError = ExecuteSpecificMySQLCommandsTypesInSQLScripts(null, _SprocScripts);
            if (dbError.ErrorOccured) return dbError;

            return new DBError();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private DBError UpdateDatabase(InstallOrUpdateType type)
        {
            DBError dbError = new DBError();

            // Install Tables first
            if ((type == InstallOrUpdateType.TABLES || type == InstallOrUpdateType.ALL_IF_EXISTS) &&
                (_TableScripts != null))
                dbError = ExecuteSpecificMySQLCommandsTypesInSQLScripts(null, _TableScripts);
            if (dbError.ErrorOccured) return dbError;

            // Install Views Next
            if ((type == InstallOrUpdateType.VIEWS || type == InstallOrUpdateType.ALL_IF_EXISTS) &&
                (_TableScripts != null))
                dbError = ExecuteSpecificMySQLCommandsTypesInSQLScripts(null, _ViewScripts);
            if (dbError.ErrorOccured) return dbError;

            // Install Sprocs Next
            if ((type == InstallOrUpdateType.VIEWS || type == InstallOrUpdateType.ALL_IF_EXISTS) &&
                (_SprocScripts != null))
                dbError = ExecuteSpecificMySQLCommandsTypesInSQLScripts(null, _SprocScripts);
            if (dbError.ErrorOccured) return dbError;

            return new DBError();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="types"></param>
        /// <param name="FRLCS_ScriptFiles"></param>
        /// <returns></returns>
        private DBError ExecuteSpecificMySQLCommandsTypesInSQLScripts(MySQLCommandType[] types, string[] FRLCS_ScriptFiles)
        {
            foreach (string scriptFile in FRLCS_ScriptFiles)
            {
                // Retrieve the Commands N' Identifiers for the Script
                MySQLCommand[] commands = null;
                MySQLIdentifier[] identifiers = null;
                if (!GetCommandsAndIdentifiersFromSqlScript(scriptFile, out commands, out identifiers))
                {
                    // argh.. .this should not happen
                }

                // Only Execute certain types/Versions????
                DBError dbError = commands[0].ExecuteScriptBlock(_DBCredential);
                if (dbError.ErrorOccured)
                {
                    //commands[0].Line_CommandStart

                }
            }
            return null;
        }

        /// <summary>
        /// Uses DBResources and SQLParser to parse an SQLScript into MySQLCommands and MySQLIdentifiers
        /// </summary>
        /// <param name="FRLCS_Script">the FRLCS full resource file and path to the script to parse</param>
        /// <param name="commands">returns out MySQLCommands found in script</param>
        /// <param name="identifiers">returns out MySQLIdentities found in script</param>
        /// <returns>true if commands and identifiers were fetched, false otherwise</returns>
        private bool GetCommandsAndIdentifiersFromSqlScript(string FRLCS_Script, out MySQLCommand[] commands, out MySQLIdentifier[] identifiers)
        {
            string[] ScriptContents = DBResources.Read_FRLCS_Script(_uniqueName, FRLCS_Script);
            SQLParser.ParseSQLScriptForMySQLCommandsAndIdentifiers(ScriptContents, out commands, out identifiers);
            return ((commands != null) && (identifiers != null) && (commands.Length != 0) && (identifiers.Length != 0));
        }

        #endregion
    }
}
