#line 615 "/home/ubuntu/felix/src/packages/rtl.fdoc"
#ifndef __FLX_RTL_H__
#define __FLX_RTL_H__

#include "flx_rtl_config.hpp"
#include "flx_exceptions.hpp"
#include "flx_gc.hpp"
#include "flx_serialisers.hpp"
#include "flx_rtl_shapes.hpp"
#include "flx_compiler_support_headers.hpp"
#include "flx_compiler_support_bodies.hpp"
#include "flx_continuation.hpp"
#include "flx_svc.hpp"

#include <string>
#include <functional>
#include <cstdint>
#include <mutex>
#include <list>
#include <atomic>
#include "flx_spinlock.hpp"

namespace flx { namespace rtl {


typedef void *void_pointer;

// ********************************************************
// Compact Linear Type and projection
// ********************************************************

typedef ::std::uint64_t cl_t;

// ********************************************************
// Felix system classes
// ********************************************************


struct RTL_EXTERN muxguard;

// MOVED TO flx_exceptions
//struct RTL_EXTERN con_t;     // continuation
struct RTL_EXTERN jump_address_t;     // label variable type
struct RTL_EXTERN fthread_t; // f-thread

struct RTL_EXTERN _uctor_;   // union constructor
//struct RTL_EXTERN _variant_;   // variant constructor
struct RTL_EXTERN schannel_t;   // synchronous channel type
struct RTL_EXTERN clptr_t;  // pointer to compact linear product component
struct RTL_EXTERN clprj_t;  // compact linear projection

struct RTL_EXTERN muxguard {
private:
   muxguard() = delete;
   muxguard(muxguard const&) = delete;
   muxguard *operator=(muxguard const&)=delete;
  ::std::mutex *m;
public:
  muxguard (::std::mutex *p);
  ~muxguard ();
};


// MOVE THIS TO RTL AND PROVIDE SUITABLE RTTI SO GC KNOWS ABOUT THE FRAME POINTER
struct RTL_EXTERN jump_address_t
{
  con_t *target_frame;
  FLX_LOCAL_LABEL_VARIABLE_TYPE local_pc;

  jump_address_t (con_t *tf, FLX_LOCAL_LABEL_VARIABLE_TYPE lpc) :
    target_frame (tf), local_pc (lpc)
  {}
  jump_address_t () : target_frame (0), local_pc(0) {}
  jump_address_t (con_t *tf) : target_frame(tf), local_pc(0) {}
  // default copy constructor and assignment
};

// ********************************************************
/// FTHREAD. Felix threads
// ********************************************************

struct RTL_EXTERN fthread_t // fthread abstraction
{
  con_t *cc;                    ///< current continuation
  fthread_t *next;              ///< link to next fthread, to be used in scheduler queue and schannels
  fthread_t();                  ///< dead thread, suitable for assignment
  fthread_t(con_t*);            ///< make thread from a continuation
  svc_req_t *run();               ///< run until dead or driver service request
  void kill();                  ///< kill by detaching the continuation
  svc_req_t *get_svc()const;      ///< get current service request of waiting thread
private: // uncopyable
  fthread_t(fthread_t const&) = delete;
  void operator=(fthread_t const&) = delete;
};


// ********************************************************
/// SCHANNEL. Synchronous channels
// ********************************************************

struct RTL_EXTERN schannel_t
{
  fthread_t *top; // has to be public for offsetof macro

