#include <glibmm/main.h>
#include <gstreamermm.h>
#include <gstreamermm/private/audiofilter_p.h>

class MyAudioFilter : public Gst::AudioFilter
{
public:
    static void class_init(Gst::ElementClass<MyAudioFilter> *klass)
    {
        klass->set_metadata("longname", "classification", "description", "author");

        klass->add_pad_template(Gst::PadTemplate::create("sink", Gst::PAD_SINK, Gst::PAD_ALWAYS, Gst::Caps::create_any()));
        klass->add_pad_template(Gst::PadTemplate::create("src", Gst::PAD_SRC, Gst::PAD_ALWAYS, Gst::Caps::create_any()));
    }

    explicit MyAudioFilter(GstAudioFilter *gobj)
        : Glib::ObjectBase(typeid (MyAudioFilter)),
          Gst::AudioFilter(gobj)
    {
        set_in_place(true);
    }

    static bool register_element(Glib::RefPtr<Gst::Plugin> plugin)
    {
        return Gst::ElementFactory::register_element(plugin, "myaudiofilter", 10, Gst::register_mm_type<MyAudioFilter>("myaudiofilter"));
    }

    virtual bool get_unit_size_vfunc(const Glib::RefPtr<Gst::Caps>& caps, gsize& size) const override
    {
        Gst::AudioInfo info;
        info.from_caps(caps);
        size = info.get_bpf();

        return true;
    }

    virtual bool setup_vfunc(const Gst::AudioInfo& info) override
    {
        return true;
    }

    virtual Gst::FlowReturn transform_vfunc(const Glib::RefPtr<Gst::Buffer>& inbuf, const Glib::RefPtr<Gst::Buffer>& outbuf) override
    {
        Gst::MapInfo inMap;
        Gst::MapInfo outMap;

        inbuf->map(inMap, Gst::MAP_READ);
        outbuf->map(outMap, Gst::MAP_WRITE);

        auto inData = inMap.get_data();
        auto outData = outMap.get_data();

        for (gsize i = 0; i < inMap.get_size(); ++i) {
            outData[i] = inData[i];
        }

        inbuf->unmap(inMap);
        outbuf->unmap(outMap);

        return Gst::FlowReturn::FLOW_OK;
    }

    virtual Gst::FlowReturn transform_ip_vfunc(const Glib::RefPtr<Gst::Buffer>& buf) override
    {
        Gst::MapInfo map;
        buf->map(map, Gst::MAP_READ | Gst::MAP_WRITE);
        buf->unmap(map);

        return Gst::FlowReturn::FLOW_OK;
    }
};


int main(int argc, char *argv[])
{
    Gst::init();
    Gst::Plugin::register_static(GST_VERSION_MAJOR, GST_VERSION_MINOR,
                                 "myaudiofilter", "blah",
                                 sigc::ptr_fun(&MyAudioFilter::register_element),
                                 "0.1.0", "LGPL", "", "", "");

    auto pipeline = Gst::Pipeline::create();
    auto source = Gst::AudioTestSrc::create();
    auto sink = Gst::ElementFactory::create_element("autoaudiosink");
    auto filter = Gst::ElementFactory::create_element("myaudiofilter");

    pipeline->add(source)->add(sink)->add(filter);
    source->link(filter)->link(sink);
    pipeline->set_state(Gst::STATE_PLAYING);

    auto mainloop = Glib::MainLoop::create();
    mainloop->run();

    return 0;
}
