/**------------------------------------------------------------------------
 * @file
 *
 * Copyright 2001 by the University of Toronto (UT).
 *
 *                       All Rights Reserved
 * 
 * Permission to copy and modify this software and its documentation 
 * only for internal use in your organization is hereby granted, 
 * provided that this notice is retained thereon and on all copies.  
 * UT makes no representations as to the suitability and operability 
 * of this software for any purpose.  It is provided "as is" without 
 * express or implied warranty.
 *
 * UT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
 * SHALL UT BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * No other rights, including for example, the right to redistribute 
 * this software and its documentation or the right to prepare 
 * derivative works, are granted unless specifically provided in a 
 * separate license agreement.
 *
 * Author: Diego Macrini based on Ali Shokoufandeh's original code
 * 
 * $Log: ShockGraph.h,v $
 * Revision 1.3  2002/06/10 18:37:33  dmac
 * New class that handles the node distance function added.
 *
 * Revision 1.2  2002/05/16 22:50:11  dmac
 * Several bugs fixed and a new DAG-derived class added
 *
 * Revision 1.1.1.1  2002/03/10 07:38:07  dmac
 * Imported sources
 *
 *
 *-----------------------------------------------------------------------*/

#ifndef __SHOCK_GRAPH_H__
#define __SHOCK_GRAPH_H__

// VC++6 -> Include CImg before DAG.h
//#include "../CImg/CImg.h"

#include "DAG.h"
#include "SGNode.h"
#include "BlumSkeleton.h"
#include "treeskel.h"
#include <sg.h>

namespace dml {

class ShockGraph;

//! /see template<class T> SmartPtr.
typedef SmartPtr<ShockGraph> SGPtr;
//typedef SmartPtr< cimg_library::CImg<float> > ImgPtr;

// SG computation params
struct SGCP
{
	int nSlipt3s;
	double dMinSlope, dMinError, dSkTreshold, dMaxYDiff;
	double dMaxAccelChg, tsTau1, tsTau2;
	int nBndryRecErrorWeight, nStrucRecErrorWeight;
	int nSimplifyExternal, nSimplifyComplete, nSimplifyInternal;
	
	SGCP() { memset(this, 0, sizeof(SGCP)); }
};

struct IMAGEINFO
{
	String strFileName;
	//ImgPtr ptrImg;
	FIELD<float>* pField;
	POINTS boundaryPts;
	double noiseLevel;
	
	IMAGEINFO() { noiseLevel = 0; pField = NULL; }
};

/*!
\brief Represents a shock graph consisting of <SGNode *>'s. 

	Will be further extended by various local-to-global transformations, etc.
	\see DAG, SGNode, DAGNode.
*/
class ShockGraph: public DAG
{
	int nLastIndexUsed;
	const char* GetNextIdx();
	BlumSkeleton* m_pSkeleton;

	SGCP compParams;
	POINTS m_boundaryPts;

public:

	enum BRANCH_DIR {IN_DIR, OUT_DIR};

/** The possibly approximate area of the corresponding region.
	In case of approximation, obtained as the sum of all radii. */
	double area;
	
	/** The possibly approximate perimeter of the corresponding region.
	In case of approximation, obtained as the number of skeleton pts. */
	double perimeter;
	
	double xmin, xmax, ymin, ymax;
	
	double Width() const { return xmax - xmin; }
	double Height() const { return ymax - ymin; }
	
	int NodeType(leda_node v) const { return GetSGNode(v)->m_nType; }
	NODE_ROLE NodeRole(leda_node v) const { return GetSGNode(v)->GetNodeRole(); }
	int NodeLength(leda_node v) const {	return GetSGNode(v)->m_shocks.GetSize(); }
	const POINTS& GetBoundaryPts() const { return m_boundaryPts; }

	int GetBranchDir(leda_node u, leda_node wrtV) const;
	const ShockInfo& GetJointPoint(leda_node u, leda_node v) const;
	
	ShockGraph();
	~ShockGraph();
	
	bool ComputeFromPPMFile(const char* szFileName, 
		double cutoff = .15, double sigma = 2, double range = 3);
	
	bool ComputeFromPPMFile2(const char* szFileName, const SGCP& sgparams);
	
