#if SQLSERVER
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace Sdaleo.Systems.SQLServer
{
    #region SQLServer Table Enums

    /// <summary>
    /// User Permissions on a Table
    /// </summary>
    public enum SQLServerTablePermission
    {
        SELECT,
        UPDATE,
        INSERT,
        DELETE,
        REFERENCES,
        // EXECUTE // <-- not a table permission
    }

    #endregion

    /// <summary>
    /// Specific actions for SQL Server Tables in a Database
    /// </summary>
    public class SQLServerTable
    {
        #region Internal Table Helper Functions

        /// <summary>
        /// We must strip out the '_' on the SQLTablePermission Enum to use it in an SQL Query
        /// </summary>
        internal static string SQLTablePermission_ToString(SQLServerTablePermission TablePermission)
        {
            return TablePermission.ToString().Replace('_', ' ');
        }

        /// <summary>
        /// Quick Helper function to get an SQLTablePermission[] that has all Permissions defined
        /// </summary>
        internal static SQLServerTablePermission[] SQLServerTablePermission_ToArray()
        {
            return (SQLServerTablePermission[])Enum.GetValues(typeof(SQLServerTablePermission));
        }

        /// <summary>
        /// Returns a generated SQL Table Permissions string for the specified Table,Username with the specified permissions
        /// </summary>
        /// <param name="TableName">Name of a Table(required)</param>
        /// <param name="Username">Name of an existing User that exists in that Database where the Table Resides (required)</param>
        /// <param name="Action">Permission Action to take</param>
        /// <param name="tablePermissions">array of permissions</param>
        /// <returns>a table permission string or empty string if error occured</returns>
        internal static string BuildSQLServerTablePermissionsStr(String TableName, string Username, SQLServerPermissionAction Action, SQLServerTablePermission[] tablePermissions)
        {
            if (String.IsNullOrEmpty(TableName) || String.IsNullOrEmpty(Username) || (tablePermissions == null))
                return string.Empty;

            // First the Action
            string sql = Action.ToString() + " ";

            // add each permission individually
            for (int i = 0; i < tablePermissions.Length; ++i)
            {
                sql += SQLTablePermission_ToString(tablePermissions[i]);
                bool bIsLast = (i == (tablePermissions.Length - 1));
                if (!bIsLast)
                    sql += ",";
            }

            // Add Table
            sql += String.Format(" ON [{1}]", TableName);

            // add action adjective
            if (Action == SQLServerPermissionAction.REVOKE)
                sql += " FROM ";
            else
                sql += " TO ";

            // add user
            sql += string.Format("[{0}]", Username);

            return sql;
        }

        #endregion

        #region Table Permission

        /// <summary>
        /// Automatically grant,deny,revoke all permissions on the specified table for the specified user
        /// </summary>
        /// <param name="credential">SQL Server Credentials with Database Info</param>
        /// <param name="Username">Specify the Username for the database (required)</param>
        /// <param name="TableName">Specify the TableName to set permissions on (required)</param>
        /// <param name="action">GRANT,DENY, or REVOKE 'Permission_All' Permission on that db</param>
        /// <returns>DBError Object with ErrorOccured, if error Occured</returns>
        public static DBError TableUserPermissionsAll(IConnectDb credential, string Username, string TableName, SQLServerPermissionAction action)
        {
            DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
            if (dbError.ErrorOccured)
                return dbError;

            if (!credential.DBMS.IsDatabaseSetAndNonSystem)
                return DBError.Create("Invalid Database Passed In via Credential");

            if (!ValidationConsts.Generic.IsValidUserName(Username))
                return DBError.Create("Invalid Username Passed In");

            dbError = Validation.IsValidTableName(TableName);
            if (dbError.ErrorOccured)
                return dbError;

            // Get All Permissions
            SQLServerTablePermission[] permissions = SQLServerTablePermission_ToArray();

            // Perform the Action
            return TableUserPermissions(credential, Username, TableName, permissions, action);
        }

        /// <summary>
        /// Allows you to specify permissions to grant,deny,revoke on the specified table for the specified user
        /// </summary>
        /// <param name="credential">SQL Server Credentials with Database Info</param>
        /// <param name="Username">Specify the Username for the database (required)</param>
        /// <param name="TableName">Specify the TableName to set permissions on (required)</param>
        /// <param name="permissions">an array of permissions to take action on on db</param>
        /// <param name="action">GRANT,DENY, or REVOKE specified Permissions on that db</param>
        /// <returns>DBError Object with ErrorOccured, if error Occured</returns>
        public static DBError TableUserPermissions(IConnectDb credential, string Username, string TableName, SQLServerTablePermission[] permissions, SQLServerPermissionAction action)
        {
            DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
            if (dbError.ErrorOccured)
                return dbError;

            if (!credential.DBMS.IsDatabaseSetAndNonSystem)
                return DBError.Create("Invalid Database Passed In via Credential");

            if (!ValidationConsts.Generic.IsValidUserName(Username))
                return DBError.Create("Invalid Username Passed In");

            dbError = Validation.IsValidTableName(TableName);
            if (dbError.ErrorOccured)
                return dbError;

            // Build the SQL and Execute the Query
            string sql = BuildSQLServerTablePermissionsStr(TableName, Username, action, permissions);
            DB db = DB.Create(credential);
            DBRetVal retVal = db.ExecuteNonQuery(sql);
            return retVal;
        }

        #endregion

        #region Table Index

        /// <summary>
        /// Reindexes all Indexes on the Specified Table. 
        /// </summary>
        /// <param name="credential">SQL Server Credentials with Database Info</param>
        /// <param name="TableName">Specify the TableName to set permissions on (required)</param>
        /// <returns>DBError Object with ErrorOccured, if error Occured</returns>
        public static DBError TableReindex(IConnectDb credential, string TableName)
        {
            DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
            if (dbError.ErrorOccured)
                return dbError;

            if (!credential.DBMS.IsDatabaseSetAndNonSystem)
                return DBError.Create("Invalid Database Passed In via Credential");

            dbError = Validation.IsValidTableName(TableName);
            if (dbError.ErrorOccured)
                return dbError;

            // Execute the Query
            string sql = String.Format("DBCC DBREINDEX(\"{0}\", \" \", 90);", TableName);
            DB db = DB.Create(credential);
            DBRetVal retVal = db.ExecuteNonQuery(sql);
            return retVal;           
        }

        /// <summary>
        /// Runs DBCC INDEXDEFRAG on the Specified Database, Table and Index
        /// </summary>
        /// <param name="credential">SQL Server Credentials with Database Info</param>
        /// <param name="TableName">Name of Table (required)</param>
        /// <param name="Index">Name of Index (required)</param>
        /// <returns>DBError Object with ErrorOccured, if error Occured</returns>
        public static DBError TableIndexDefrag(IConnectDb credential, string TableName, string IndexName)
        {
            DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
            if (dbError.ErrorOccured)
                return dbError;

            if (!credential.DBMS.IsDatabaseSetAndNonSystem)
                return DBError.Create("Invalid Database Passed In via Credential");

            dbError = Validation.IsValidTableName(TableName);
            if (dbError.ErrorOccured)
                return dbError;

            if (String.IsNullOrEmpty(IndexName))
                return DBError.Create("Invalid IndexName Passed In");

            // Execute the Query
            string sql = String.Format("DBCC INDEXDEFRAG ([{0}], [{1}], [{2}]) WITH NO_INFOMSGS", credential.DBMS.Database, TableName, IndexName);
            DB db = DB.Create(credential);
            DBRetVal retVal = db.ExecuteNonQuery(sql);
            return retVal;
        }

        #endregion

        #region Common Table Functions

        /// <summary>
        /// Returns all the Table Names for the specified Database
        /// </summary>
        /// <param name="credential">SQL Server Credentials with Database Info</param>
        /// <param name="Tables">array of table names, or null if error occured</param>
        /// <returns>DBError Object with ErrorOccured, if error Occured</returns>
        public static DBError Tables(IConnectDb credential, out string[] Tables)
        {
            Tables = null;
            DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
            if (dbError.ErrorOccured)
                return dbError;

            if (!credential.DBMS.IsDatabaseSetAndNonSystem)
                return DBError.Create("Invalid Database Passed In via Credential");            

            // Execute the Query
            string sql = "SELECT [name] FROM sys.Tables ORDER BY [name]";
            DB db = DB.Create(credential);
            DBRetVal retVal = db.FillDataTable(sql);
            if (retVal.IsValid)
            {
                List<String> retList = new List<string>();
                foreach (DataRow row in retVal.GetDataTableRetVal().Rows)
                         retList.Add(row["name"].ToString());
                Tables = retList.ToArray();
            }
            return retVal;
        }

        /// <summary>
        /// Check to see if the Table Exists on the specified Database
        /// </summary>
        /// <param name="credential">SQL Server Credentials with Database Info</param>
        /// <param name="TableName">Specify the TableName to check for (required)</param>
        /// <param name="bExists">true if exists, false otherwise</param>
        /// <returns>DBError Object with ErrorOccured, if error Occured</returns>
        public static DBError TableExists(IConnectDb credential, string TableName, out bool bExists)
        {
            bExists = false;
            DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
            if (dbError.ErrorOccured)
                return dbError;

            if (!credential.DBMS.IsDatabaseSetAndNonSystem)
                return DBError.Create("Invalid Database Passed In via Credential");

            dbError = Validation.IsValidTableName(TableName);
            if (dbError.ErrorOccured)
                return dbError;

            // Execute the Query
            string sql = String.Format("SELECT [name] FROM sys.Tables WHERE [name]='{0}' ORDER BY [name]", TableName);
            DB db = DB.Create(credential);
            DBRetVal retVal = db.ExecuteScalar(sql);
            bExists = !retVal.IsValid;
            return retVal;
        }

        /// <summary>
        /// Changes the Table Name for the specified Table
        /// </summary>
        /// <param name="credential">SQL Server Credentials with Database Info</param>
        /// <param name="TableName">Name of Table (required)</param>
        /// <param name="TableNameNew">Name of Table New (required)</param>
        /// <returns>DBError Object with ErrorOccured, if error Occured</returns>
        public static DBError TableRename(IConnectDb credential, string TableName, string TableNameNew)
        {
            DBError dbError = ValidationConsts.IsCredentialValid(credential, DBSystem.SQL_SERVER);
            if (dbError.ErrorOccured)
                return dbError;

            if (!credential.DBMS.IsDatabaseSetAndNonSystem)
                return DBError.Create("Invalid Database Passed In via Credential");

            dbError = Validation.IsValidTableName(TableName);
            if (dbError.ErrorOccured)
                return dbError;

            dbError = Validation.IsValidTableName(TableNameNew);
            if (dbError.ErrorOccured)
                return dbError;

            // Update the Index Names using SMO * UNTESTED *
            //Server server = DataType.ConvertCredentialToSMOServer(credential);
            //foreach (Index index in server.Databases[credential.Database].Tables[TableName].Indexes)
            //    index.Name = index.Name.Replace(TableName, TableNameNew);
            //server.Databases[credential.Database].Tables[TableName].Alter();

            // Execute the Query
            string sql = String.Format("EXEC sp_rename @objname = [{0}], @newname = [{1}], @objtype = 'OBJECT'", TableName, TableNameNew);
            DB db = DB.Create(credential);
            DBRetVal retVal = db.ExecuteNonQuery(sql);
            return retVal;
        }

        #endregion
    }
}
#endif