/* Python interface to the pub/sub code
Copyright (C) 2020 Jonathan Hanks

This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include <cds-pubsub/pub.hh>
#include <cds-pubsub/sub.hh>

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/functional.h>

namespace py = pybind11;

namespace
{

    void
    publish( pub_sub::Publisher& p,
             pub_sub::KeyType    key,
             const py::bytes&    message_data )
    {
        auto        obj_ptr = message_data.ptr( );
        const char* data = PyBytes_AsString( obj_ptr );
        Py_ssize_t  data_size = PyBytes_Size( obj_ptr );

        pub_sub::DataPtr ptr( new unsigned char[ data_size ] );
        std::copy( data, data + data_size, ptr.get( ) );
        pub_sub::Message msg( std::move( ptr ), data_size );
        p.publish( key, std::move( msg ) );
    }

    py::bytes
    SubMessageData( const pub_sub::SubMessage& msg )
    {
        return py::bytes( reinterpret_cast< char* >( msg.data( ) ),
                          msg.size( ) );
    }
} // namespace

PYBIND11_MODULE( pubsub, m )
{

    py::class_< pub_sub::SubMessage >( m, "sub_message" )
        .def_property_readonly( "sub_id", &pub_sub::SubMessage::sub_id )
        .def_property_readonly( "key", &pub_sub::SubMessage::key )
        .def( "data", &SubMessageData );

    py::class_< pub_sub::Publisher >( m, "publisher" )
        .def( py::init< const std::string& >( ) )
        .def( "add_destination", &pub_sub::Publisher::add_destination )
        .def( "publish", &publish );

    py::class_< pub_sub::Subscriber >( m, "subscriber" )
        .def( py::init<>( ) )
        .def( "subscribe", &pub_sub::Subscriber::subscribe );
}