#include "../common.h"
#include "../categories.h"
#include "../magic_strings.h"
#include "../attribute_dumper.h"
#include "../relation_dumper.h"
#include "../ft_type.h"
#include "../ft_helper.h"

namespace maps {

namespace {
const std::string EDGE_ID = "edge_id";
const std::string F_ZLEV = "f_zlev";
const std::string T_ZLEV = "t_zlev";
const std::string F_NODE_ID = "f_node_id";
const std::string T_NODE_ID = "t_node_id";
const std::string FT_TYPE_ID = "ft_type_id";
const std::string FACE_IDS = "face_ids";
const std::string FT_COUNT = "ft_count";
const std::string FT_IDS = "ft_ids";
} // namespace

struct FtElCategory: public Category {
    virtual std::string nodeName() const = 0;

    virtual void dumpRelations(
        json::ArrayBuilder& builder,
        const pqxx::row& tuple,
        const FtData& ft) const = 0;

    void tupleToJson(
        json::ObjectBuilder& builder,
        const pqxx::row& tuple) const override
    {
        FtData ft(tuple);
        const std::string category = ft.category(name());

        builder[jkey::ATTRIBUTES] = [&](json::ObjectBuilder builder) {
            AttributeDumper ad(category, builder);
            ad.dumpCategory();
            ad.dump<int>(F_ZLEV, tuple);
            ad.dump<int>(T_ZLEV, tuple);
        };
        dumpGeometry(builder, tuple);
        builder[jkey::RELATIONS] = [&](json::ArrayBuilder builder) {
            dumpRelations(builder, tuple, ft);
        };
    }
};

struct FtLnElCategory: public FtElCategory {
    std::string name() const override
    {
        return category::FT_LN_EL;
    }

    const Table& table() const override
    {
        return table::FT_EDGE;
    }

    std::string nodeName() const override
    {
        return category::FT_LN_JC;
    }

    std::string loadRowsSqlTemplate() const override
    {
        return
            "WITH a AS (SELECT edge_id, MAX(ft_id) AS ft_id FROM ft_edge GROUP BY edge_id) "
            "SELECT edge.edge_id id, "
                "f_zlev, t_zlev, f_node_id, t_node_id, "
                "st_asgeojson(shape) shape, "
                "(SELECT array_agg(ft_edge.ft_id) "
                    "FROM ft_edge "
                    "WHERE ft_edge.edge_id=edge.edge_id "
                ") " + FT_IDS + ", " +
                ftElSql("a.ft_id", "edge.edge_id") + " "
            "FROM edge JOIN a USING (edge_id) "
            "WHERE edge.edge_id in %1% ";
    }

    void dumpRelations(
        json::ArrayBuilder& builder,
        const pqxx::row& tuple,
        const FtData& ft) const override
    {
        RelationDumper rd(builder, tuple);
        rd.dump(ft.relation(relation::FT_LN_EL_START), F_NODE_ID);
        rd.dump(ft.relation(relation::FT_LN_EL_END), T_NODE_ID);

        if (!ft.isTrivial()) {
            rd.dump(ft.relation(relation::FT_LN_PART), FT_IDS);
        }
    }

};

struct FtFcElCategory: public FtElCategory {
    std::string name() const override { return category::FT_FC_EL; }
    std::string nodeName() const override { return category::FT_FC_JC; }

    const Table& table() const override
    {
        return table::EDGE;
    }


    std::string loadIdsSql() const override
    {
        return
            "SELECT DISTINCT face_edge.edge_id FROM ft, ft_face, face_edge "
            "WHERE face_edge.face_id = ft_face.face_id "
            "AND ft_face.ft_id = ft.ft_id "
            "AND ft.ft_type_id NOT IN (" + polygonFtTypes() + ")";
    }

    std::string loadRowsSqlTemplate() const override
    {
        return
            "SELECT edge.edge_id id, "
                "f_zlev, t_zlev, f_node_id, t_node_id, "
                "st_asgeojson(shape) shape, "
                "(SELECT array_agg(face_edge.face_id) "
                    "FROM face_edge, ft_face, ft "
                    "WHERE face_edge.edge_id=edge.edge_id "
                    "AND ft_face.face_id = face_edge.face_id "
                    "AND ft.ft_id = ft_face.ft_id "
                    "AND ft_type_id NOT IN (" + polygonFtTypes() + ")"
                ") " + FACE_IDS + ", " +
                ftFcElSql("edge.edge_id") + " "
            "FROM edge "
            "WHERE edge.edge_id in %1%";
    }

    void dumpRelations(
        json::ArrayBuilder& builder,
        const pqxx::row& tuple,
        const FtData& ft) const override
    {
        RelationDumper rd(builder, tuple);
        rd.dump(ft.relation(relation::FT_FC_EL_START), F_NODE_ID);
        rd.dump(ft.relation(relation::FT_FC_EL_END), T_NODE_ID);
        rd.dump(ft.relation(relation::FT_FC_PART), FACE_IDS);
    }

};

DEFINE_CATEGORY_OBJECT(FtLnEl, FT_LN_EL);
DEFINE_CATEGORY_OBJECT(FtFcEl, FT_FC_EL);

} // namespace maps
