/*****
* flatguide.h
* Andy Hammerlindl 2005/02/23
*
* The data structure that builds up a knotlist.  This is done by calling in
* order the methods to set knots, specifiers, and tensions.
* Used by the guide solving routines.
*
* NOTE: figure out how nullpath{}..a should be handled.
*****/

#ifndef FLATGUIDE_H
#define FLATGUIDE_H

#include "knot.h"
#include "guideflags.h"

namespace camp {

class flatguide
{
 // A cached solution of the path.  When traversing through a tree of guides,
 // if a cycle tag is encountered, then the path is solved up to that point.
 // If the guide continues from there (which rarely occurs in practice), all of
 // the control points solved are added as control specifiers, and then solved
 // into a path again.  In the (usual) case that a cycle ends a path, the
 // cached path avoids this second pass.
 bool solved;

 // Used by reverse(guide) to indicate the presence of an unresolved
 // interior cycle.
 bool precycle;

 path p;

 cvector<knot> nodes;

 // Information before the first knot.  For a non-cyclic guide, this is
 // ignored.  For a cyclic guide, it may be useful, but I can't determine a
 // sensible way to use it yet.
 tension tout;
 spec *out;

 // Information for the next knot to come.
 tension tin;
 spec *in;

 static spec open;

 tension& tref(side s)
 {
   switch (s) {
     case OUT:
       return nodes.empty() ? tout : nodes.back().tout;
     case IN:
     default:
       return tin;
   }
 }

 // Returns a reference to a spec* so that it may be assigned.
 spec*& sref(side s)
 {
   switch (s) {
     case OUT:
       return nodes.empty() ? out : nodes.back().out;
     case IN:
     default:
       return in;
   }
 }

 void addPre(path& p, Int j);
 void addPoint(path& p, Int j);
 void addPost(path& p, Int j);

 void clearNodes() {
   nodes.clear();
   in=&open;
   tin=tension();
 }
 void clearPath() {
   p=path();
   solved=false;
 }

 void uncheckedAdd(path p, bool allowsolve=true);

 // Sets solved to false, indicating that the path has been updated since last
 // being solved.  Also, copies a solved path back in as knots and control
 // specifiers, as it will have to be solved again.
 void update() {
   if (solved) {
     solved=false;
     clearNodes();
     add(p);
     clearPath();
   }
 }

public:
 flatguide()
   : solved(true), precycle(false), p(), out(&open), in(&open) {}

 Int size() const {
   return (Int) nodes.size();
 }

 knot Nodes(Int i) const {
   return nodes[i];
 }

 void setTension(tension t, side s) {
   update();
   tref(s)=t;
 }
 void setSpec(spec *p, side s) {
   assert(p);
   update();
   spec *&ref=sref(s);
   // Control specifiers trump normal direction specifiers.
   if (!ref || !ref->controlled() || p->controlled())
     ref=p;
 }

 void add(pair z) {
   update();
   // Push the pair onto the vector as a knot, using the current in-specifier
   // and in-tension for the in side for the knot. Use default values for the
   // out side, as those will be set after the point is added.
   nodes.push_back(knot(z,in,&open,tin,tension()));

   // Reset the in-spec and in-tension to defaults;
   tin=tension();
   in=&open;
 }

 // Reverts to an empty state.
 void add(path p, bool allowsolve=true) {
   update();
   uncheckedAdd(p,allowsolve);
 }

 void clear() {
   clearNodes();
   clearPath();
 }

 void close() {
   if(!nodes.empty()) {
     nodes.front().in=in;
     nodes.front().tin=tin;
   }
 }

 void resolvecycle() {
   if(!nodes.empty())
     nodes.push_back(nodes.front());
 }

 void precyclic(bool b) {
   precycle=b;
 }

 bool precyclic() {
   return precycle;
 }

 // Once all information has been added, release the flat result.
 simpleknotlist list(bool cycles=false) {
   if(cycles && !nodes.empty()) close();
   return simpleknotlist(nodes,cycles);
 }

 // Yield a path from the guide as represented here.
 path solve(bool cycles=false) {
   if (solved)
     return p;
   else {
     simpleknotlist l=list(cycles);
     p=camp::solve(l);
     solved=true;
     return p;
   }
 }
};

} // namespace camp

#endif // FLATGUIDE_H