/**************************************************************************

   File:                DiscreteDivergenceSkeleton.h

   Author(s):           Pavel Dimitrov

   Created:             22 Jul 2002

   Last Revision:       $Date: 2002/07/25 20:50:47 $

   Description: 

   $Revision: 1.2 $

   $Log: DiscreteDivergenceSkeleton.h,v $
   Revision 1.2  2002/07/25 20:50:47  pdimit
   Making release 0.1

   Revision 1.1  2002/07/23 21:02:51  pdimit
   The branch segmentation of the thinned DivMap is improved --
   the two pixel branches are now included. Still, the degenerate case
   of the square of junction-points is not taken care of.

   Also, a DiscreteDivergenceSkeleton is created which still does not know
   of left and right for the branches, i.e. the contour has not been cut.


	
   Copyright (c) 2002 by Pavel Dimitrov, Centre for Intelligent Machines,
   McGill University, Montreal, QC.  Please see the copyright notice
   included in this distribution for full details.

 **************************************************************************/

#ifndef DISCRETEDIVERGENCESKELETON_H
#define DISCRETEDIVERGENCESKELETON_H

/**********************
     Include Files
 **********************/
#include <stdlib.h>
#include <iostream>
#include "defines.h"
#include "DivergenceMap.h"

namespace sg{
  struct FluxPoint {
    Point p;     // position
    double val;  // flux value
    double dist; // closest distance
    
    char col; // for the thinning and skeleton
    
    FluxPoint() { 
      dist = 0;
      col=-1;
    }
    
    FluxPoint(Point pt, double div, double d, char c){
      dist=d;
      p = pt;
      val = div;
      col=c;
    }
    
    friend std::ostream& operator<<(std::ostream& os, const FluxPoint& fp)
    {
    	return os << '(' << fp.p.x << ',' << fp.p.y << ')';
    }
    
    bool operator==(const FluxPoint& fp)
    {
    	return ((p == fp.p) && (val == fp.val) && (dist == fp.dist) && (col == fp.col));
    }
    
    bool operator<(const FluxPoint &dp){
      return val<dp.val;
    }
  };

  typedef std::vector<FluxPoint> FluxPointList;
  class DiscreteDivergenceSkeleton;
  class DiscreteDivergenceSkeletonEdge;
  class DiscreteDivergenceSkeletonNode;

  typedef DiscreteDivergenceSkeleton DDSkeleton;
  typedef DiscreteDivergenceSkeletonEdge DDSEdge;
  typedef DiscreteDivergenceSkeletonNode DDSNode;

  typedef std::vector<DiscreteDivergenceSkeletonEdge *> DDSEdgeVect;
  typedef std::vector<DiscreteDivergenceSkeletonNode *> DDSNodeVect;

  /////////////////////////////////////////////////////////////////
  //////////////////////////// class DiscreteDivergenceSkeletonNode
  class DiscreteDivergenceSkeletonNode {
  public: //shuld be protected but AFMMSkeleton.h must be corrected first
    DiscreteDivergenceSkeleton *dds;
  public:
    FluxPoint fp;
    std::vector<DiscreteDivergenceSkeletonEdge *> edges;
    
  public:
    DiscreteDivergenceSkeletonNode(FluxPoint p){
      fp = p;
    }

    void addEdge(DiscreteDivergenceSkeletonEdge *e){
      edges.push_back(e);
    }
    
    DDSEdgeVect &getEdges() { return edges; }
    void setSkeleton(DiscreteDivergenceSkeleton *sk){ dds = sk; }

    double getFlux() { return fp.val;  }
    double getDist() { return fp.dist; }
    char   getCol()  { return fp.col;  }
    Point  getPoint(){ return fp.p;    }
    DDSkeleton *getSkeleton() { return dds; }
  };

  /////////////////////////////////////////////////////////////////
  //////////////////////////// class DiscreteDivergenceSkeletonEdge 
  class DiscreteDivergenceSkeletonEdge {
  public: //shuld be protected but AFMMSkeleton.h must be corrected first
    DiscreteDivergenceSkeleton *dds;
    FluxPointList flux_points;
    
    DiscreteDivergenceSkeletonNode *n1, *n2;
  public:
    Curve *curve;
    bool has_right;
    double right_t1, right_t2; // parameter values between which the
    // contour segment on the right is

    bool has_left;
    double left_t1, left_t2; // parameter values between which the
    // contour segment on the right is

    double m_dEgdeRecError; //this value is used in the AFMMSkeleton
    //while simplifying the skeleton one needs to know the reconstruction
    //error of a given edge
    //Initially will be -1
    
