Imports DigitalData.Modules.Encryption
Imports DigitalData.Modules.Logging
Imports Oracle.ManagedDataAccess.Client

Public Class Oracle
    Implements IDatabase

    Public Property DBInitialized As Boolean = False Implements IDatabase.DBInitialized
    Public Property CurrentConnectionString As String = "" Implements IDatabase.CurrentConnectionString

    Private ReadOnly _Timeout As Integer
    Private ReadOnly _Logger As Logger

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

        ConnectionString = ConnectionString
        DBInitialized = TestCanConnect(ConnectionString)
    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)
        _Timeout = Timeout
        _Logger = LogConfig.GetLogger()

        CurrentConnectionString = GetConnectionString(Server, Database, UserId, Password)
        DBInitialized = TestCanConnect(CurrentConnectionString)
    End Sub

    Private Function TestCanConnect(ConnectionString As String) As Boolean
        Try
            Dim oSQLconnect As New OracleConnection
            oSQLconnect.ConnectionString = ConnectionString
            oSQLconnect.Open()
            oSQLconnect.Close()
            Return True
        Catch ex As Exception
            _Logger.Error(ex)
            Return False
        End Try
    End Function

    Public Shared Function GetConnectionString(Server As String, Database As String, UserId As String, Password As String) As String
        Dim oConnectionString = $"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={Server})(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME={Database})));User Id={UserId};Password={Password};"
        Return oConnectionString
    End Function

    ''' <summary>
    ''' Encrypts a connection string password.
    ''' </summary>
    ''' <param name="ConnectionString">A connection string with a plain-text password</param>
    ''' <returns>The connection string with the password encrypted.</returns>
    <DebuggerStepThrough()>
    Public Shared Function EncryptConnectionString(ConnectionString As String) As String
        Dim oEncryption As New EncryptionLegacy()
        Dim oBuilder As New OracleConnectionStringBuilder() With {.ConnectionString = ConnectionString}
        Dim oEncryptedPassword = oEncryption.EncryptData(oBuilder.Password)
        oBuilder.Password = oEncryptedPassword

        Return oBuilder.ToString()
    End Function

    ''' <summary>
    ''' Decrypts a connection string password.
    ''' </summary>
    ''' <param name="ConnectionString">A connection string with a encrypted password</param>
    ''' <returns>The connection string with the password decrypted.</returns>
    <DebuggerStepThrough()>
    Public Shared Function DecryptConnectionString(ConnectionString As String) As String
        Dim oEncryption As New EncryptionLegacy()
        Dim oBuilder As New OracleConnectionStringBuilder() With {.ConnectionString = ConnectionString}
        Dim oDecryptedPassword = oEncryption.DecryptData(oBuilder.Password)
        oBuilder.Password = oDecryptedPassword

        Return oBuilder.ToString()
    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>
    Public Function NewExecutenonQuery(executeStatement As String) As Boolean
        Try
            Dim oSQLconnect As New OracleConnection
            Dim oSQLCOmmand As OracleCommand
            oSQLconnect.ConnectionString = CurrentConnectionString
            oSQLconnect.Open()
            oSQLCOmmand = oSQLconnect.CreateCommand()
            oSQLCOmmand.CommandText = executeStatement
            oSQLCOmmand.CommandTimeout = _Timeout
            oSQLCOmmand.ExecuteNonQuery()
            oSQLCOmmand.Dispose()
            oSQLconnect.Close()
            Return True
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Debug("executeStatement: " & executeStatement)
            Return False
        End Try
    End Function

    ''' <summary>
    ''' Executes the passed sql-statement as Scalar
    ''' </summary>
    ''' <param name="executeStatement">the sql statement</param>
    ''' <returns>Returns the scalarvalue</returns> 
    Public Function NewExecuteScalar(executeStatement As String)
        Dim result
        Try
            Dim oSQLconnect As New OracleConnection
            Dim oSQLCOmmand As OracleCommand
            oSQLconnect.ConnectionString = CurrentConnectionString
            oSQLconnect.Open()
            oSQLCOmmand = oSQLconnect.CreateCommand()
            oSQLCOmmand.CommandText = executeStatement
            oSQLCOmmand.CommandTimeout = _Timeout
            result = oSQLCOmmand.ExecuteScalar()
            oSQLCOmmand.Dispose()
            oSQLconnect.Close()
            Return result
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Debug("executeStatement: " & executeStatement)
            Throw ex
        End Try
    End Function

    Public Function GetDatatable(pSQLCommand As String, Optional pTimeout As Integer = Constants.DEFAULT_TIMEOUT) As DataTable Implements IDatabase.GetDatatable
        Try
            Using oConnection = GetConnection(CurrentConnectionString)
                Dim oSQLCommand As OracleCommand
                oSQLCommand = oConnection.CreateCommand()
                oSQLCommand.CommandText = pSQLCommand
                oSQLCommand.CommandTimeout = pTimeout

                Dim oAdapter As OracleDataAdapter = New OracleDataAdapter(oSQLCommand)
                Dim oTable As DataTable = New DataTable()

                _Logger.Debug("GetDatatable: Running Query [{0}]", oSQLCommand)

                oAdapter.Fill(oTable)

                Return oTable
            End Using
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("GetDatatable: Error in GetDatatable while executing command: [{0}]", pSQLCommand)
            Return Nothing
        End Try
    End Function

    Public Function GetDatatable(SqlCommand As System.Data.SqlClient.SqlCommand, Optional Timeout As Integer = 120) As DataTable Implements IDatabase.GetDatatable
        Throw New NotImplementedException()
    End Function

    Public Function ExecuteNonQuery(pSQLCommand As String, pTimeout As Integer) As Boolean Implements IDatabase.ExecuteNonQuery
        Try
            Using oConnection = GetConnection(CurrentConnectionString)
                Dim oSQLCOmmand As OracleCommand
                oSQLCOmmand = oConnection.CreateCommand()
                oSQLCOmmand.CommandText = pSQLCommand
                oSQLCOmmand.CommandTimeout = pTimeout


                _Logger.Debug("ExecuteNonQuery: Running Query [{0}]", oSQLCOmmand)
                oSQLCOmmand.ExecuteNonQuery()
                oSQLCOmmand.Dispose()
                Return True
            End Using
        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("ExecuteNonQuery: Error in ExecuteNonQuery while executing command: [{0}]", pSQLCommand)
            Return False
        End Try
    End Function

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

    Public Function GetScalarValue(pSQLCommand As String, pTimeout As Integer) As Object Implements IDatabase.GetScalarValue
        Dim result
        Try
            Using oConnection = GetConnection(CurrentConnectionString)
                Dim oSQLCOmmand As OracleCommand
                oSQLCOmmand = oConnection.CreateCommand()
                oSQLCOmmand.CommandText = pSQLCommand
                oSQLCOmmand.CommandTimeout = pTimeout

                _Logger.Debug("GetScalarValue: Running Query [{0}]", oSQLCOmmand)
                result = oSQLCOmmand.ExecuteScalar()

                Return result
            End Using

        Catch ex As Exception
            _Logger.Error(ex)
            _Logger.Warn("GetScalarValue: Error in GetScalarValue while executing command: [{0}]", pSQLCommand)
            Throw ex
        End Try
    End Function

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

    Private Function GetConnection(ConnectionString As String) As OracleConnection
        Try
            Dim oConnection As New OracleConnection(ConnectionString)
            oConnection = OpenSQLConnection(oConnection)

            Dim oMaskedConnectionString = MaskConnectionString(ConnectionString)
            _Logger.Debug("The Following Connection is open: {0}", oMaskedConnectionString)

            Return oConnection
        Catch ex As Exception
            _Logger.Error(ex)

            Return Nothing
        End Try
    End Function

    ''' <summary>
    ''' This Function intentionally has no try..catch block to have any errors caught outside
    ''' </summary>
    ''' <param name="Connection"></param>
    ''' <returns></returns>
    Private Function OpenSQLConnection(Connection As OracleConnection) As OracleConnection
        If Connection.State = ConnectionState.Closed Then
            Connection.Open()
        End If

        Return Connection
    End Function

    <DebuggerStepThrough()>
    Private Function MaskConnectionString(ConnectionString As String) As String
        Try
            If ConnectionString Is Nothing OrElse ConnectionString.Length = 0 Then
                Throw New ArgumentNullException("ConnectionString")
            End If

            Dim oBuilder As New OracleConnectionStringBuilder() With {.ConnectionString = ConnectionString}
            Dim oConnectionString = ConnectionString.Replace(oBuilder.Password, "XXXXX")
            Return oConnectionString
        Catch ex As Exception
            _Logger.Error(ex)
            Return "Invalid ConnectionString"
        End Try
    End Function

    Public Function GetDatatableWithoutTransaction(SqlCommand As String, Optional Timeout As Integer = 120) As DataTable Implements IDatabase.GetDatatableWithoutTransaction
        Throw New NotImplementedException()
    End Function

    Public Function GetDatatableWithoutTransaction(SqlCommand As SqlClient.SqlCommand, Optional Timeout As Integer = 120) As DataTable Implements IDatabase.GetDatatableWithoutTransaction
        Throw New NotImplementedException()
    End Function
End Class
