using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SystemX.Common;
using SystemX.Common.Serialization;
using static SystemX.Net.Platform.Common.Util.LogMessage;

namespace SystemX.Net.DB.DBType
{
    public partial class XDBTMSSQL : IDBControl, IDisposable
    {
        private SqlConnection dbConnection;
        private SqlCommand dbCommand;
        private SqlDataReader queryDataReader;

        private DataSet queryDataSet;
        private DataTable queryDataTable;

        private byte[] ucCollectSerializationSchema;
        private XTableInfo XSchemaTables;

        private DataTable HostList;
        private DataTable UserList;

        public string ConnectionText { get; set; }

        public bool IsConnected { get; set; }

        private string strSetHostTableName;
        private string strSetUserTableName;

        public byte[] GetCollectSerializationSchemaInfo()
        {
            return ucCollectSerializationSchema;
        }

        public XTableInfo GetCollectSchemaInfo()
        {
            return XSchemaTables;
        }

        public DataTable GetHostList(bool bDBQueryCheck = false)
        {
            if(bDBQueryCheck == false)
                return HostList;
            else
            {
                if (QueryHostList(strSetHostTableName))
                    return HostList;
                else
                    return null;
            }
        }

        public DataTable GetUserList(bool bDBQueryCheck = false)
        {
            if (bDBQueryCheck == false)
                return UserList;
            else
            {
                if (QueryUserList(strSetUserTableName))
                    return UserList;
                else
                    return null;
            }
        }

        public XDBTMSSQL()
        {
            HostList = null;
        }
        ~XDBTMSSQL()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool bDisposing)
        {
            if (bDisposing)
                ChkCloseConnect();

            // do releasing unmanaged resource (종결자가 없는 객체의 자원 해제)
            // i.e. close file handle of operating systems

            // suppress calling of Finalizer
            GC.SuppressFinalize(this);
        }

        public bool InitConnection()
        {
            try
            {
                ChkCloseConnect();

                dbConnection = null;
                dbCommand = null;
                queryDataReader = null;
                queryDataSet = null;
                queryDataTable = null;

                IsConnected = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SystemX Database Initialization is Failed. Error: {ex.Message}");

                return false;
            }

            return true;
        }

        public void ChkCloseConnect()
        {
            if (IsConnected)
            {
                if (dbConnection != null)
                {
                    dbConnection.Close();
                    dbConnection.Dispose();
                    dbConnection = null;
                }

                IsConnected = false;
            }
        }

        public bool OpenConnection()
        {
            ChkCloseConnect();

            try
            {
                dbCommand = new SqlCommand();
                dbConnection = new SqlConnection(ConnectionText);
                dbConnection.Open();

                dbCommand.Connection = dbConnection;
                    
                if (CollectSchemaInfomation())
                    IsConnected = true;
                else
                    Console.WriteLine($"SystemX Database CollectSchemaInfomation() is Failed.");
            }
            catch (SqlException exsql)
            {
                Console.WriteLine($"SystemX Database Connection Open is Failed. Error: {exsql.Message}");

                IsConnected = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SystemX Database Connection Open is Failed. Error: {ex.Message}");

                IsConnected = false;
            }

            return IsConnected;
        }
        public bool OpenConnection(bool bUseHostInfo, string strHostTableName, string strUserTableName)
        {
            ChkCloseConnect();

            strSetHostTableName = string.Empty;
            strSetUserTableName = string.Empty;

            try
            {
                dbCommand = new SqlCommand();
                dbConnection = new SqlConnection(ConnectionText);
                dbConnection.Open();

                dbCommand.Connection = dbConnection;

                if (CollectSchemaInfomation())
                {
                    if (bUseHostInfo)
                    {
                        strSetHostTableName = strHostTableName;
                        strSetUserTableName = strUserTableName;

                        if (QueryHostList(strSetHostTableName) &&
                            QueryUserList(strSetUserTableName))
                            IsConnected = true;
                        else
                            Console.WriteLine($"SystemX Database QueryHostList() is Failed.");
                    }
                    else
                        IsConnected = true;
                }
                else
                    Console.WriteLine($"SystemX Database CollectSchemaInfomation() is Failed.");
            }
            catch (SqlException exsql)
            {
                Console.WriteLine($"SystemX Database Connection Open is Failed. Error: {exsql.Message}");

                IsConnected = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SystemX Database Connection Open is Failed. Error: {ex.Message}");

                IsConnected = false;
            }

            return IsConnected;
        }

