#include "pch.h"
#include "System/Data/SqlClient/SqlConnection.h"
#include "System/Data/SqlClient/SqlCommand.h"
#include "System/Data/Internal/DatabaseEngine.h"
#include "System/Data/Internal/SqliteDatabaseBackend.h"
#include "System/Data/Internal/InMemoryDatabaseBackend.h"
#include "System/InvalidOperationException.h"
#include <algorithm>
#include <string>

namespace DotNetDupe {
    namespace System {
        namespace Data {
            namespace SqlClient {

                struct SqlConnection::Impl {
                    DotNetDupe::System::String m_sConnectionString;
                    std::string m_dbName;
                    std::string m_engineType;
                    bool m_bIsOpen = false;
                };

                SqlConnection::SqlConnection() : m_pImpl(DotNetDupe::System::SmartPointer<Impl>::NewShared()) {
                }

                SqlConnection::SqlConnection(const DotNetDupe::System::String& sConnectionString) 
                    : m_pImpl(DotNetDupe::System::SmartPointer<Impl>::NewShared()) {
                    SetConnectionString(sConnectionString);
                }

                SqlConnection::~SqlConnection() = default;

                DotNetDupe::System::String SqlConnection::GetConnectionString() const {
                    return m_pImpl->m_sConnectionString;
                }

                static std::string ExtractConnectionStringValue(const std::string& raw, const std::string& key, const std::string& defaultVal) {
                    size_t idx = raw.find(key);
                    if (idx == std::string::npos) return defaultVal;
                    idx += key.length();
                    size_t endIdx = raw.find(";", idx);
                    return (endIdx != std::string::npos) ? raw.substr(idx, endIdx - idx) : raw.substr(idx);
                }

                static std::string ExtractDatabaseName(const std::string& raw) {
                    if (raw.find("Database=") != std::string::npos) return ExtractConnectionStringValue(raw, "Database=", "DefaultDb");
                    return ExtractConnectionStringValue(raw, "Data Source=", "DefaultDb");
                }

                void SqlConnection::SetConnectionString(const DotNetDupe::System::String& sConnStr) {
                    m_pImpl->m_sConnectionString = sConnStr;
                    std::string raw = sConnStr.GetRawString();
                    m_pImpl->m_dbName = ExtractDatabaseName(raw);
                    m_pImpl->m_engineType = ExtractConnectionStringValue(raw, "Engine=", "InMemory");
                }

                DotNetDupe::System::String SqlConnection::GetDatabaseName() const {
                    return DotNetDupe::System::String(m_pImpl->m_dbName.c_str());
                }

                bool SqlConnection::IsOpen() const {
                    return m_pImpl->m_bIsOpen;
                }

                void SqlConnection::Open() {
                    m_pImpl->m_bIsOpen = true;

                    std::string engineUpper = m_pImpl->m_engineType;
                    std::transform(engineUpper.begin(), engineUpper.end(), engineUpper.begin(), ::toupper);

                    if (engineUpper == "SQLITE") {
#if defined(DOTNETDUPE_USE_SQLITE)
                        auto sqliteBackend = DotNetDupe::System::SmartPointer<DotNetDupe::System::Data::Internal::SqliteDatabaseBackend>::NewShared(m_pImpl->m_sConnectionString.GetRawString());
                        DotNetDupe::System::Data::Internal::DatabaseEngine::Instance().RegisterBackend(DotNetDupe::System::String(m_pImpl->m_dbName.c_str()), sqliteBackend.DynamicCast<DotNetDupe::System::Data::Internal::IDatabaseBackend>());
#else
                        throw DotNetDupe::System::InvalidOperationException("SQLite database engine is not compiled in this build.");
#endif
                    } else {
                        auto inMemoryBackend = DotNetDupe::System::SmartPointer<DotNetDupe::System::Data::Internal::InMemoryDatabaseBackend>::NewShared();
                        DotNetDupe::System::Data::Internal::DatabaseEngine::Instance().RegisterBackend(DotNetDupe::System::String(m_pImpl->m_dbName.c_str()), inMemoryBackend.DynamicCast<DotNetDupe::System::Data::Internal::IDatabaseBackend>());
                    }
                }

                void SqlConnection::Close() {
                    m_pImpl->m_bIsOpen = false;
                }

                DotNetDupe::System::SmartPointer<DotNetDupe::System::Data::Common::DbCommand> SqlConnection::CreateCommand() {
                    return DotNetDupe::System::SmartPointer<SqlCommand>::NewShared("", this);
                }

            }
        }
    }
}

Generated by OpenCppCoverage (Version: 0.9.9.0)