/*****
* knot.h
* Andy Hammerlindl 200/02/10
*
* Describes a knot, a point and its neighbouring specifiers, used as an
* intermediate structure in solving paths.
*****/

#ifndef KNOT_H
#define KNOT_H

#include <iostream>
#include <vector>
#include <algorithm>

#include "mod.h"
#include "pair.h"
#include "path.h"

namespace camp {

using mem::vector;

// The choice of branch cuts of atan2 disguishes between y=+0.0 and y=-0.0 in
// the case where x<0.  This can lead to strange looking paths being
// calculated from guides of the form a..b..cycle.  To avoid these degenerate
// cases, the niceAngle routine moves the branch cut so that the sign of a
// zero won't matter.
double niceAngle(pair z);

// A cyclic vector: ie. a vector where the index is taken mod the size of the
// vector.
template <typename T>
class cvector : public vector<T> {
public:
 cvector() {}
 cvector(size_t n) : vector<T>(n) {}
 cvector(size_t n, const T& t) : vector<T>(n,t) {}
 cvector(const vector<T>& v) : vector<T>(v) {}

 T& operator[](Int j) {
   return vector<T>::operator[](imod(j,(Int) this->size()));
 }
 const T& operator[](Int j) const {
   return vector<T>::operator[](imod(j,(Int) this->size()));
 }
};

// Forward declaration.
class knotlist;

/* A linear equation (one of a set of equations to solve for direction through
  knots in a path).  The i-th equation is:

  pre*theta[i-1] + piv*theta[i] + post*theta[i+1] = aug

  where indices are taken mod n.
*/
struct eqn {
 double pre,piv,post,aug;
 eqn(double pre, double piv, double post, double aug)
   : pre(pre), piv(piv), post(post), aug(aug) {}

 friend ostream& operator<< (ostream& out, const eqn& e)
 {
   return out << e.pre << " * pre + "
              << e.piv << " * piv + "
              << e.post << " * post = "
              << e.aug;
 }
};


// A direction specifier, telling how the path behaves coming in or out of a
// point.  The base class represents the "open" specifier.
class spec : public gc {
public:
 virtual ~spec() {}

 // If the knot is open, it gives no restriction on the behavior of the
 // path.
 virtual bool open() { return true; }
 virtual bool controlled() { return false; }
 virtual pair control() {return pair(0.0,0.0);}
 virtual double curl() { return -1.0; }
 virtual pair dir() { return pair(0.0,0.0); }

 // When a knot has a restriction on one side but is open on the other, the
 // restriction implies a restriction on the other side.  This is the partner
 // restriction defined here, where the pair argument is for the location of
 // the knot.
 virtual spec *outPartner(pair) { return this; }
 virtual spec *inPartner(pair) { return this; }

 virtual void print(ostream&) const {}
};

inline ostream& operator<< (ostream& out, spec& s)
{
 s.print(out);
 return out;
}


// Specifier used at an endpoint.
class endSpec : public spec {
public:
 bool open() { return false; }

 // Returns an equation used to solve for the thetas along the knot.  These are
 // called by eqnprop in the non-cyclic case for the first and last equations.
 virtual eqn eqnOut(Int j, knotlist& l,
                    cvector<double>& d, cvector<double>& psi) = 0;
 virtual eqn eqnIn (Int j, knotlist& l,
                    cvector<double>& d, cvector<double>& psi) = 0;
};

// A specifier with a given direction (in radians).
class dirSpec : public endSpec {
 double given;
public:
 // Direction should be given in the range [-PI,PI]
 dirSpec(double given)
   : given(given) {}
 dirSpec(pair z)
   : given(niceAngle(z)) {}

 pair dir() { return expi(given); }

 eqn eqnOut(Int j, knotlist& l, cvector<double>& d, cvector<double>& psi);
 eqn eqnIn (Int j, knotlist& l, cvector<double>& d, cvector<double>& psi);

 void print(ostream& out) const {
   out << "{dir(" << degrees(given) << ")}";
 }
};

// A curl specifier.  The curvature at the end knot should be gamma times the
// curvature at the neighbouring knot.
class curlSpec : public endSpec {
 double gamma;
public:
 // Gamma should be non-negative.
 curlSpec(double gamma=1.0)
   : gamma(gamma) {
   if(gamma < 0)
     reportError("curl cannot be less than 0");
 }

 double curl() { return gamma; }

