#include <yandex/maps/wiki/revision/filters.h>
#include <yandex/maps/wiki/common/pyutils.hpp>

#include <boost/python.hpp>
#include <string>

namespace bp = boost::python;

namespace maps {
namespace wiki {
namespace revision {
namespace python {

namespace {

filters::ProxyFilterExpr createBinaryFilter(
    filters::BinaryFilterExpr::Operation op,
    const filters::ProxyFilterExpr& leftExpr,
    const filters::ProxyFilterExpr& rightExpr)
{ return filters::BinaryFilterExpr(op, leftExpr.filter(), rightExpr.filter()); }


filters::ProxyFilterExpr createNegativeFilter(
    const filters::ProxyFilterExpr& expr)
{ return filters::NegativeFilterExpr(expr.filter()); }


filters::ProxyFilterExpr attrInFilter(
        const filters::Attr& attr,
        const bp::object& values)
{ return attr.in(common::toVector<std::string>(values)); }

filters::ProxyFilterExpr attrDefinedAllFilter(const bp::object& names)
{ return filters::Attr::definedAll(common::toVector<std::string>(names)); }

filters::ProxyFilterExpr attrDefinedAnyFilter(const bp::object& names)
{ return filters::Attr::definedAny(common::toVector<std::string>(names)); }


filters::ProxyFilterExpr metaAttrInFilter(
        const filters::MetaAttr& metaAttr,
        const bp::object& values)
{ return metaAttr.in(common::toVector<DBID>(values)); }

} // namespace

BOOST_PYTHON_MODULE(revision_filters)
{
    using namespace bp;

    class_<filters::ProxyFilterExpr>("FilterExpr", no_init);

    class_<filters::True>("TrueFilter");
    implicitly_convertible<filters::True, filters::ProxyFilterExpr>();

    class_<filters::False>("FalseFilter");
    implicitly_convertible<filters::False, filters::ProxyFilterExpr>();


    enum_<filters::BinaryFilterExpr::Operation>("BinaryOperation")
        .value("AND", filters::BinaryFilterExpr::Operation::And)
        .value("OR", filters::BinaryFilterExpr::Operation::Or);

    def("create_binary_filter", &createBinaryFilter,
        (arg("op"), "expr1", "expr2"));

    def("create_negative_filter", &createNegativeFilter, arg("expr"));

    class_<filters::AttrFilterExpr>("AttrFilterExpr", no_init);
    implicitly_convertible<filters::AttrFilterExpr, filters::ProxyFilterExpr>();

    filters::AttrFilterExpr (filters::Attr::*equalsString)(const std::string&) const = &filters::Attr::equals;
    filters::BigintAttrFilterExpr (filters::Attr::*equalsInt64)(int64_t) const = &filters::Attr::equals;

    class_<filters::Attr>("Attr", init<const std::string&>(arg("name")))
        .def("defined", &filters::Attr::defined)
        .def("__eq__", equalsString, arg("value"))
        .def("__eq__", equalsInt64, arg("value"))
        .def("in_", &attrInFilter, arg("values"))
        .def("like", &filters::Attr::like, arg("value"))
        .def("defined_all", &attrDefinedAllFilter, arg("names"))
            .staticmethod("defined_all")
        .def("defined_any", &attrDefinedAnyFilter, arg("names"))
            .staticmethod("defined_any");


    class_<filters::GeomFilterExpr>("GeomFilterExpr", no_init);
    implicitly_convertible<filters::GeomFilterExpr, filters::ProxyFilterExpr>();

    filters::GeomFilterExpr(*intersects)(double, double, double, double) = &filters::Geom::intersects;

    class_<filters::Geom>("Geom")
        .def("defined", &filters::Geom::defined)
            .staticmethod("defined")
        .def("intersects", intersects, (arg("x1"), "y1", "x2", "y2"))
            .staticmethod("intersects");


    class_<filters::TableAttrFilterExpr>("TableAttrFilterExpr", no_init);
    implicitly_convertible<
        filters::TableAttrFilterExpr,
        filters::ProxyFilterExpr>();

    class_<filters::MetaAttr>("MetaAttr", no_init)
        .def("is_zero", &filters::MetaAttr::isZero)
        .def("is_not_zero", &filters::MetaAttr::isNotZero)
        .def("in_", &metaAttrInFilter, arg("values"))
        .def("is_null", &filters::MetaAttr::isNull)
        .def("is_not_null", &filters::MetaAttr::isNotNull)
        .def("is_true", &filters::MetaAttr::isTrue)
        .def("is_false", &filters::MetaAttr::isFalse)
        .def("__eq__", &filters::MetaAttr::operator==, arg("value"))
        .def("__ne__", &filters::MetaAttr::operator!=, arg("value"))
        .def("__lt__", &filters::MetaAttr::operator<, arg("value"))
        .def("__le__", &filters::MetaAttr::operator<=, arg("value"))
        .def("__gt__", &filters::MetaAttr::operator>, arg("value"))
        .def("__ge__", &filters::MetaAttr::operator>=, arg("value"));


    class_<filters::TableDateTimeFilterExpr>("TableDateTimeFilterExpr", no_init);
    implicitly_convertible<
        filters::TableDateTimeFilterExpr,
        filters::ProxyFilterExpr>();

    class_<filters::MetaDateTime>("MetaDateTime", no_init)
        .def("__eq__", &filters::MetaDateTime::operator==, arg("value"))
        .def("__ne__", &filters::MetaDateTime::operator!=, arg("value"))
        .def("__lt__", &filters::MetaDateTime::operator<, arg("value"))
        .def("__le__", &filters::MetaDateTime::operator<=, arg("value"))
        .def("__gt__", &filters::MetaDateTime::operator>, arg("value"))
        .def("__ge__", &filters::MetaDateTime::operator>=, arg("value"));


    class_<filters::ObjRevAttr, bases<filters::MetaAttr>>(
            "ObjRevAttr", init<const std::string&>("name"))
        .def("object_id", &filters::ObjRevAttr::objectId)
            .staticmethod("object_id")
        .def("commit_id", &filters::ObjRevAttr::commitId)
            .staticmethod("commit_id")
        .def("prev_commit_id", &filters::ObjRevAttr::prevCommitId)
            .staticmethod("prev_commit_id")
        .def("master_object_id", &filters::ObjRevAttr::masterObjectId)
            .staticmethod("master_object_id")
        .def("slave_object_id", &filters::ObjRevAttr::slaveObjectId)
            .staticmethod("slave_object_id")
        .def("is_deleted", &filters::ObjRevAttr::isDeleted)
            .staticmethod("is_deleted")
        .def("is_not_deleted", &filters::ObjRevAttr::isNotDeleted)
            .staticmethod("is_not_deleted")
        .def("is_regular_object", &filters::ObjRevAttr::isRegularObject)
            .staticmethod("is_regular_object")
        .def("is_not_regular_object", &filters::ObjRevAttr::isNotRegularObject)
            .staticmethod("is_not_regular_object")
        .def("is_relation", &filters::ObjRevAttr::isRelation)
            .staticmethod("is_relation")
        .def("is_not_relation", &filters::ObjRevAttr::isNotRelation)
            .staticmethod("is_not_relation");


    class_<filters::CommitAttr, bases<filters::MetaAttr>>(
            "CommitAttr", init<const std::string&>("name"))
        .def("id_", &filters::CommitAttr::id)
            .staticmethod("id_")
        .def("stable_branch_id", &filters::CommitAttr::stableBranchId)
            .staticmethod("stable_branch_id")
        .def("is_draft", &filters::CommitAttr::isDraft)
            .staticmethod("is_draft")
        .def("is_approved", &filters::CommitAttr::isApproved)
            .staticmethod("is_approved");


    class_<filters::CommitCreationTime, bases<filters::MetaDateTime>>(
            "CommitCreationTime");
}

} // namespace python
} // namespace revision
} // namespace wiki
} // namespace maps
