/* file: engine.h */
/*******************************************************************************
* Copyright 2014-2019 Intel Corporation.
*
* This software and the related documents are Intel copyrighted  materials,  and
* your use of  them is  governed by the  express license  under which  they were
* provided to you (License).  Unless the License provides otherwise, you may not
* use, modify, copy, publish, distribute,  disclose or transmit this software or
* the related documents without Intel's prior written permission.
*
* This software and the related documents  are provided as  is,  with no express
* or implied  warranties,  other  than those  that are  expressly stated  in the
* License.
*******************************************************************************/

#ifndef __DATA_SOURCE_MODIFIERS_SQL_INTERNAL_ENGINE_H__
#define __DATA_SOURCE_MODIFIERS_SQL_INTERNAL_ENGINE_H__

#include "services/internal/error_handling_helpers.h"
#include "data_management/features/internal/helpers.h"
#include "data_management/data_source/internal/sql_feature_utils.h"
#include "data_management/data_source/modifiers/internal/engine.h"
#include "data_management/data_source/modifiers/sql/modifier.h"

namespace daal
{
namespace data_management
{
namespace modifiers
{
namespace sql
{
namespace internal
{

using data_management::internal::SQLFeatureInfo;
using data_management::internal::SQLFeaturesInfo;
using data_management::internal::SQLFetchBuffer;
using data_management::internal::SQLFetchBufferFragment;

/**
 * <a name="DAAL-CLASS-DATA_MANAGEMENT__MODIFIERS__SQL__INTERNAL__FEATURECONFIG"></a>
 * \brief Class represents configuration of single input feature
 */
class InputFeatureInfo : public modifiers::internal::InputFeatureInfo
{
public:
    InputFeatureInfo() { }

    explicit InputFeatureInfo(const SQLFeatureInfo &featureInfo,
                              const SQLFetchBufferFragment &fetchBuffer) :
        _featureInfo(featureInfo),
        _fetchBuffer(fetchBuffer) { }

    const SQLFeatureInfo &getFeatureInfo() const
    {
        return _featureInfo;
    }

