/*******************************************************************************
* Copyright 2020-2021 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/

#pragma once

#include "oneapi/dal/io/csv/common.hpp"
#include "oneapi/dal/detail/common.hpp"
#include "oneapi/dal/graph/common.hpp"
#include "oneapi/dal/detail/error_messages.hpp"
#include "oneapi/dal/detail/memory.hpp"

namespace oneapi::dal::csv {

namespace detail {
namespace v1 {
template <typename Object>
class read_args_impl;
} // namespace v1

using v1::read_args_impl;

} // namespace detail

namespace v1 {

template <typename Object = table>
class read_args;

template <>
class ONEDAL_EXPORT read_args<table> : public base {
public:
    read_args();

private:
    dal::detail::pimpl<detail::read_args_impl<table>> impl_;
};

} // namespace v1

using v1::read_args;

} // namespace oneapi::dal::csv

namespace oneapi::dal::preview::csv {

namespace detail {

using byte_alloc_ptr = dal::detail::shared<preview::detail::byte_alloc_iface>;

template <typename Allocator>
inline byte_alloc_ptr make_allocator(Allocator&& alloc) {
    return std::make_shared<preview::detail::alloc_connector<std::decay_t<Allocator>>>(
        std::forward<Allocator>(alloc));
}

class read_args_graph_impl : public base {
public:
    explicit read_args_graph_impl(const byte_alloc_ptr& alloc, read_mode mode)
            : allocator(alloc),
              mode(mode) {
        if (mode != read_mode::edge_list && mode != read_mode::weighted_edge_list) {
            throw invalid_argument(dal::detail::error_messages::unsupported_read_mode());
        }
    }

    byte_alloc_ptr get_allocator() const {
        return allocator;
    }
    read_mode get_read_mode() const {
        return mode;
    }
    byte_alloc_ptr allocator;
    read_mode mode;
};
} // namespace detail

struct read_args_tag {};

template <typename Object>
class ONEDAL_EXPORT read_args : public base {
public:
    using object_t = Object;
    using tag_t = read_args_tag;
    explicit read_args(read_mode mode = read_mode::edge_list)
            : impl_(new detail::read_args_graph_impl(detail::make_allocator(std::allocator<int>{}),
                                                     mode)) {}
    template <typename Allocator>
    explicit read_args(Allocator&& allocator, read_mode mode = read_mode::edge_list)
            : impl_(new detail::read_args_graph_impl(
                  detail::make_allocator(std::forward<Allocator>(allocator)),
                  mode)) {}

    detail::byte_alloc_ptr get_allocator() const {
        return impl_->get_allocator();
    }

    auto& set_read_mode(read_mode mode) {
        set_read_mode_impl(mode);
        return *this;
    }
    read_mode get_read_mode() const {
        return impl_->get_read_mode();
    }

protected:
    void set_read_mode_impl(read_mode mode) {
        if (mode != read_mode::edge_list && mode != read_mode::weighted_edge_list)
            throw invalid_argument(dal::detail::error_messages::unsupported_read_mode());
        impl_->mode = mode;
    }

private:
    dal::detail::pimpl<detail::read_args_graph_impl> impl_;
};

} // namespace oneapi::dal::preview::csv
