#pragma once
#include "transformer.h"
#include "condition.h"

#include <maps/libs/xml/include/xml.h>
#include <maps/libs/json/include/value.h>
#include <maps/libs/json/include/builder.h>

#include <memory>

namespace maps::wiki::json2ymapsdf::transformers {

const std::string POLYGON_RECORD = "POLYGON";
const std::string LINE_RECORD = "LINE";
const std::string AD_RECOGNITION_RECORD = "AD_RECOGNITION";
const std::string COMMON_RECORD = "COMMON";
const std::string COMMON_RELATION = "COMMON_RELATION";
const std::string SKIP_RECORD = "SKIP";
const std::string RD_EL_LANE_RECORD = "RD_EL_LANE";
const std::string ENTRANCE_LEVEL_FLAT_RANGE = "ENTRANCE_LEVEL_FLAT_RANGE";

class FieldTransformer;

class RecordTransformer {
public:
    RecordTransformer(const ymapsdf::schema::Schema& ymapsdfSchema)
        : ymapsdfSchema_(ymapsdfSchema)
    {}
    virtual ~RecordTransformer() = default;

    virtual ymapsdf::Records transform(const tds::Item&) const = 0;

    virtual ymapsdf::schema::ColumnSet columns() const;
    virtual ymapsdf::schema::TableSet tables() const;

    virtual void configureAutoCopy(const ymapsdf::schema::ColumnSet&) { }
    virtual void configureAutoDefault(const ymapsdf::schema::ColumnSet&) { }

    bool skip() const { return columns().empty(); }

    virtual std::string name() const = 0;
    virtual void serialize(maps::json::ObjectBuilder&);

public:
    static std::shared_ptr<RecordTransformer> create(
        const ymapsdf::schema::Schema& ymapsdfSchema,
        const ymapsdf::schema::Table* table,
        const tds::schema::Category* category,
        const xml3::Node* node = nullptr);

    static std::shared_ptr<RecordTransformer> createRelation(
        const ymapsdf::schema::Schema& ymapsdfSchema,
        const tds::schema::Relation& tRel,
        const ymapsdf::schema::Relation& yRel,
        const xml3::Node* node = nullptr);

    static std::shared_ptr<RecordTransformer> createSkip(const ymapsdf::schema::Schema& ymapsdfSchema);

protected:
    const ymapsdf::schema::Schema& ymapsdfSchema_;
};

class SkipTransformer : public RecordTransformer {
public:
    SkipTransformer(const ymapsdf::schema::Schema& ymapsdfSchema)
        : RecordTransformer{ymapsdfSchema}
    { }
    SkipTransformer(
        const ymapsdf::schema::Schema& ymapsdfSchema,
        const ymapsdf::schema::Table*,
        const tds::schema::Category*,
        const xml3::Node*)
        : RecordTransformer{ymapsdfSchema}
    { }
    virtual ~SkipTransformer() = default;

    ymapsdf::Records transform(const tds::Item&) const override { return {}; }

    std::string name() const override { return SKIP_RECORD; }
};

class CommonTransformer : public RecordTransformer {
public:
    CommonTransformer(
        const ymapsdf::schema::Schema& ymapsdfSchema,
        const ymapsdf::schema::Table* table,
        const xml3::Node* recordNode);
    virtual ~CommonTransformer() = default;

    virtual ymapsdf::Records transform(const tds::Item& obj) const override;

    virtual ymapsdf::schema::ColumnSet columns() const override;

    void configureAutoDefault(const ymapsdf::schema::ColumnSet& configuredFields) override;
    void configureAutoCopy(const ymapsdf::schema::ColumnSet& configuredFields) override;

    void serialize(maps::json::ObjectBuilder&) override;

protected:
    virtual bool hasAttribute(const std::string& attr) const = 0;
    virtual bool hasConstAttribute(const std::string& attr) const = 0;

protected:
    using Conditions = std::vector<std::shared_ptr<Condition>>;
    using FieldTransformers = std::vector<std::shared_ptr<FieldTransformer>>;

protected:
    Conditions orConditions_;
    FieldTransformers fieldTransformers_;
    const ymapsdf::schema::Table* table_;
    const tds::schema::Category* category_;

private:
    bool allowEmpty() const;
    bool allowEmpty_ = false;
};

class CommonRecordTransformer : public CommonTransformer {
public:
    CommonRecordTransformer(
        const ymapsdf::schema::Schema& ymapsdfSchema,
        const ymapsdf::schema::Table* table,
        const tds::schema::Category* category,
        const xml3::Node* recordNode = nullptr);
    virtual ~CommonRecordTransformer() = default;

    std::string name() const override { return COMMON_RECORD; }

private:
    bool hasAttribute(const std::string& attr) const override;
    bool hasConstAttribute(const std::string& attr) const override;
};

class CommonRelationTransformer : public CommonTransformer {
public:
    CommonRelationTransformer(
        const ymapsdf::schema::Schema& ymapsdfSchema,
        const tds::schema::Relation& tRel,
        const ymapsdf::schema::Relation& yRel,
        const xml3::Node* node = nullptr);

    std::string name() const { return COMMON_RELATION; }

private:
    bool hasAttribute(const std::string& attr) const;
    bool hasConstAttribute(const std::string& attr) const;

private:
    const tds::schema::Relation& relation_;
};

} // namespace maps::wiki::json2ymapsdf::transformers
