using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlServerCe;
using Foo.DataAccessLayer.DataTypes;

namespace Foo.DataAccessLayer
{
    public class dWorkspace
    {

        /// <summary>
        /// Quick Check to see If the WorkspaceName already exists
        /// </summary>
        /// <param name="WorkspaceName">Name of Workspace</param>
        /// <returns>true if Name exists, false otherwise</returns>
        public bool DoesWorkspaceNameExist(string WorkspaceName)
        {
            // Check WorkspaceName Integrity
            if (!DataTypeValidation.IsValidWorkspaceName(WorkspaceName))
                return false;

            object obj = null;
            lock (this)
            {
                string sql = "SELECT [Name] FROM Workspaces WHERE [Name] = @workspacename";
                SqlCeParameter[] sqlparams = new SqlCeParameter[] { new SqlCeParameter("@workspacename", WorkspaceName) };

                obj = DB.RunSQLCommandTextExecuteScalar(sql, sqlparams);
            }
            return (obj != null);
        }

        /// <summary>
        /// Retrieve a String list of all WorkspaceNames in the Database
        /// </summary>
        /// <param name="SortOrder">Specify the SortOrder of the List</param>
        /// <returns>a list of worspace names, or empty[] if none exist</returns>
        public string[] GetAllWorkspaceNames(SortOrderForWorkspaces SortBy)
        {
            List<String> resultList = new List<String>();
            lock (this)
            {
                string sql = "SELECT [Name] FROM Workspaces";

                // Add WorkspaceSortOrder
                sql = SortOrderSQLHelpers.SortOrderForWorkspacesHelper(sql, SortBy); 

                DataSet dataset = DB.RunSQLTextFillDataSet(sql);
                if (!DataTypeValidation.IsEmptyDataSet(dataset))
                {
                    foreach (DataRow row in dataset.Tables[0].Rows)
                        resultList.Add(row[0].ToString());
                }                
            }
            return resultList.ToArray<String>();
        }

        /// <summary>
        /// Retrieve a Workspace List of all Workspaces in the Database
        /// </summary>
        /// <param name="SortOrder">Specify the SortOrder of the List</param>
        /// <returns>a list of worspaces</returns>
        public WorkspaceItem[] GetAllWorkspaces(SortOrderForWorkspaces SortBy)
        {
            List<String> resultList = new List<String>();
            lock (this)
            {
                string sql = "SELECT [Name],[LastAccessed],[AccessCounter] FROM Workspaces";

                // Add WorkspaceSortOrder
                sql = SortOrderSQLHelpers.SortOrderForWorkspacesHelper(sql, SortBy);               

                DataSet dataset = DB.RunSQLTextFillDataSet(sql);                
                if (!DataTypeValidation.IsEmptyDataSet(dataset))
                {
                    int nRows = dataset.Tables[0].Rows.Count;
                    WorkspaceItem[] resultWorkspaces = new WorkspaceItem[nRows];

                    for (int i = 0; i < nRows; ++i)
                    {
                        DataRow datarow = dataset.Tables[0].Rows[i];
                        resultWorkspaces[i] = new WorkspaceItem();

                        // Add Workspace Fields
                        resultWorkspaces[i].Name = datarow["Name"].ToString();

                        if (!String.IsNullOrEmpty(datarow["LastAccessed"].ToString()))
                            resultWorkspaces[i].SetLastAccessed(DateTime.Parse(datarow["LastAccessed"].ToString()));

                        if (!String.IsNullOrEmpty(datarow["AccessCounter"].ToString()))
                            resultWorkspaces[i].AccessCounter = int.Parse(datarow["AccessCounter"].ToString());                        
                    }

                    return resultWorkspaces;                    
                }
            }

            // return Blank Item                        
            return DataTypeHelpers.EmptyWorkspaceItemGroup();            
        }

        /// <summary>
        /// Use this to modify a workspace Item (allows you to modify a workspace's properties NOT the name)
        /// </summary>
        /// <param name="workspaceItem">a workspaceItem whose properties you want to update (Must pass all validations)</param>
        /// <returns>true if succesfully updated, false otherwise</returns>
        public bool ModifyWorkspaceItem(WorkspaceItem workspaceItem)
        {
            // Check WorkspaceName Integrity
            if ((workspaceItem == null) ||                
                !workspaceItem.IsValid)
                return false;

            lock (this)
            {
                // existence check
                if (!DoesWorkspaceNameExist(workspaceItem.Name))
                    return false;

                // First Step - is to create the update statement to make the neccessary modifications
                string sql2 = "UPDATE Workspaces SET [LastAccessed]=@lastaccessed,[AccessCounter]=@accesscounter WHERE [Name]=@workspacename";
                SqlCeParameter[] sqlparams2 = new SqlCeParameter[]
                {
                    new SqlCeParameter("@accesscounter", workspaceItem.AccessCounter),
                    new SqlCeParameter("@lastaccessed", workspaceItem.LastAccessed),
                    new SqlCeParameter("@workspacename", workspaceItem.Name),
                };

                int nResult = DB.RunSQLCommandTextExecuteNonQuery(sql2, sqlparams2);
                return (nResult == 1);
            }
        }

