
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work 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.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//
//

#ifndef SEND_EVENTS_HH
#define SEND_EVENTS_HH

#include "List.hh"


//
// This implements a paradigm where a class can be transmitting events to
// multiple sources, and each receiver can receive events of the same type
// from multiple sources through functions dedicated to each source
// (or they can optionally be congregated into a single function.)
//

// Example:  A class S derived from sending_class is providing events
// of type E.  It's a helper function to do things with receiving
// class R that has special receiver data D.
// 

//
// Sending class:
//
//     class S : public sending_class { ... };
//
// Receiving function:
//
//     bool receive_function ( S*  sender,
//     			       E   event_data,
//    			       R*  receiver_class,
//    			       D   receiver_data,
//                             ER& event_return);
//
//     The return value is true if the event was handled and should be
//     sent to no further listeners.
//
// Typically members of class R:
//
//     D receiver_data;
//
//     connection<S, E, R, D, ER>* connector;
//
// Initialization of connector:
//
//     R* receive_class = new R(...);
//
//     connector = new connection<S, E, R, D, ER> ( receive_class, receiver_data, receive_function );
//
//     S sender;
//
//     sender.add_receiver(connector);
//
//          ...
//
//     sender.release_receiver(connector);
//
// For sender to send an event from a member function, using the function send_event*(
// inherited from sending_class:
//
//     E  event_data;
//     ER event_return;
//
//     bool handled = send_event(this, event_data, event_return);
//
// event_return is filled from the receiver that said it handled the event, or the
// return struct of the last receiver to process the event if none said they
// handled it.
//
// For events that are meant to be scattered rather than handled by only one handler,
// no event will have handled the event, and all will have possibly valid returns.
//
// These can be analyzed via sending_class::get_num_receivers() and the call
//
//     sending_class::get_return_struct(receiver_num, event_return);
//


template<class event_data_struct, class event_return_struct> class sending_base_class;




template <typename event_data_struct, typename event_return_struct> class sender_connection
{
public:

  List< sending_base_class<event_data_struct, event_return_struct> >  sl;
  
  virtual bool send_event(sending_base_class<event_data_struct, event_return_struct>*  s,
			  event_data_struct                                            event_data,
			  event_return_struct&                                         event_return) = 0;

  sender_connection();
  virtual ~sender_connection();
};




template <class sending_class, typename event_data_struct, class receiving_class, typename receiver_data_struct, typename event_return_struct> class connection
  : public sender_connection<event_data_struct, event_return_struct>
{
public:

  typedef bool receiving_function(sending_class*         sender,
				  event_data_struct      event_data,
				  receiving_class*       receiver_class,
				  receiver_data_struct   reciever_data,
				  event_return_struct&   event_return);
  
  receiving_class*      receiver;
  receiver_data_struct  receiver_data;
  receiving_function*   receiver_function;
  
  virtual bool send_event(sending_base_class<event_data_struct, event_return_struct>* s, event_data_struct event_data, event_return_struct& event_return);

  connection(receiving_class* receiver, receiver_data_struct receiver_data, receiving_function* receiver_function);  
  virtual ~connection();
};




template<class event_data_struct, class event_return_struct> class sending_base_class
{
public:
  typedef class sender_connection<event_data_struct, event_return_struct> connection_class;

  List<connection_class>  cl;
  
  bool send_event(sending_base_class* p, event_data_struct event_data, event_return_struct& event_return);
  void get_return_struct(int receiver_num, event_return_struct& event_return);
  void add_receiver(connection_class* cc);
  void remove_receiver(connection_class* cc);
  void remove_all_receivers();
  int  get_num_receivers();
  
  sending_base_class();
  ~sending_base_class();
};






template<class event_data_struct, class event_return_struct>
bool sending_base_class<event_data_struct, event_return_struct> ::
send_event(sending_base_class*   p,
	   event_data_struct     event_data,
	   event_return_struct&  event_return)
{
  for(auto le=cl.first(); le; le=le->next())
    {
      connection_class* c = le->data();
      bool handled = c->send_event(p, event_data, event_return);
      if(handled)
	return true;
    }
  
  return false;
}



template<class event_data_struct, class event_return_struct>
void sending_base_class<event_data_struct, event_return_struct> ::
get_return_struct(int receiver_num, event_return_struct& event_return)
{
  connection_class* cc = cl.getItemInList(receiver_num);
  
  if(!cc)
    {
      printf("ERROR In get_return struct in send_events.hh: item not on list.\n");
      abort();
    }
  
  event_return = cc->event_return;
}


template<class event_data_struct, class event_return_struct>
void sending_base_class<event_data_struct, event_return_struct> ::
add_receiver(connection_class* cc)
{
  cl.addAtEnd(cc, false);
  cc->sl.addAtEnd(this, false);
}



template<class event_data_struct, class event_return_struct>
void sending_base_class<event_data_struct, event_return_struct> ::
remove_receiver(connection_class* cc)
{
  cl.remove(cc);
  cc->sl.remove(this);
} 



template<class event_data_struct, class event_return_struct>
void sending_base_class<event_data_struct, event_return_struct> ::
remove_all_receivers()
{
  // Must be careful when removing all elements from the list, since when we remove
  // an element it changes the list.  So need to start again at the start.
  for(auto le = cl.first(); le; le=cl.first())
    remove_receiver(le->data());
}




template<class event_data_struct, class event_return_struct>
int sending_base_class<event_data_struct, event_return_struct> ::
get_num_receivers()
{
  return cl.numInList();
}
  

template<class event_data_struct, class event_return_struct>
sending_base_class<event_data_struct, event_return_struct> ::
sending_base_class()
{
}



template<class event_data_struct, class event_return_struct>
sending_base_class<event_data_struct, event_return_struct> ::
~sending_base_class()
{
  remove_all_receivers();
}



template <typename event_data_struct, typename event_return_struct>
sender_connection<event_data_struct, event_return_struct> ::
sender_connection()
{
}


template <typename event_data_struct, typename event_return_struct>
sender_connection<event_data_struct, event_return_struct> ::
~sender_connection()
{
  for(auto le = sl.first(); le; )
    {
      auto sender = le->data();
      le=le->next();
      sender->remove_receiver(this);
    }
}


template <class sending_class, typename event_data_struct, class receiving_class, typename receiver_data_struct, typename event_return_struct>
bool connection<sending_class, event_data_struct, receiving_class, receiver_data_struct, event_return_struct> ::
send_event(sending_base_class<event_data_struct, event_return_struct>* s, event_data_struct event_data, event_return_struct& event_return)
{
  bool handled = (*receiver_function) ( (sending_class*)s, event_data, receiver, receiver_data, event_return );
  return handled;
}


template <class sending_class, typename event_data_struct, class receiving_class, typename receiver_data_struct, typename event_return_struct>
connection<sending_class, event_data_struct, receiving_class, receiver_data_struct, event_return_struct> ::
connection(receiving_class* receiver, receiver_data_struct receiver_data, receiving_function* receiver_function)
  : sender_connection<event_data_struct, event_return_struct>()
{
  this->receiver = receiver; this->receiver_data = receiver_data; this->receiver_function = receiver_function;
}


template <class sending_class, typename event_data_struct, class receiving_class, typename receiver_data_struct, typename event_return_struct>
connection<sending_class, event_data_struct, receiving_class, receiver_data_struct, event_return_struct> ::
~connection()
{
}


#endif