    const SQLFetchBufferFragment &getFetchBuffer() const
    {
        return _fetchBuffer;
    }

private:
    SQLFeatureInfo _featureInfo;
    SQLFetchBufferFragment _fetchBuffer;
};

/**
 * <a name="DAAL-CLASS-DATA_MANAGEMENT__MODIFIERS__SQL__INTERNAL__FEATURECONFIG"></a>
 * \brief Class represents configuration of single feature
 */
class OutputFeatureInfo : public modifiers::internal::OutputFeatureInfo { };

/**
 * <a name="DAAL-CLASS-DATA_MANAGEMENT__MODIFIERS__SQL__INTERNAL__CONFIGIMPL"></a>
 * \brief Internal implementation of feature modifier configuration
 */
class ConfigImpl : public Config,
                   public modifiers::internal::Config<InputFeatureInfo,
                                                      OutputFeatureInfo>
{
private:
    typedef modifiers::internal::Config<InputFeatureInfo,
                                        OutputFeatureInfo> impl;

public:
    ConfigImpl() { }

    explicit ConfigImpl(const services::internal::CollectionPtr<InputFeatureInfo *> &pickedInputFeatures,
                        services::Status *status = NULL) :
        impl(pickedInputFeatures, status) { }

    virtual size_t getNumberOfInputFeatures() const DAAL_C11_OVERRIDE
    {
        return impl::getNumberOfInputFeatures();
    }

    virtual services::Status setNumberOfOutputFeatures(size_t numberOfOutputFeatures) DAAL_C11_OVERRIDE
    {
        return impl::setNumberOfOutputFeatures(numberOfOutputFeatures);
    }

    virtual services::Status setOutputFeatureType(size_t outputFeatureIndex,
                                                  features::FeatureType featureType) DAAL_C11_OVERRIDE
    {
        return impl::setOutputFeatureType(outputFeatureIndex, featureType);
    }

    virtual services::Status setNumberOfCategories(size_t outputFeatureIndex,
                                                   size_t numberOfCategories) DAAL_C11_OVERRIDE
    {
        return impl::setNumberOfCategories(outputFeatureIndex, numberOfCategories);
    }

    virtual services::Status setCategoricalDictionary(size_t outputFeatureIndex,
                                                      const CategoricalFeatureDictionaryPtr &dictionary) DAAL_C11_OVERRIDE
    {
        return impl::setCategoricalDictionary(outputFeatureIndex, dictionary);
    }
};

/**
 * <a name="DAAL-CLASS-DATA_MANAGEMENT__MODIFIERS__SQL__INTERNAL__CONTEXTIMPL"></a>
 * \brief Internal implementation of feature modifier context
 */
class ContextImpl : public Context,
                    public modifiers::internal::Context<InputFeatureInfo,
                                                        OutputFeatureInfo>
{
private:
    typedef modifiers::internal::Context<InputFeatureInfo,
                                         OutputFeatureInfo> impl;

public:
    ContextImpl() { }

    explicit ContextImpl(const services::internal::CollectionPtr<InputFeatureInfo *> &pickedInputFeatures,
                         services::Status *status = NULL) :
        impl(pickedInputFeatures, status) { }

    virtual size_t getNumberOfColumns() const DAAL_C11_OVERRIDE
    {
        return impl::getNumberOfInputFeatures();
    }

    virtual services::BufferView<DAAL_DATA_TYPE> getOutputBuffer() const DAAL_C11_OVERRIDE
    {
        return impl::getOutputBuffer();
    }

    virtual services::BufferView<char> getRawValue(size_t columnIndex) const DAAL_C11_OVERRIDE
    {
        const InputFeatureInfo &fi = impl::getPickedInputFeature(columnIndex);
        return fi.getFetchBuffer().view();
    }
};

/**
 * <a name="DAAL-CLASS-DATA_MANAGEMENT__MODIFIERS__SQL__INTERNAL__MODIFIERSMANAGER"></a>
 * \brief Class that holds modifiers and implements logic of modifiers applying flow
 */
class ModifiersManager : public modifiers::internal::ModifiersManager<FeatureModifierIface,
                                                                      ConfigImpl, ContextImpl>
{
public:
    static services::SharedPtr<ModifiersManager> create(services::Status *status = NULL)
    {
        return services::internal::wrapSharedAndTryThrow(new ModifiersManager(), status);
    }

    services::Status prepare(const SQLFeaturesInfo &featuresInfo,
                             const SQLFetchBuffer &fetchBuffer)
    {
        services::Status status;

        const features::FeatureIdMappingIfacePtr mapping = createMapping(featuresInfo, &status);
        DAAL_CHECK_STATUS_VAR(status);

        const services::internal::CollectionPtr<InputFeatureInfo> inputFeaturesInfo =
            createInputFeaturesInfo(featuresInfo, fetchBuffer, &status);
        DAAL_CHECK_STATUS_VAR(status);

        status |= getBinder().bind(mapping, inputFeaturesInfo);

        return status;
    }

private:
    ModifiersManager() { }

    services::internal::CollectionPtr<InputFeatureInfo> createInputFeaturesInfo(const SQLFeaturesInfo &featuresInfo,
                                                                                const SQLFetchBuffer &fetchBuffer,
                                                                                services::Status *status = NULL)
    {
        const size_t numberOfFeatures = featuresInfo.getNumberOfFeatures();

        services::internal::CollectionPtr<InputFeatureInfo> inputFeaturesInfo =
            services::internal::HeapAllocatableCollection<InputFeatureInfo>::create(numberOfFeatures);
        if (!inputFeaturesInfo)
        {
            services::internal::tryAssignStatusAndThrow(status, services::ErrorMemoryAllocationFailed);
            return inputFeaturesInfo;
        }

        for (size_t i = 0; i < numberOfFeatures; i++)
        {
            const SQLFeatureInfo &fi = featuresInfo[i];
            const SQLFetchBufferFragment &ff = fetchBuffer.getFragment(i);
            (*inputFeaturesInfo)[i] = InputFeatureInfo(fi, ff);
        }

        return inputFeaturesInfo;
    }

    features::FeatureIdMappingIfacePtr createMapping(const SQLFeaturesInfo &featuresInfo,
                                                     services::Status *status = NULL)
    {
        const size_t numberOfFeatures = featuresInfo.getNumberOfFeatures();

        services::Status localStatus;
        const features::internal::FeatureIdDefaultMappingPtr mapping =
            features::internal::FeatureIdDefaultMapping::create(numberOfFeatures, &localStatus);
        if (!localStatus)
        {
            services::internal::tryAssignStatusAndThrow(status, localStatus);
            return mapping;
        }

        for (size_t i = 0; i < numberOfFeatures; ++i)
        {
            mapping->setFeatureKey(i, featuresInfo[i].columnName);
        }

        return mapping;
    }
};
typedef services::SharedPtr<ModifiersManager> ModifiersManagerPtr;

} // namespace internal
} // namespace sql
} // namespace modifiers
} // namespace data_management
} // namespace daal

#endif