        /// <summary>
        /// Increments the access counter and sets LastAccessed for the 
        /// specified workspace name. Call this whenever you have launched a workspace.
        /// </summary>
        /// <param name="WorkspaceName">an existing workspace Name</param>
        /// <returns>true if successful set the internal counters, false otherwise</returns>
        public bool WorkspaceWasLaunched(string WorkspaceName)
        {
            // Check WorkspaceName Integrity
            if (!DataTypeValidation.IsValidWorkspaceName(WorkspaceName))
                return false;

            lock (this)
            {
                // existence check
                if (!DoesWorkspaceNameExist(WorkspaceName))
                    return false;

                // First Step - is to get the AccessCounter already in the db
                string sql = "SELECT [AccessCounter] FROM Workspaces WHERE [Name]=@workspacename";
                SqlCeParameter[] sqlparams = new SqlCeParameter[] { new SqlCeParameter("@workspacename", WorkspaceName) };

                int accesscount = 1;
                object obj = DB.RunSQLCommandTextExecuteScalar(sql, sqlparams);
                if (obj != null)
                {
                    accesscount = int.Parse(obj.ToString());
                    accesscount = accesscount + 1; // increment by 1
                }
                
                // Second Step - is to set the access counter and today's date for the workspace
                string sql2 = "UPDATE Workspaces SET [LastAccessed]=@lastaccessed,[AccessCounter]=@accesscounter WHERE [Name]=@workspacename";
                SqlCeParameter[] sqlparams2 = new SqlCeParameter[]
                {
                    new SqlCeParameter("@lastaccessed", DateTime.Now),
                    new SqlCeParameter("@accesscounter", accesscount),
                    new SqlCeParameter("@workspacename", WorkspaceName),
                };

                int nResult = DB.RunSQLCommandTextExecuteNonQuery(sql2, sqlparams2);
                return (nResult == 1);
            }
        }        

        /// <summary>
        /// Inserts a new WorspaceName into the Workspace Table (if it not already exists)
        /// </summary>
        /// <param name="WorkspaceName">new non-existing unique workspace Name</param>
        /// <returns>true if successful, false otherwise</returns>
        public bool InsertWorkspaceName(string WorkspaceName)
        {
            // Check WorkspaceName Integrity
            if (!DataTypeValidation.IsValidWorkspaceName(WorkspaceName))
                return false;

            int nResult = -1;
            lock (this)
            {
                // ensure uniqueness
                if (DoesWorkspaceNameExist(WorkspaceName))
                    return false;
               
                string sql = "INSERT INTO Workspaces ([Name],[LastAccessed],[AccessCounter]) VALUES (@workspacename,@accessed,@count)";
                SqlCeParameter[] sqlparams = new SqlCeParameter[] 
                { 
                    new SqlCeParameter("@workspacename",WorkspaceName),
                    new SqlCeParameter("@accessed",DateTime.Now),
                    new SqlCeParameter("@count","0"),
                };

                nResult = DB.RunSQLCommandTextExecuteNonQuery(sql, sqlparams);
            }
            return (nResult == 1);
        }

        /// <summary>
        /// Renames a Workspace to a new Name. In order to do so, we must also rename
        /// all possible links that exists, so this process is a two-step process
        /// </summary>
        /// <param name="OldWorkspaceName"></param>
        /// <param name="NewWorkspaceName"></param>
        /// <returns>true if successful, false otherwise</returns>
        public bool RenameWorkspace(string OldWorkspaceName, string NewWorkspaceName)
        {
            // Check WorkspaceName Integrity
            if (!DataTypeValidation.IsValidWorkspaceName(OldWorkspaceName) ||
                !DataTypeValidation.IsValidWorkspaceName(NewWorkspaceName))
                return false;

            lock (this)
            {
                // ensure existence
                if (!DoesWorkspaceNameExist(OldWorkspaceName))
                    return false;

                // First Step - update Workspace Table
                string sql = "UPDATE Workspaces SET [Name]=@newworkspacename WHERE [Name]=@oldworkspacename";
                SqlCeParameter[] sqlparams = new SqlCeParameter[]
                {
                    new SqlCeParameter("@oldworkspacename", OldWorkspaceName),
                    new SqlCeParameter("@newworkspacename", NewWorkspaceName),
                };

                int nResult = DB.RunSQLCommandTextExecuteNonQuery(sql, sqlparams);
                if (nResult != 1)
                    return false;

                // Second Step - update Links Table (any links that point to this workspace)
                string sql2 = "UPDATE WorkspaceLinks SET [WorkspaceName]=@newworkspacename WHERE [WorkspaceName]=@oldworkspacename";
                SqlCeParameter[] sqlparams2 = new SqlCeParameter[]
                {
                    new SqlCeParameter("@oldworkspacename", OldWorkspaceName),
                    new SqlCeParameter("@newworkspacename", NewWorkspaceName),
                };

                int nResult2 = DB.RunSQLCommandTextExecuteNonQuery(sql2, sqlparams2);
                if (nResult2 >= 0)
                    return true;
                else
                    return false;
            }
        }