        public bool CloseConnection()
        {
            try
            {
                ChkCloseConnect();
            }
            catch (SqlException exsql)
            {
                Console.WriteLine($"SystemX Database Connection Open is Failed. Error: {exsql.Message}");

                throw;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SystemX Database Connection Open is Failed. Error: {ex.Message}");

                throw;
            }

            return true;
        }


        public SqlConnection getConnection()
        {
            return dbConnection;
        }

        public string MakeTableInfoQuery(string strTableName)
        {
            //--컬럼 정보 가져오기
            string strTableColInfoCommand = "SELECT A.TABLE_CATALOG" +
                                            ",A.TABLE_NAME" +
                                            ",A.ORDINAL_POSITION" +
                                            ",A.COLUMN_NAME" +
                                            ",A.DATA_TYPE" +
                                            ",ISNULL(A.CHARACTER_MAXIMUM_LENGTH, '') CHARACTER_LENGTH" +
                                            ",ISNULL(A.NUMERIC_PRECISION, '') NUMBERIC_LENGTH" +
                                            ",A.IS_NULLABLE" +
                                            ",ISNULL(A.COLUMN_DEFAULT, '')" +
                                            ",ISNULL(B.CONSTRAINT_NAME, '')" +
                                            ",ISNULL(A.CHARACTER_SET_NAME, '')" +
                                            ",ISNULL(A.COLLATION_NAME, '')" +
                                            ",CASE WHEN ISNULL(C.NAME, '') = '' THEN '' ELSE 'Identity' END AUTO" +
                                            " FROM INFORMATION_SCHEMA.COLUMNS A LEFT OUTER JOIN" +
                                            " INFORMATION_SCHEMA.KEY_COLUMN_USAGE B" +
                                            " ON A.TABLE_NAME = B.TABLE_NAME" +
                                            " AND A.COLUMN_NAME = B.COLUMN_NAME" +
                                            " LEFT OUTER JOIN" +
                                            " syscolumns C" +
                                            " ON C.ID = object_id(A.TABLE_NAME) AND A.COLUMN_NAME = C.NAME AND C.COLSTAT & 1 = 1 WHERE A.TABLE_NAME = " +
                                            "'" + strTableName + "'" +
                                            " ORDER BY A.ORDINAL_POSITION";

            return strTableColInfoCommand;
        }