    double getSegDist(double st1, double st2){
      double t1, t2;
      Point p1 = flux_points.front().p;
      Point p2 = flux_points.back().p;

      Distance dst = curve->computeDistance(p1, st1, st2);
      t1 = dst.t;
      dst = curve->computeDistance(p2, st1, st2);
      t2 = dst.t;

//        std::cerr << "\nst1: " <<st1 << " st2: " << st2;
//        std::cerr << " t1: " <<t1 << " t2: " << t2 << "\n";

      if (st1 <= st2)
	return fabs(t1-t2);
      else { // st1 > st2

	if(t1 >= st1 && t2 < st1)
	  return curve->getLength() - t1 + t2;

	if(t2 >= st1 && t1 < st1)
	  return curve->getLength() - t2 + t1;
	
	return fabs(t1-t2); // otherwise, both t1 and t2 are on the same side
      }
    }

  public:
    DiscreteDivergenceSkeletonEdge(DiscreteDivergenceSkeleton *dds,
				   FluxPointList flux_points,
				   DiscreteDivergenceSkeletonNode *n1,
				   DiscreteDivergenceSkeletonNode *n2){
      this->dds = dds;
      this->flux_points = flux_points;
      
      this->n1 = n1; 
      n1->addEdge(this);

      this->n2 = n2; 
      n2->addEdge(this);
      
      has_right = false;
      right_t1 = -1; right_t2 = -1;
      has_left = false;
      left_t1 = -1; left_t2 = -1;
      m_dEgdeRecError = -1;

    }

    //~DiscreteDivergenceSkeletonEdge() { /*delete curve;*/ }
    
    bool hasRight() { return has_right; }
    void setRightSegment(double t1, double t2){
      right_t1 = t1;
      right_t2 = t2;
      
      has_right=true;
    }

    bool hasLeft() { return has_left; }
    void setLeftSegment(double t1, double t2){
      left_t1 = t1;
      left_t2 = t2;

      has_left = true;
    }

    double leftT1() { return left_t1; }
    double leftT2() { return left_t2; }
    double rightT1() { return right_t1; }
    double rightT2() { return right_t1; }

    double getRightSegLength() {
      return getSegDist(right_t1, right_t2);
    }

    double getLeftSegLength() {
      return getSegDist(left_t1, left_t2);
    }

    void print_left_right_seg(Curve *c){
      Point lp1 = c->atT(left_t1);
      Point lp2 = c->atT(left_t2);
      Point rp1 = c->atT(right_t1);
      Point rp2 = c->atT(right_t2);

      std::cerr << "left: " << lp1 << " to " << lp2;
      std::cerr << " [" << left_t1 << ", " << left_t2 << "] " <<getLeftSegLength()<< " \n";
      std::cerr << "right: " << rp1 << " to " << rp2;
      std::cerr << " [" << right_t1 << ", " << right_t2 << "] "<<getRightSegLength()<< "\n";
    }

    void setCurve(Curve *c){ curve = c; }

    void setSkeleton(DiscreteDivergenceSkeleton *sk){ dds = sk; }
    FluxPointList &getFluxPoints() { return flux_points; }
    DiscreteDivergenceSkeletonNode *getN1() { return n1; }
    DiscreteDivergenceSkeletonNode *getN2() { return n2; }
    DDSkeleton *getSkeleton() { return dds; }
  };

  /////////////////////////////////////////////////////////////////
  //////////////////////////// class DiscreteDivergenceSkeleton 
  class DiscreteDivergenceSkeleton {
  protected:
    DivergenceMap *dm;
    Shape *shape;
    std::vector<DiscreteDivergenceSkeletonEdge *> edges;
    std::vector<DiscreteDivergenceSkeletonNode *> nodes;

  public:
    DiscreteDivergenceSkeleton(){
        dm = NULL;
        shape = NULL;
    }
    
    DiscreteDivergenceSkeleton(DivergenceMap &dm){
        Create(dm);      
    }
    
    virtual void Create(DivergenceMap &dm) {
        this->dm = dm.clone();
        shape = dm.getShape();
    }

    void addNode(DiscreteDivergenceSkeletonNode *n){
      nodes.push_back(n);
      n->setSkeleton(this);
    }

    void addEdge(DiscreteDivergenceSkeletonEdge *e){
      edges.push_back(e);
      e->setSkeleton(this);
      e->setCurve(shape->getCurves()->front());
    }

    std::vector<DiscreteDivergenceSkeletonEdge *> &getEdges(){return edges;}
    std::vector<DiscreteDivergenceSkeletonNode *> &getNodes(){return nodes;}

    Shape* getShape() { return shape; }
    const DivergenceMap *getDivergenceMap() { return dm; }
    
    virtual ~DiscreteDivergenceSkeleton()
    {
	std::vector<DiscreteDivergenceSkeletonEdge *>::iterator edgeIt;
	std::vector<DiscreteDivergenceSkeletonNode *>::iterator nodeIt;

	for(edgeIt = edges.begin(); edgeIt != edges.end(); edgeIt++)
		delete (*edgeIt);

	for(nodeIt = nodes.begin(); nodeIt != nodes.end(); nodeIt++)
		delete (*nodeIt);

	delete dm;
	delete shape;
    }
  };

}


#endif  /* DISCRETEDIVERGENCESKELETON_H */