        /// <summary>
        /// Deletes a Workspace. In order to do so, we must also delete and links
        /// that the Workspace has. When Deleting links and encountering a link to
        /// an artifact that is not references anywhere, we should delete the artifact 
        /// as well. So this is a five-step process.
        /// </summary>
        /// <param name="WorkspaceName">an existing workspace name</param>
        /// <returns>true if successful, false otherwise</returns>
        public bool DeleteWorkspace(string WorkspaceName)
        {
            // Check WorkspaceName Integrity
            if (!DataTypeValidation.IsValidWorkspaceName(WorkspaceName))
                return false;

            lock (this)
            {
                // ensure existence
                if (!DoesWorkspaceNameExist(WorkspaceName))
                    return false;

                // First Step - delete WorkspaceName from Workspace Table
                string sql = "DELETE FROM Workspaces WHERE [Name]=@workspacename";
                SqlCeParameter[] sqlparams = new SqlCeParameter[] { new SqlCeParameter("@workspacename", WorkspaceName) };

                int nResult = DB.RunSQLCommandTextExecuteNonQuery(sql, sqlparams);
                if (nResult != 1)
                    return false;

                // Second Step - We need all the links that are part of the workspace in the links table
                string sql2 = "SELECT [AID] FROM WorkspaceLinks WHERE [WorkspaceName]=@workspacename";
                SqlCeParameter[] sqlparams2 = new SqlCeParameter[] { new SqlCeParameter("@workspacename", WorkspaceName) };

                DataSet dataset = DB.RunSQLTextFillDataSet(sql2, sqlparams2);
                List<int> workspaceLinksListAIDs = new List<int>();

                if (!DataTypeValidation.IsEmptyDataSet(dataset))
                {
                    foreach (DataRow row in dataset.Tables[0].Rows)
                        workspaceLinksListAIDs.Add(int.Parse(row[0].ToString()));
                }

                // Third Step - We now have all the AIDs to look later so we can now delete all the links for this
                //              workspace from the links table
                string sql3 = "DELETE FROM WorkspaceLinks WHERE [WorkspaceName]=@workspacename";
                SqlCeParameter[] sqlparams3 = new SqlCeParameter[] { new SqlCeParameter("@workspacename", WorkspaceName) };

                nResult = DB.RunSQLCommandTextExecuteNonQuery(sql3, sqlparams3);
                if (nResult == -1)
                    return false;

                // Fourth Step - We now should check which AIDs are still referenced in the Link table, those that
                //               still have references, we keep, others we want to delete
                string sql4 = "SELECT [UID] FROM WorkspaceLinks WHERE [AID]=@aid";                     
                List<int> workspaceLinksListAIDsWithoutReferences = new List<int>();

                foreach (int aid in workspaceLinksListAIDs)
                {
                    SqlCeParameter[] sqlparams4 = new SqlCeParameter[] { new SqlCeParameter("@aid", aid) };
                    object obj = DB.RunSQLCommandTextExecuteScalar(sql4, sqlparams4);

                    if (obj == null) // this aid has no references
                        workspaceLinksListAIDsWithoutReferences.Add(aid);
                }

                // Fifth Step - Now we want to delete all artifacts in the artifact table that have no references
                //              Since we already deleted all links, and workspaces, we should just directly delete the dangling
                //              Artifacts here
                string sql5 = "DELETE FROM Artifacts WHERE [UID]=@uid";
                foreach (int aid in workspaceLinksListAIDsWithoutReferences)
                {
                    SqlCeParameter[] sqlparam5 = new SqlCeParameter[]{ new SqlCeParameter("@uid", aid) };
                    nResult = DB.RunSQLCommandTextExecuteNonQuery(sql5, sqlparam5);

                    if (nResult != 1) // we expect there to be an artifact to delete
                        return false;
                }                              
            }
            return true;
        }

    }
}