 eqn eqnOut(Int j, knotlist& l, cvector<double>& d, cvector<double>& psi);
 eqn eqnIn (Int j, knotlist& l, cvector<double>& d, cvector<double>& psi);

 void print(ostream& out) const {
   out << "{curl " << gamma << "}";
 }
};


// A specifier with a control point.  All information for this portion of the
// curve has been determined.
class controlSpec : public spec {
public:
 pair cz;
 bool straight;

 controlSpec(pair cz, bool straight=false)
   : cz(cz), straight(straight) {}

 bool open() { return false; }
 bool controlled() { return true; }
 pair control() { return cz; }

 // The partner spec will be a dirSpec in the same direction the specifier
 // takes the path, unless the velocity is zero, then it uses a curl
 // specifier.
 spec *outPartner(pair);
 spec *inPartner(pair);

 void print(ostream& out) const {
   // NOTE: This format cannot be read back in.
   out << "{control " << cz << "}";
 }
};


// The tension information for one side of a knot.
struct tension {
 double val;
 bool atleast;

 tension(double val=1.0, bool atleast=false)
   : val(val), atleast(atleast) {
   if(val < 0.75)
     reportError("tension cannot be less than 3/4");
 }
};

inline
ostream& operator<<(ostream& out, tension t)
{
 return out << "tension" << (t.atleast ? " atleast " : " ") << t.val;
}

// A knot, a point with specifiers to double the path coming in and going out
// of the knot.
struct knot {
 pair z;
 spec *in;
 spec *out;
 tension tin, tout;

 knot() {}
 knot(pair z, spec *in, spec *out,
      tension tin=tension(), tension tout=tension())
   : z(z), in(in), out(out), tin(tin), tout(tout) {}

 double alpha() { return 1.0/tout.val; }
 double beta() { return 1.0/tin.val; }
};

ostream& operator<<(ostream& out, const knot& k);

// Abstract base class for a section of a guide.
class knotlist {
public:
 virtual ~knotlist() {}

 virtual Int length() = 0;
 virtual bool cyclic() = 0;

 // Returns the number of knots.
 Int size() {
   return cyclic() ? length() : length() + 1;
 }
 bool empty() {
   return size()==0;
 }

 virtual knot& cell(Int) = 0;
 virtual knot& operator[] (Int i) {
#if 0
   assert(cyclic() || (0 <= i && i <= length()));  // Bounds check.
#endif
   return cell(i);
 }
 knot& front() {
   return (*this)[0];
 }
 knot& back() {
   return (*this)[length()];
 }
};


// Defines a knotlist as a piece of another knotlist.
class subknotlist : public knotlist {
 knotlist& l;
 Int a,b;
public:
 subknotlist(knotlist& l, Int a, Int b)
   : l(l), a(a), b(b) {}

 Int length() { return b-a; }
 bool cyclic() { return false; }
 knot& cell(Int i) { return l[a+i]; }
};

struct simpleknotlist : public knotlist {
 cvector<knot> nodes;
 bool cycles;

 simpleknotlist(cvector<knot> nodes, bool cycles=false)
   : nodes(nodes), cycles(cycles) {}

 Int length() { return cycles ? (Int) nodes.size() : (Int) nodes.size() - 1; }
 bool cyclic() { return cycles; }
 knot& cell(Int j) { return nodes[j]; }
};

// A protopath is a path being made.
struct protopath {
 bool cycles;
 Int n;
 mem::vector<solvedKnot> nodes;

 protopath(Int n, bool cycles)
   : cycles(cycles), n(n), nodes(n) {}

 solvedKnot& operator[](Int j) {
   return nodes[imod(j,n)];
 }

 bool& straight(Int j) {
   return (*this)[j].straight;
 }
 pair& pre(Int j) {
   return (*this)[j].pre;
 }
 pair& point(Int j) {
   return (*this)[j].point;
 }
 pair& post(Int j) {
   return (*this)[j].post;
 }

 void controlEnds() {
   if (!cycles) {
     solvedKnot& start=(*this)[0];
     solvedKnot& end=(*this)[n-1];

     start.pre=start.point;
     end.post=end.point;
   }
 }
 // Once all the controls are set, return the final (constant) path.
 path fix() {
   return path(nodes,n,cycles);
 }
};


// Represents properties that can be computed along a knotlist.
// Examples include distances (d), turning angles (psi), and the linear
// equations used to solve for the thetas.
template <typename T>
class knotprop {
protected:
 knotlist& l;

 // Calculate the property for the usual case in the iteration (and for a
 // cyclic knot, the only case), at the index given.
 virtual T mid(Int) = 0;

