// dzn-runtime -- Dezyne runtime library
//
// Copyright © 2018 Rutger van Beusekom <rutger.van.beusekom@verum.com>
// Copyright © 2019, 2020 Jan Nieuwenhuizen <janneke@gnu.org>
//
// This file is part of dzn-runtime.
//
// dzn-runtime is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// dzn-runtime 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with dzn-runtime.  If not, see <http://www.gnu.org/licenses/>.
//
// Commentary:
//
// Code:

#ifndef DZN_META_HH
#define DZN_META_HH

#include <algorithm>
#include <functional>
#include <string>
#include <stdexcept>
#include <vector>

inline std::string to_string()
{
  return "return-meta";
}

namespace dzn
{
#define DZN_VERSION_STRING "2.11.0"
int const version_major = 2;
int const version_minor = 11;
int const version_patch = 0;

  struct meta;

  namespace port
  {
    struct meta
    {
      struct
      {
        std::string port;
        void* address;
        const dzn::meta* meta;
      } provide;

      struct
      {
        std::string port;
        void* address;
        const dzn::meta* meta;
      } require;
    };
  }

  struct meta
  {
    std::string name;
    std::string type;
    const meta* parent;
    mutable size_t rank;
    std::vector<const port::meta*> require;
    std::vector<const meta*> children;
    std::vector<std::function<void()>> ports_connected;
  };

  inline void rank(const dzn::meta* m, size_t r)
  {
    if(m) {
      m->rank = std::max(m->rank, r);
      for(auto i : m->require) rank(i->provide.meta, m->rank + 1);
    }
  }

  inline std::string path(const meta* m, std::string p = std::string())
  {
    p = p.empty() ? p : "." + p;
    if(!m) return "<external>" + p;
    if(!m->parent) return m->name + p;
    return path(m->parent, m->name + p);
  }

  struct binding_error: public std::runtime_error
  {
    binding_error(const port::meta& m, const std::string& msg)
    : std::runtime_error("not connected: " + path(m.provide.address ? m.provide.meta : m.require.meta,
                                                  m.provide.address ? m.provide.port : m.require.port) + "." + msg)
    {}
  };

  template <typename Signature>
  struct async
  {
    struct {
      std::function<Signature> req;
      std::function<void()> clr;
    } in;
    struct {
      std::function<Signature> ack;
    } out;

    dzn::port::meta meta;

    inline async(const dzn::port::meta& m) : meta(m) {}

    void check_bindings() const
    {
      if (! in.req) throw dzn::binding_error(meta, "in.req");
      if (! in.clr) throw dzn::binding_error(meta, "in.clr");
      if (! out.ack) throw dzn::binding_error(meta, "out.ack");
    }
  };
}
#endif //DZN_META_HH
//version: 2.11.0
