/* cor_syn_iface.h */

#ifndef COR_SYN_IFACE__H
#define COR_SYN_IFACE__H

#include <coriolis/kernel.h>
#include "component.h"
#include "link.h"
#include "cor_link.h"
#include <LEDA/color.h>
#include "view3d.h"

struct dc_func;

extern BodySystem universe;
extern bool upheaval; /* set to true if changes made require coriolis to call 
			 upheaval before advancing time */

const char *const coriolis_clock_name = "coriolis";

bool init_coriolis_agent( void );
bool close_coriolis_agent( void );

/* label looked for when looking for the body of a physical component */
const char *const coriolis_shape_label = "body_shape";

/* coriolis agent clock.  uses virtual dc_clock::update to advance the coriolis
   universe, and bound the clock to coriolis time */
extern dc_clock *coriolis_clock;

/* amount of time actually advanced */
double advance_coriolis_time( double max_advance );

/* PHYSICAL LINK TYPES
   distance link - a given bodypoint must lie a given distance from another 
                   bodypoint/worldpoint
   axial link - attaches two bodies at two poins allowing only rotational 
                freedom along that axis
   attachment - attaches two bodies
 */


/* base physical link class 
   physical links create constraints, combine objects, provide impulses, or
   make objects subject to field effects or other influences */
class dc_physical_link : public dc_link {
protected:
  RigidBody *src_rb, *tgt_rb;
public:
  dc_physical_link( void ) { src_rb = tgt_rb = nil; }

  const dc_link_type type( void ) const { return Physical_lt; }

  int rehash( void );
};

/* dc_constraint is not abstract, but only descendants are useful */
class dc_constraint_link : public dc_physical_link {
private:
  list<EConstraint *> econstr_list;

protected:
  void add_constr( EConstraint & );
  void unset_constr( void );
public:
  // dc_constraint_link();
  ~dc_constraint_link();
  
  int rehash( void );
};

/* will construct a constraint such that a point on source has to lie a given
   distance from a point on target */
class dc_distance_link : public dc_constraint_link {
protected:
  triple source_bodypt;
  triple target_bodypt; /* if target is nil then target_bodypt is considered to
			   be a world_point */
  double distance;
public:
  dc_distance_link();

  void set_pts( ctriple source_pt, ctriple target_pt, double dist );

  int rehash( void );
  const dc_link_type type( void ) const { return Distance_lt; }

  bool exec( void ); /* draws link */
  ostream &display( ostream & = cout ) const;
};

class dc_axial_link : public dc_constraint_link {
protected:
  triple source_pta, source_ptb;
  triple target_pta, target_ptb;
public:
  dc_axial_link();
  
  /* true if error -- pta must be different from ptb for both source & target */
  bool set_pts( ctriple source_pta, ctriple source_ptb,
		ctriple target_pta, ctriple target_ptb );

  int rehash( void );
  const dc_link_type type( void ) const { return Axial_lt; }

  bool exec( void ); /* draws link */
  ostream &display( ostream & = cout ) const;
};

class dc_attachment_link : public dc_constraint_link {
public:
  //  dc_attachment_link();

  int rehash( void );
  const dc_link_type type( void ) const { return Attachment_lt; }

  //  ostream &display( ostream & = cout ) const;
};

struct dc_variable_force : public Influence {
  dc_func *force;
  dc_func *src_bp;
  dc_func *tgt_bp;

  RigidBody *src;
  RigidBody *tgt;

  dc_variable_force( void ) { force = src_bp = tgt_bp = nil; src = tgt = nil; }
  ~dc_variable_force( void );

  void init( dc_func &, dc_func &, dc_func & );
  void init( dc_func &, dc_func & );
  
  void ignore( Body & );    /* used to remove influence */
  void update( void );      /* required */

  int rehash( void );
};

/* applies a force generated by a function to a body at every update */
class dc_force_link : public dc_physical_link {
private:
  dc_variable_force I;
public:
  dc_force_link( void );
  ~dc_force_link( void );

  /* true if error.  error if function does not return a triple */
  bool set_pts( dc_func &f, dc_func &s, dc_func &t );
  bool set_pts( dc_func &f, dc_func &s );

  int rehash( void );
  // bool exec( void ); /* applies force */
  ostream &display( ostream & = cout ) const;
};

inline ostream &operator<<( ostream &stream, const dc_distance_link &l ) {
  return l.display( stream );
}
inline ostream &operator<<( ostream &stream, const dc_axial_link &l ) {
  return l.display( stream );
}
inline ostream &operator<<( ostream &stream, const dc_attachment_link &l ) {
  return l.display( stream );
}

/* CORIOLIS AGENT SPECIFICATION GRAMMAR -- 
    filename  translation    orientation            scale
   <filename> +(<x>,<y>,<z>)o(<or>,<oi>,<oj>,<ok>)x(<width>,<height>,<depth>)
 */

#endif