 // The special cases, these default to the usual case: mid.
 virtual T solo(Int j)     // Calculates the property for a list of length 0.
 {
   return mid(j);
 }
 virtual T start(Int j)    // Calculates it at the start of the list.
 {
   return mid(j);
 }
 virtual T end(Int j)      // Calculate it at the end.
 {
   return mid(j);
 }

 virtual cvector<T> linearCompute()
 {
   Int n=l.length();
   cvector<T> v;
   if (n==0)
     v.push_back(solo(0));
   else {
     v.push_back(start(0));
     for (Int j=1; j<n; ++j)
       v.push_back(mid(j));
     v.push_back(end(n));
   }
   return v;
 }

 virtual cvector<T> cyclicCompute()
 {
   Int n=l.length();
   cvector<T> v;
   for (Int j=0; j<n; ++j)
     v.push_back(mid(j));
   return v;
 }

 virtual cvector<T> linearBackCompute()
 {
   Int n=l.length();
   cvector<T> v;
   if (n==0)
     v.push_back(solo(0));
   else {
     v.push_back(end(n));
     for (Int j=1; j<n; ++j)
       v.push_back(mid(n-j));
     v.push_back(start(0));
   }
   return v;
 }

 virtual cvector<T> cyclicBackCompute()
 {
   Int n=l.length();
   cvector<T> v;
   for (Int j=1; j<=n; ++j)
     v.push_back(mid(n-j));
   return v;
 }

public:
 virtual ~knotprop() {}

 virtual cvector<T> compute() {
   return l.cyclic() ? cyclicCompute() : linearCompute();
 }

 // Compute the values in the opposite order.  This is needed for instance if
 // the i-th calculation needed a result computed in the i+1-th, such as in the
 // back substitution for solving thetas.
 virtual cvector<T> backCompute() {
   cvector<T> v=l.cyclic() ? cyclicBackCompute() : linearBackCompute();

   // Even though they are computed in the backwards order, return them in the
   // standard order.
   reverse(v.begin(),v.end());
   return v;
 }

 knotprop(knotlist& l)
   : l(l) {}
};

// A knot transforms, it takes in one knotlist and transforms it knot for knot
// into a new one.
class knottrans : public knotprop<knot> {
protected:
 virtual knot mid(Int j) {
   /* By default, just copy the knot. */
   return l[j];
 }

public:
 virtual ~knottrans() {}

 knottrans(knotlist& l)
   : knotprop<knot>(l) {}

 virtual simpleknotlist trans() {
   return simpleknotlist(compute(),l.cyclic());
 }
};

// Like a knotprop, but it doesn't compute a vector of values for the knot.  It
// iterates over the knotlist calling method for side-effect.  For instance,
// this is used to plug control points into protopaths.
class knoteffect {
protected:
 knotlist& l;

 virtual void mid(Int) = 0;

 // The special cases, these default to the usual case: mid.
 virtual void solo(Int j) {
   mid(j);
 }
 virtual void start(Int j) {
   mid(j);
 }
 virtual void end(Int j) {
   mid(j);
 }

 virtual void linearExec()
 {
   Int n=l.length();
   if (n==0)
     solo(0);
   else {
     start(0);
     for (Int j=1; j<n; ++j)
       mid(j);
     end(n);
   }
 }

 virtual void cyclicExec()
 {
   Int n=l.length();
   for (Int j=0; j<n; ++j)
     mid(j);
 }

 virtual void linearBackExec()
 {
   Int n=l.length();
   if (n==0)
     solo(0);
   else {
     end(n);
     for (Int j=1; j<n; ++j)
       mid(n-j);
     start(0);
   }
 }

 virtual void cyclicBackExec()
 {
   Int n=l.length();
   for (Int j=1; j<=n; ++j)
     mid(n-j);
 }

public:
 virtual ~knoteffect() {}

 virtual void exec() {
   if (l.cyclic())
     cyclicExec();
   else
     linearExec();
 }

 virtual void backCompute() {
   if (l.cyclic())
     cyclicBackExec();
   else
     linearBackExec();
 }

 knoteffect(knotlist& l)
   : l(l) {}
};

path solve(knotlist& l);

path solveSimple(cvector<pair>& z);

double velocity(double theta, double phi, tension t);

} // namespace camp

GC_DECLARE_PTRFREE(camp::eqn);
GC_DECLARE_PTRFREE(camp::tension);

#endif // KNOT_H