#pragma once

#include "sign.h"
#include "table_traits.h"

#include <maps/libs/sql_chemistry/include/gateway.h>

namespace maps::mrc::traffic_signs {
inline std::istream& operator>>(std::istream& is, TrafficSign& ts) {
    std::string str;
    is >> str;
    ts = stringToTrafficSign(str);
    return is;
}
} // namespace maps::mrc::traffic_signs

namespace maps::mrc::db {
namespace table {
using namespace sql_chemistry;

struct Sign : Table<db::Sign> {
    static constexpr std::string_view name_{"signs_detect.sign"sv};

    static constexpr BigSerialKey id{"sign_id"sv, name_};
    static constexpr EnumColumn<db::Sign::Type> type{"type"sv, name_};
    static constexpr MercatorColumn<geolib3::Point2> position{"position"sv, name_};
    static constexpr DoubleColumn positionVariance{"position_variance"sv, name_};
    static constexpr DoubleColumn heading{"heading"sv, name_};
    static constexpr DoubleColumn headingVariance{"heading_variance"sv, name_};

    static constexpr auto columns_() {
        return std::tie(id, type, position, positionVariance, heading, headingVariance);
    }
};

} // namespace table

class SignGateway : public sql_chemistry::Gateway<table::Sign> {
public:
    using Tb = table::Sign;
    using Base = Gateway<Tb>;
    using Base::Base;
    using Base::Entities;

    Entities loadByBbox(const geolib3::BoundingBox& mercatorBbox, Sign::Type type) {
        return load(Tb::position.intersects(mercatorBbox) && Tb::type.equals(type));
    }
};

namespace table {
using namespace sql_chemistry;
struct SignFeature : Table<db::SignFeature> {
    static constexpr std::string_view name_{"signs_detect.sign_feature"sv};

    // not null FOREIGN KEY signs_detect.sign
    static constexpr NumericColumn<TId> signId{"sign_id"sv, name_};
    static constexpr NumericColumn<TId> featureId{"feature_id"sv, name_};
    static constexpr NumericColumn<size_t> minX{"min_x"sv, name_};
    static constexpr NumericColumn<size_t> minY{"min_y"sv, name_};
    static constexpr NumericColumn<size_t> maxX{"max_x"sv, name_};
    static constexpr NumericColumn<size_t> maxY{"max_y"sv, name_};

    static constexpr auto columns_() { return std::tie(signId, featureId, minX, minY, maxX, maxY); }
};

struct SignPanorama : Table<db::SignPanorama> {
    static constexpr std::string_view name_{"signs_detect.sign_panorama"sv};

    // not null FOREIGN KEY signs_detect.sign
    static constexpr NumericColumn<TId> signId{"sign_id"sv, name_};
    static constexpr NumericColumn<TId> panoramaId{"panorama_id"sv, name_};
    static constexpr NumericColumn<size_t> minX{"min_x"sv, name_};
    static constexpr NumericColumn<size_t> minY{"min_y"sv, name_};
    static constexpr NumericColumn<size_t> maxX{"max_x"sv, name_};
    static constexpr NumericColumn<size_t> maxY{"max_y"sv, name_};

    static constexpr auto columns_() { return std::tie(signId, panoramaId, minX, minY, maxX, maxY); }
};

} // namespace table

class SignFeatureGateway : public sql_chemistry::Gateway<table::SignFeature> {
public:
    using Tb = table::SignFeature;
    using Base = Gateway<Tb>;
    using Base::Base;
    using Base::Entities;

    void removeByFeatureId(TId featureId) { remove(Tb::featureId == featureId); }

    void removeByFeatureIds(TIds featureIds) { remove(Tb::featureId.in(std::move(featureIds))); }

    Entities loadBySignIds(TIds signIds) { return load(Tb::signId.in(std::move(signIds))); }

    Entities loadBySignId(TId signId) { return load(Tb::signId == signId); }

    TIds loadFeatureIds(const sql_chemistry::Filter& filter);
};

class SignPanoramaGateway : public sql_chemistry::Gateway<table::SignPanorama> {
public:
    using Tb = table::SignPanorama;
    using Base = Gateway<Tb>;
    using Base::Base;
    using Base::Entities;

    void removeByPanoramaId(TId panoramaId) { remove(Tb::panoramaId == panoramaId); }

    void removeByPanoramaIds(TIds panoramaIds) { remove(Tb::panoramaId.in(std::move(panoramaIds))); }

    Entities loadBySignIds(TIds signIds) { return load(Tb::signId.in(std::move(signIds))); }

    Entities loadBySignId(TId signId) { return load(Tb::signId == signId); }

    TIds loadPanoramaIds(const sql_chemistry::Filter& filter);
};

TABLE_TRAITS(Sign, table::Sign);
TABLE_TRAITS(SignFeature, table::SignFeature);
TABLE_TRAITS(SignPanorama, table::SignPanorama);

} // namespace maps::mrc::db
