#include <yandex/maps/wiki/mds_dataset/dataset.h>
#include <yandex/maps/wiki/mds_dataset/export_metadata.h>
#include <yandex/maps/wiki/common/string_utils.h>

namespace maps {
namespace wiki {
namespace mds_dataset {

class FileLink::Impl
{
public:
    Impl(mds::Key mdsKey, std::string name, std::string readingUrl)
        : mdsKey_(mdsKey), name_(name), readingUrl_(readingUrl)
    {}

    std::tuple<const mds::Key&, const std::string&, const std::string&>
    tie() const
    {
        return std::tie(mdsKey_, name_, readingUrl_);
    }

    mds::Key mdsKey_;
    std::string name_;
    std::string readingUrl_;
};

FileLink::FileLink(mds::Key mdsKey, std::string name, std::string readingUrl)
    : impl_(new Impl(
        std::move(mdsKey),
        std::move(name),
        std::move(readingUrl)))
{}

COPYABLE_PIMPL_DEFINITIONS(FileLink)

const mds::Key& FileLink::mdsKey() const { return impl_->mdsKey_; }
const std::string& FileLink::name() const { return impl_->name_; }
const std::string& FileLink::readingUrl() const { return impl_->readingUrl_; }

bool FileLink::operator<(const FileLink& other) const
{
    return impl_->tie() < other.impl_->tie();
}

bool FileLink::operator==(const FileLink& other) const
{
    return impl_->tie() == other.impl_->tie();
}

std::ostream& operator<<(std::ostream& out, const mds::Key& mdsKey)
{
    return out << "{ groupId: " << mdsKey.groupId
               << ", path: " << mdsKey.path
               << " }";
}

std::ostream& operator<<(std::ostream& out, const FileLink& fileLink)
{
    return out << "{ mdsKey: " << fileLink.mdsKey()
               << ", name: " << fileLink.name()
               << ", url: " << fileLink.readingUrl()
               << " }";
}

template <typename MetaData>
class Dataset<MetaData>::Impl
{
public:
    Impl(MetaData metadata, FileLinks fileLinks)
        : metadata_(std::move(metadata))
        , fileLinks_(std::move(fileLinks))
    {}

    const MetaData& metadata() const { return metadata_; }
    const FileLinks& fileLinks() { return fileLinks_; }

    std::tuple<const MetaData&, const FileLinks&> tie() const
    {
        return std::tie(metadata_, fileLinks_);
    }

private:
    MetaData metadata_;
    FileLinks fileLinks_;
};

template <typename MetaData>
Dataset<MetaData>::Dataset(MetaData metadata, FileLinks fileLinks)
    : pimpl_(new Impl(std::move(metadata), std::move(fileLinks)))
{}

template <typename MetaData>
Dataset<MetaData>::~Dataset() = default;

template <typename MetaData>
Dataset<MetaData>::Dataset(Dataset&&) = default;

template <typename MetaData>
Dataset<MetaData>& Dataset<MetaData>::operator=(Dataset&&) = default;

template <typename MetaData>
Dataset<MetaData>::Dataset(const Dataset& other)
    : pimpl_(new Impl(*other.pimpl_))
{
}

template <typename MetaData>
Dataset<MetaData>& Dataset<MetaData>::operator=(const Dataset& other)
{
    if (this != &other) {
        Dataset temp(other);
        pimpl_.swap(temp.pimpl_);
    }
    return *this;
}

template <typename MetaData>
const MetaData& Dataset<MetaData>::metadata() const
{
    return pimpl_->metadata();
}

template <typename MetaData>
const FileLinks& Dataset<MetaData>::fileLinks() const
{
    return pimpl_->fileLinks();
}

template <typename MetaData>
bool Dataset<MetaData>::operator<(const Dataset<MetaData>& other) const
{
    return pimpl_->tie() < other.pimpl_->tie();
}

template <typename MetaData>
bool Dataset<MetaData>::operator==(const Dataset<MetaData>& other) const
{
    return pimpl_->tie() == other.pimpl_->tie();
}

template <typename MetaData>
std::ostream& operator<<(std::ostream& out, const Dataset<MetaData>& dataset)
{
    return out << "{ metadata: " << dataset.metadata()
               << ", fileLinks: [" << common::join(dataset.fileLinks(), ", ")
               << "] }";

}

// Explicit instantiation
template class Dataset<ExportMetadata>;

template std::ostream& operator<< (
        std::ostream& out, const Dataset<ExportMetadata>& dataset);

} // mds_dataset
} // wiki
} // maps