        public bool CollectSchemaInfomation()
        {
            bool bInfoCollectResult = true;

            try
            {
                dbCommand.CommandText = "SELECT * FROM INFORMATION_SCHEMA.TABLES A ORDER BY A.TABLE_NAME;";

                queryDataReader = dbCommand.ExecuteReader();
                DataTable dtQueryTable = new DataTable();
                dtQueryTable.Load(queryDataReader);

                List<string> listTableInfo = new List<string>();

                if (dtQueryTable.Columns.Contains("TABLE_NAME"))
                {
                    int iColPos = dtQueryTable.Columns.IndexOf("TABLE_NAME");

                    for (int i = 0; i < dtQueryTable.Columns[0].Table.Rows.Count; i++)
                    {
                        object[] objRows = dtQueryTable.Columns[0].Table.Rows[i].ItemArray;

                        string strGetTableName = objRows[iColPos] as string;

                        if (strGetTableName.IndexOf("sysdiagrams") < 0)
                            listTableInfo.Add(objRows[iColPos] as string);
                    }
                }

                int iNamePos = 0, iTypePos = 0, iCharLengPos = 0, iNumLengPos = 0, iIsNullAblePos = 0, iAutoPos = 0;

                int iPosition = 0;
                XTable[] listTable = new XTable[listTableInfo.Count];

                foreach (string strTbName in listTableInfo)
                {
                    dbCommand.CommandText = MakeTableInfoQuery(strTbName);
                    queryDataReader = dbCommand.ExecuteReader();
                    dtQueryTable = new DataTable();
                    dtQueryTable.Load(queryDataReader);

                    if (dtQueryTable.Columns.Contains("COLUMN_NAME"))
                        iNamePos = dtQueryTable.Columns.IndexOf("COLUMN_NAME");
                    if (dtQueryTable.Columns.Contains("DATA_TYPE"))
                        iTypePos = dtQueryTable.Columns.IndexOf("DATA_TYPE");
                    if (dtQueryTable.Columns.Contains("CHARACTER_LENGTH"))
                        iCharLengPos = dtQueryTable.Columns.IndexOf("CHARACTER_LENGTH");
                    if (dtQueryTable.Columns.Contains("NUMBERIC_LENGTH"))
                        iNumLengPos = dtQueryTable.Columns.IndexOf("NUMBERIC_LENGTH");
                    if (dtQueryTable.Columns.Contains("IS_NULLABLE"))
                        iIsNullAblePos = dtQueryTable.Columns.IndexOf("IS_NULLABLE");
                    if (dtQueryTable.Columns.Contains("AUTO"))
                        iAutoPos = dtQueryTable.Columns.IndexOf("AUTO");

                    listTable[iPosition] = new XTable(strTbName);
                    for (int i = 0; i < dtQueryTable.Rows[0].Table.Rows.Count; i++)
                    {
                        object[] objRows = dtQueryTable.Columns[0].Table.Rows[i].ItemArray;

                        string strGetName = objRows[iNamePos] as string;
                        string strGetTypeName = objRows[iTypePos] as string;
                        int iGetTypeCharLeng = Convert.ToInt32(objRows[iCharLengPos]);
                        int iGetTypeNumLeng = Convert.ToInt32(objRows[iNumLengPos]);
                        string strGetNullState = objRows[iIsNullAblePos] as string;
                        string strGetAuto = objRows[iAutoPos] as string;

                        bool bGetIsNullAble = false;

                        if (strGetNullState.IndexOf("YES") >= 0)
                            bGetIsNullAble = true;
                        else if (strGetNullState.IndexOf("NO") >= 0)
                            bGetIsNullAble = false;

                        Type SetThisType = null;
                        switch (strGetTypeName)
                        {
                            case "bit":
                                //SET_CODE = TypeCode.Boolean;
                                SetThisType = typeof(Boolean);
                                break;
                            case "tinyint":
                                //SET_CODE = TypeCode.Int64;
                                SetThisType = typeof(Byte);
                                break;
                            case "int":
                                //SET_CODE = TypeCode.Int32;
                                SetThisType = typeof(Int32);
                                break;
                            case "bigint":
                                //SET_CODE = TypeCode.Int64;
                                SetThisType = typeof(Int64);
                                break;
                            case "float":
                                //SET_CODE = TypeCode.Double;
                                SetThisType = typeof(Double);
                                break;
                            case "nvarchar":
                                //SET_CODE = TypeCode.String;
                                SetThisType = typeof(String);
                                break;
                            case "nchar":
                                //SET_CODE = TypeCode.String;
                                SetThisType = typeof(String);
                                break;
                            case "datetime":
                                //SET_CODE = TypeCode.DateTime;
                                SetThisType = typeof(DateTime);
                                break;
                        }

                        listTable[iPosition].XTableAdd(i, strGetName, SetThisType, iGetTypeCharLeng, bGetIsNullAble);
                    }

                    iPosition++;
                }

                XSchemaTables = new XTableInfo(listTable);

                ucCollectSerializationSchema = SystemXNetSerialization.ObjectToByteArray(XSchemaTables);
                XTableInfo XTablesReturn = (XTableInfo)SystemXNetSerialization.ByteArrayToObject(ucCollectSerializationSchema);
            }
            catch (Exception e)
            {
                bInfoCollectResult = false;

                //throw new Exception("Collect Schema infomation fail!");

                MessageOutput.ConsoleWrite(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss>>") + @"Collect Schema infomation fail![SystemX.Net.DB.DBType : XDBTMSSQL.CollectSchemaInfomation]\r\n" + e.Message, ConsoleColor.Yellow, LogMessageLevel.DEBUG);
            }
            finally
            {
                if (queryDataReader != null)
                    queryDataReader.Close();
            }

            return bInfoCollectResult;
        }
        public bool QueryHostList(string strTableName)
        {
            bool bReuslt = true;

            try
            {
                dbCommand.CommandText = "SELECT * FROM " + $"{strTableName} WITH(NOLOCK) ORDER BY NO ASC;";

                queryDataReader = dbCommand.ExecuteReader();
                HostList = new DataTable();
                HostList.Load(queryDataReader);
            }
            catch(Exception e)
            {
                HostList = null;

                bReuslt = false;
            }
            finally
            {
                if (queryDataReader != null)
                    queryDataReader.Close();
            }

            return bReuslt;
        }

        public bool QueryUserList(string strTableName)
        {
            bool bReuslt = true;

            try
            {
                dbCommand.CommandText = "SELECT * FROM " + $"{strTableName} WITH(NOLOCK) ORDER BY NO ASC;";

                queryDataReader = dbCommand.ExecuteReader();
                UserList = new DataTable();
                UserList.Load(queryDataReader);
            }
            catch (Exception e)
            {
                UserList = null;

                bReuslt = false;
            }
            finally
            {
                if (queryDataReader != null)
                    queryDataReader.Close();
            }

            return bReuslt;
        }
    }
}