  void push_reader(fthread_t *);        ///< add a reader
  fthread_t *pop_reader();              ///< pop a reader, NULL if none
  void push_writer(fthread_t *);        ///< add a writer
  fthread_t *pop_writer();              ///< pop a writer, NULL if none
  schannel_t();

private: // uncopyable
  schannel_t(schannel_t const&) = delete;
  void operator= (schannel_t const&) = delete;
};

// ********************************************************
/// VARIANTS. Felix union type
/// note: non-polymorphic, so ctor can be inline
// ********************************************************

struct RTL_EXTERN _uctor_
{
  int variant;  ///< Variant code
  void *data;   ///< Heap variant constructor data
  _uctor_() : variant(-1), data(0) {}
  _uctor_(int i, void *d) : variant(i), data(d) {}
  _uctor_(int *a, _uctor_ x) : variant(a[x.variant]), data(x.data) {}
};


// ********************************************************
/// VARIANTS. Felix variant type
/// note: non-polymorphic, so ctor can be inline
// ********************************************************

/* NOT USED ANY MORE
struct RTL_EXTERN _variant_
{
  char const *vname;  ///< Variant code
  void *vdata;   ///< Heap variant constructor data
  _variant_() : vname(""), vdata(0) {}
  _variant_(char const *n, void *d) : vname(n), vdata(d) {}
};
**/


// ===============================================================
// COMPACT LINEAR STUFF
// ===============================================================

// ********************************************************
/// COMPACT LINEAR PROJECTIONS
// ********************************************************

struct RTL_EXTERN clprj_t
{
  cl_t divisor;
  cl_t modulus;
  clprj_t () : divisor(1), modulus(-1) {}
  clprj_t (cl_t d, cl_t m) : divisor (d), modulus (m) {}

};

// reverse compose projections left \odot right
inline clprj_t rcompose (clprj_t left, clprj_t right) {
  return clprj_t (left.divisor * right.divisor, right.modulus);
}

// apply projection to value
inline cl_t apply (clprj_t prj, cl_t v) {
  return v / prj.divisor % prj.modulus;
}

// ********************************************************
/// COMPACT LINEAR POINTERS
// ********************************************************

struct RTL_EXTERN clptr_t
{
  cl_t *p;
  cl_t divisor;
  cl_t modulus;
  clptr_t () : p(0), divisor(1),modulus(-1) {}
  clptr_t (cl_t *_p, cl_t d, cl_t m) : p(_p), divisor(d),modulus(m) {}

  // upgrade from ordinary pointer
  clptr_t (cl_t *_p, cl_t siz) : p (_p), divisor(1), modulus(siz) {}

  // add projection to existing compact linear pointer
  clptr_t (clptr_t _p, cl_t div, cl_t siz) : p(_p.p), divisor (_p.divisor * div), modulus(siz) {}
};

struct RTL_EXTERN const_clptr_t
{
  cl_t const *p;
  cl_t divisor;
  cl_t modulus;
  const_clptr_t () : p(0), divisor(1),modulus(-1) {}
  const_clptr_t (cl_t const *_p, cl_t d, cl_t m) : p(_p), divisor(d),modulus(m) {}

  // copy constructors
  const_clptr_t (const_clptr_t const &x) : p(x.p), divisor(x.divisor), modulus(x.modulus) {}
  const_clptr_t (clptr_t const &x) : p(x.p), divisor(x.divisor), modulus(x.modulus) {}

  // upgrade from ordinary pointer
  const_clptr_t (cl_t const *_p, cl_t siz) : p (_p), divisor(1), modulus(siz) {}

  // add projection to existing compact linear pointer
  const_clptr_t (const_clptr_t _p, cl_t div, cl_t siz) : p(_p.p), divisor (_p.divisor * div), modulus(siz) {}
};


// apply projection to pointer
inline clptr_t applyprj (clptr_t cp, clprj_t d)  {
  return  clptr_t (cp.p, d.divisor * cp.divisor, d.modulus);
}

inline const_clptr_t applyprj (const_clptr_t cp, clprj_t d)  {
  return  const_clptr_t (cp.p, d.divisor * cp.divisor, d.modulus);
}

// dereference
inline cl_t clt_deref(clptr_t q) { return *q.p / q.divisor % q.modulus; }
inline cl_t clt_deref(const_clptr_t q) { return *q.p / q.divisor % q.modulus; }

// storeat
// NOTE: not available for const version
inline void storeat (clptr_t q, cl_t v) {
    *q.p = *q.p - (*q.p / q.divisor % q.modulus) * q.divisor + v * q.divisor;
    //*q.p -= ((*q.p / q.divisor % q.modulus) - v) * q.divisor; //???
}

// ===============================================================
// TRACE
// ===============================================================
struct flx_trace_t
{
  size_t count;
  int enable_trace;
};

extern RTL_EXTERN int flx_enable_trace;

RTL_EXTERN void flx_trace(flx_trace_t* tr,flx_range_srcref_t sr, char const *file, int line, char const *msg);

}} // namespaces

#endif