Imports System.Data.Common
Imports System.Data.SqlClient
Imports DigitalData.Modules.Logging

Public Class MSSQLServer
    Implements IDatabase

    Public DBInitialized As Boolean = False
    Public CurrentSQLConnectionString As String = ""

    Private ReadOnly _Timeout As Integer
    Private ReadOnly _Logger As Logger

    Public Sub New(LogConfig As LogConfig, ConnectionString As String, Optional Timeout As Integer = 120)
        _Logger = LogConfig.GetLogger()
        _Timeout = Timeout

        CurrentSQLConnectionString = ConnectionString

        Try
            DBInitialized = TestCanConnect()
        Catch ex As Exception
            DBInitialized = False
            _Logger.Error(ex)
        End Try
    End Sub

    Public Sub New(LogConfig As LogConfig, Server As String, Database As String, UserId As String, Password As String, Optional Timeout As Integer = 120)
        _Logger = LogConfig.GetLogger()
        _Timeout = Timeout

        CurrentSQLConnectionString = GetConnectionString(Server, Database, UserId, Password)

        Try
            DBInitialized = TestCanConnect()
        Catch ex As Exception
            DBInitialized = False
            _Logger.Error(ex)
        End Try
    End Sub

    Public Shared Function GetConnectionString(Server As String, Database As String, UserId As String, Password As String) As String
        Dim oConnectionStringBuilder As New SqlConnectionStringBuilder() With {
            .DataSource = Server,
            .InitialCatalog = Database,
            .UserID = UserId,
            .Password = Password
        }

        Return oConnectionStringBuilder.ToString
    End Function

    Private Function TestCanConnect() As Boolean
        Try
            Dim oConnection As New SqlConnection(CurrentSQLConnectionString)
            oConnection.Open()
            oConnection.Close()
            Return True
        Catch ex As Exception
            _Logger.Error(ex)
            Return False
        End Try
    End Function

    Private Function GetSQLConnection() As SqlConnection
        Try
            Dim oConnection As New SqlConnection(CurrentSQLConnectionString)
            oConnection.Open()
            Dim OMyConnection As New SqlClient.SqlConnection(CurrentSQLConnectionString) 'csb.ConnectionString)
            Dim csb As New SqlClient.SqlConnectionStringBuilder
            csb.ConnectionString = CurrentSQLConnectionString
            Dim constr = OMyConnection.ConnectionString
            Try
                constr = constr.Replace(csb.Password, "XXXXX")
            Catch ex As Exception

            End Try
            'If constr.Contains("Trusted_Connection=True") = False Then
            '    constr = constr.Replace(csb.Password, "XXXXX")
            'End If
            _Logger.Debug($"Following Connectionstring is open: {constr}")
            Return oConnection
        Catch ex As Exception
            _Logger.Error(ex)

            Return Nothing
        End Try
    End Function

    ''' <summary>
    ''' Returns a datatable for a sql-statement 
    ''' </summary>
    ''' <param name="SqlCommand">sqlcommand for datatable (select XYZ from TableORView)</param>
    ''' <returns>Returns a datatable</returns>
    Public Function GetDatatable(SqlCommand As String, Timeout As Integer) As DataTable Implements IDatabase.GetDatatable
        Try
            If TestCanConnect() = False Then
                Return Nothing
            End If

            Using oConnection = GetSQLConnection()
                Using oSQLCOmmand = oConnection.CreateCommand()
                    oSQLCOmmand.CommandText = SqlCommand
                    oSQLCOmmand.CommandTimeout = Timeout

                    Dim dt As DataTable = New DataTable()
                    Dim oAdapter As SqlDataAdapter = New SqlDataAdapter(oSQLCOmmand)
                    oAdapter.Fill(dt)
                    Return dt
                End Using
            End Using
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("sqlcommand: " & SqlCommand)
            Return Nothing
        End Try
    End Function

    Public Function GetDatatable(SqlCommand As String) As DataTable Implements IDatabase.GetDatatable
        Return GetDatatable(SqlCommand, _Timeout)
    End Function

    ''' <summary>
    ''' Executes the passed sql-statement 
    ''' </summary>
    ''' <param name="executeStatement">the sql statement</param>
    ''' <returns>Returns true if properly executed, else false</returns>
    ''' <remarks></remarks>
    Public Function NewExecutenonQuery(executeStatement As String) As Boolean
        '_Logger.Warn("NewExecutenonQuery is deprecated. Use ExecuteNonQuery instead.")
        Return ExecuteNonQuery(executeStatement)
    End Function

    Public Function ExecuteNonQuery(SQLCommand As String, Timeout As Integer) As Boolean Implements IDatabase.ExecuteNonQuery
        Try
            If TestCanConnect() = False Then
                Return Nothing
            End If

            Using oConnection = GetSQLConnection()
                Using oSQLCOmmand = oConnection.CreateCommand()
                    oSQLCOmmand.CommandText = SQLCommand
                    oSQLCOmmand.CommandTimeout = Timeout
                    oSQLCOmmand.ExecuteNonQuery()
                    Return True
                End Using
            End Using
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("executeStatement: " & SQLCommand)
            Return False
        End Try
    End Function

    Public Function ExecuteNonQuery(SQLCommand As String) As Boolean Implements IDatabase.ExecuteNonQuery
        Return ExecuteNonQuery(SQLCommand, _Timeout)
    End Function

    ''' <summary>
    ''' Executes the passed sql-statement as Scalar
    ''' </summary>
    ''' <param name="ScalarSQL">the sql statement</param>
    ''' <returns>Returns true if properly executed, else false</returns>
    Public Function NewExecuteScalar(ScalarSQL As String) As Object
        _Logger.Warn("NewExecuteScalar is deprecated. Use GetScalarValue instead.")
        Return GetScalarValue(ScalarSQL)
    End Function

    Public Function GetScalarValue(SQLQuery As String, Timeout As Integer) As Object Implements IDatabase.GetScalarValue
        Try
            If TestCanConnect() = False Then
                Return Nothing
            End If

            Using oConnection As SqlConnection = GetSQLConnection()
                Using oSQLCOmmand = oConnection.CreateCommand()
                    oSQLCOmmand.CommandText = SQLQuery
                    oSQLCOmmand.CommandTimeout = Timeout
                    Dim oResult As Object = oSQLCOmmand.ExecuteScalar()
                    Return oResult
                End Using
            End Using
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("SQLQuery: " & SQLQuery)
            Return Nothing
        End Try
    End Function

    Public Function GetScalarValue(SQLQuery As String) As Object Implements IDatabase.GetScalarValue
        Return GetScalarValue(SQLQuery, _Timeout)
    End Function

    Public Function GetScalarValue(SQLCommand As SqlCommand, OutputParameter As String, Timeout As Integer) As Object
        Try
            If TestCanConnect() = False Then
                Return Nothing
            End If

            If SQLCommand.CommandText.Contains(" ") Then
                SQLCommand.CommandType = CommandType.Text
            Else
                SQLCommand.CommandType = CommandType.StoredProcedure
            End If

            Using oConnection As SqlConnection = GetSQLConnection()

                SQLCommand.Connection = oConnection
                SQLCommand.Parameters(OutputParameter).Direction = ParameterDirection.Output
                SQLCommand.CommandTimeout = Timeout
                SQLCommand.ExecuteNonQuery()
                oConnection.Close()

                Return SQLCommand.Parameters(OutputParameter).Value
            End Using
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("SQLQuery: " & SQLCommand.CommandText)
            Return Nothing
        End Try
    End Function

    Public Function GetScalarValue(SQLCommand As SqlCommand, OutputParameter As String) As Object
        Return GetScalarValue(SQLCommand, OutputParameter, _Timeout)
    End Function

    ''' <summary>
    ''' Executes the passed sql-statement in asyncmode
    ''' </summary>
    ''' <param name="executeStatement">the sql statement</param>
    ''' <param name="commandtimeout">Optional Timeout</param>
    ''' <remarks></remarks>
    Public Sub NewExecuteNonQueryAsync(executeStatement As String, Optional commandtimeout As Integer = 120)
        If TestCanConnect() = False Then
            Exit Sub
        End If

        Try
            Dim oCallback As New AsyncCallback(AddressOf NewExecuteNonQueryAsync_Callback)

            Using oConnection As SqlConnection = GetSQLConnection()
                Using oSQLCOmmand = oConnection.CreateCommand()
                    oSQLCOmmand.CommandText = executeStatement
                    oSQLCOmmand.CommandTimeout = commandtimeout
                    oSQLCOmmand.BeginExecuteNonQuery(oCallback, oSQLCOmmand)
                End Using
            End Using
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("executeStatement: " & executeStatement)
        End Try
    End Sub

    Private Sub NewExecuteNonQueryAsync_Callback(ByVal result As IAsyncResult)
        Dim command As SqlCommand = CType(result.AsyncState, SqlCommand)
        Dim res = command.EndExecuteNonQuery(result)
        _Logger.Info("Finished executing Async database operation: {0}", command.CommandText)
    End Sub
End Class