	// begin Tree Skell Interface
	bool ComputeFromPPMFile3(const char* szPPMFileName, const SGCP& sgparams);
	bool ComputeFromPPMFile3(const IMAGEINFO& imageInfo, const SGCP& sgparams);
	static McGillSkeleton* TranslateTreeSkelIntoDDS(TreeSkel* ts, const SGCP& sgparams);
	static void ConnectTreeSkelNodes(sg::DDSkeleton*, const TreeSkel::TreeBranch&, const TreeSkel::TreePoint*, sg::DDSNode*, bool);
	static sg::FluxPoint TranslateTreePtIntoFluxPt(const TreeSkel::TreePoint* pTreePt);
	static  void ShowBranchInfo(sg::DDSEdge* , sg::DDSNode* , int);
	// end

	bool RecomputeFromSkeleton(const SGCP& sgparams);
	
	sg::DDSkeleton* ComputeDDSkeleton(const char* szPPMFileName);
	bool ComputeSGFromDDSkeleton(sg::DDSkeleton* sk);
	
	SGNode* CopyNodeInfo(sg::DDSEdge* ddsEdge, leda_node u, SGNode* pNode);
	void LabelEndPoints(SGNode* pNode, sg::FluxPointList& fpl, sg::DDSNode* n1, sg::DDSNode* n2);
	SGNode* GroupShockPoints(leda_node u, SGNode* pNode);
	SGNode* SplitNode(leda_node v, SGNode* pNode, int nEnd, SEGMENT& leftSeg, SEGMENT& rightSeg);
	void ConnectNodes(leda_node u, SGNode* pNode, sg::DDSEdge* e, sg::DDSNode* n);

	void RevertEdge(leda_edge pxEdge);

	void DetectLigatureNodes(sg::DDSkeleton* sk);
	void Insert4sAnd2s();
	void TryRelabel3As2or4(leda_node v);
	void DirectEdgesAcordingToTime();
	void FinalizeSGConstruction();
	
	void ComputeObjectViewName();
	void ComputeNodeRole(leda_node v);
	
	GRAMMAR_RULE GetRewriteRule(leda_node r) const;
	bool CheckChildNodeTypes(leda_node r, SmartArray<int> validTypes, const ShockInfo* pInvJointPt = NULL) const;
	leda_node CheckGrammar(leda_node root = nil) const;
	
	sg::DiscreteDivergenceSkeletonEdge* GetGreatestUntouched (leda_d_array<long, leda_node>& map, 
		std::vector<sg::DiscreteDivergenceSkeletonEdge*> edges);
	
	SGNode* SplitSGNode(SGNode* pNode, leda_d_array<long, leda_node>& map);
	
	void DrawEdges(sg::DiscreteDivergenceSkeletonEdge* SKparent, 
		sg::DiscreteDivergenceSkeletonNode* node, leda_d_array<long, leda_node>& map, SGNode* SGparent);
	int ComputeNodeLabel(const SGNode *node) const;
	
	NODE_LABEL GetNodeLbl(leda_node v) const;
	
	// DAG virtual functions
	virtual DAG& operator=(const DAG& rhs);
	virtual ShockGraph& operator=(const ShockGraph& rhs);
	
	virtual void Clear();
	virtual void ComputeDerivedValues();
	
	// DAG pure virtual functions
	virtual double NodeDistance(leda_node g1Node, const DAG& g2, leda_node g2Node) const;
	virtual bool AreNodesRelated(leda_node g1Node, const DAG& g2, leda_node g2Node) const;
	virtual DAG* CreateObject() const;
	virtual DAGNodePtr CreateNodeObject(NODE_LABEL lbl) const;
	virtual DAGNodePtr ReadNode(std::istream& is) const;
	virtual String ClassName() const;
	
	virtual std::istream& Read(std::istream& is, bool bOnlyDataForMatching = false);
	virtual std::ostream& Write(std::ostream& os) const;
	virtual void Print(std::ostream& os = std::cout) const;
	
	const BlumSkeleton* GetSkeleton() const { return m_pSkeleton; }
	void SetSkeleton(BlumSkeleton* pSkeleton) { m_pSkeleton = pSkeleton; }
	
	const SGNode* GetSGNode(leda_node v) const { return (const SGNode*)(const DAGNode*)GetNode(v); }
	SGNode* UnsafeGetSGNode(leda_node v) { return (SGNode*)GetSGNode(v); }
};

} //namespace dml

#endif //__SHOCK_GRAPH_H__
