<'
# Copyright (c) 2012-2013, 2017-2019, 2024-2026 LAAS/CNRS
# All rights reserved.
#
# Redistribution and use  in source  and binary  forms,  with or without
# modification, are permitted provided that the following conditions are
# met:
#
#   1. Redistributions of  source  code must retain the  above copyright
#      notice and this list of conditions.
#   2. Redistributions in binary form must reproduce the above copyright
#      notice and  this list of  conditions in the  documentation and/or
#      other materials provided with the distribution.
#
#                                      Anthony Mallet on Mon Sep 17 2012
#

if {[llength $argv] != 1} { error "expected arguments: component" }
lassign $argv component

# compute handy shortcuts
set comp [$component name]
set COMP [string toupper [$component name]]

lang c
'>
#ifndef H_<"$COMP">_MSGLIB
#define H_<"$COMP">_MSGLIB

#include <map>
#include <memory>
#include <mutex>

#include "actionlib/client/action_client.h"

#include "<"$comp">_service.h"
#include "<"$comp">_action.h"
#include "<"$comp">_portlib.h"

#include "private.h"

template<typename T> struct gnmix_topic_connection {
  ros::Subscriber s;
  T data;
  pthread_spinlock_t cblock;
  boost::shared_ptr<T const> cbdata;

  gnmix_topic_connection() {
    pthread_spin_init(&cblock, 0);
  }

  ~gnmix_topic_connection() {
    s.shutdown();
    pthread_spin_destroy(&cblock);
  }
};

template<typename T> struct gnmix_pub_connection {
  std::string name;
  ros::Publisher p;
  T data;

  ~gnmix_pub_connection() {
    p.shutdown();
  }
};

namespace <"$comp">_genom {
  template<class T>
  struct actionclient {
    /* This holds a reference on an actionlib client for action T, that is
     * unique and shared for each remote component instance. This is needed
     * because actionlib clients will not receive latched status topic
     * information, except for the very first subscription. isServerConnected()
     * will thus fail. It is assumed that actionlib client have the proper
     * locking to handle such a shared client, so locking is done only for
     * client creation. */
    typedef typename actionlib::ActionClient<T> client;
    typedef typename std::map<std::string, std::shared_ptr<client>> map;

    std::shared_ptr<client> c;

    actionclient(ros::NodeHandle &n, const std::string &name) {
      /* get unique client for this action, creating it if it does not exist
       * yet */
      const std::lock_guard<std::mutex> lock(l);

      i = m.find(name);
      if (i == m.end()) {
        const auto k = m.emplace(name, new client(n, name));
        i = k.first;
      }

      /* share client */
      c = i->second;
    }

    ~actionclient() {
      const std::lock_guard<std::mutex> lock(l);

      /* destroy unique client if the reference count allows it */
      c.reset();
      if (i->second.use_count() == 1) m.erase(i);
    }

   private:
    static std::mutex l;
    static map m;

    typename map::const_iterator i;
  };

  template<class T>
  typename actionclient<T>::map actionclient<T>::m;
  template<class T>
  std::mutex actionclient<T>::l;


  struct connections : connections_base {
<'foreach s [$component services] {'>
<'  if {[catch {$s task}]} {'>
    ros::ServiceClient <"[$s name]">_;
<'  } else {'>
    actionclient< action_<"[$s name]"> > *<"[$s name]">_;
<'  }'>
<'}'>

<'foreach p [$component ports out simple] {'>
    gnmix_topic_connection< port_<"[$p name]"> > <"[$p name]">_;
<'}'>
<'foreach p [$component ports out multiple] {'>
    std::map <std::string,
      gnmix_topic_connection< port_<"[$p name]"> > > <"[$p name]">_;
<'}'>
<'foreach p [$component ports in] {'>
    std::map <std::string,
      gnmix_pub_connection< port_<"[$p name]"> > > <"[$p name]">_;
<'}'>

    connections() {
<'foreach s [$component services] {'>
<'  if {![catch {$s task}]} {'>
      <"[$s name]">_ = NULL;
<'  }'>
<'}'>
    }

    ~connections() {
<'foreach s [$component services] {'>
<'  if {[catch {$s task}]} {'>
      <"[$s name]">_.shutdown();
<'  } else {'>
      if (<"[$s name]">_) delete <"[$s name]">_;
<'  }'>
<'}'>
    }
  };
}

#endif /* H_<"$COMP">_MSGLIB */
