/**------------------------------------------------------------------------
 * @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
 * 
 * $Log: Skeleton.h,v $
 *
 *
 *-----------------------------------------------------------------------*/

#include <LEDA/core/d_array.h>
#include "BlumSkeleton.h"
#include "BasicUtils.h"

using namespace sg;
using namespace dml;

void McGillSkeleton::ReadNodesAndEdges(std::istream& is, DDSkeleton& dds)
{
	DDSNode* n;
	FluxPoint fp;
	leda_d_array<long, DDSNode*> map;
	int i, j, size, fp_count;
	double lt1, lt2, rt1, rt2;
	void *key, *keyN1, *keyN2;
	
	is.read((char*) &size, sizeof(size));
	
	for (i = 0; i < size; i++) 
	{
		is.read((char*) &key, sizeof(key));
		is.read((char*) &fp, sizeof(fp));

		n =  new DDSNode(fp);

		map[(long)key] = n;
		dds.addNode(n);
	}

	is.read((char*) &size, sizeof(size));

	for (i = 0; i < size; i++) 
	{
		is.read((char*) &keyN1, sizeof(keyN1));
		is.read((char*) &keyN2, sizeof(keyN2));

		is.read((char*) &lt1, sizeof(lt1));
		is.read((char*) &lt2, sizeof(lt2));
		is.read((char*) &rt1, sizeof(rt1));
		is.read((char*) &rt2, sizeof(rt2));

		is.read((char*) &fp_count, sizeof(fp_count));
		FluxPointList fpl;

		for (j = 0; j < fp_count; j++) 
		{
			is.read((char*) &fp, sizeof(fp));
			fpl.push_back(fp);
		}

		DDSEdge* e = new DDSEdge(&dds, fpl, map[(long)keyN1], map[(long)keyN2]);

		if (lt1 != -1 || lt2 != -1) e->setLeftSegment(lt1, lt2);
		if (rt1 != -1 || rt2 != -1) e->setRightSegment(rt1, rt2);

		dds.addEdge(e);
	}
}

void McGillSkeleton::WriteNodesAndEdges(std::ostream& os, DDSkeleton& dds)
{
	DDSNodeVect::iterator I;
	DDSEdgeVect::iterator II;
	FluxPointList::iterator III;

	DDSNodeVect& nodes = dds.getNodes();
	DDSEdgeVect& edges = dds.getEdges();

	DDSNode *n, *n1, *n2;
	DDSEdge *e;
	FluxPoint fp;

	int size = nodes.size();
	os.write((char*) &size, sizeof(size));
	
	for(I = nodes.begin(); I != nodes.end(); I++)
	{
        n = *I;
		os.write((char*) &n, sizeof(n));
		os.write((char*) &n->fp, sizeof(n->fp));
	}
	
	size = edges.size();
	os.write((char*) &size, sizeof(size));

	for(II = edges.begin(); II != edges.end(); II++)
	{
		e = *II;
		n1 = e->getN1();
		n2 = e->getN2();
		os.write((char*) &n1, sizeof(n1));
		os.write((char*) &n2, sizeof(n2));
		os.write((char*) &e->left_t1, sizeof(e->left_t1));
		os.write((char*) &e->left_t2, sizeof(e->left_t2));
		os.write((char*) &e->right_t1, sizeof(e->right_t1));
		os.write((char*) &e->right_t2, sizeof(e->right_t2));

		FluxPointList& fpl = e->getFluxPoints();

		size = fpl.size();
		os.write((char*) &size, sizeof(size));

		for(III = fpl.begin(); III != fpl.end(); III++)
		{
			fp = *III;
			os.write((char*) &fp, sizeof(fp));
		}
	}

}

DDSkeleton* McGillSkeleton::ReadDDSkeleton(std::istream& is)
{
	LineSeg* pLineSeg;
	bool closed;
	double xmin, xmax, ymin, ymax;
	int size;
	double startLength;
	Point startPt, endPt;

	//Read Contour
	is.read((char*) &xmin, sizeof(xmin));
	is.read((char*) &xmax, sizeof(xmax));
	is.read((char*) &ymin, sizeof(ymin));
	is.read((char*) &ymax, sizeof(ymax));

	std::vector<CurveSeg*> segments;
	is.read((char*) &size, sizeof(size));

    for(int i = 0; i < size; i++)
	{
		is.read((char*) &startLength, sizeof(startLength));
		is.read((char*) &startPt, sizeof(startPt));
		is.read((char*) &endPt, sizeof(endPt));

		pLineSeg = new LineSeg(startPt, endPt, startLength);
		segments.push_back(pLineSeg);
	}

	is.read((char*) &closed, sizeof(closed));
	DiscreteSegCurve c(segments, closed);

	SimpleShape ss(&c, xmin, xmax, ymin, ymax);
	DistanceTransform dt(&ss);
	DivergenceMap dm(dt);
	DDSkeleton* dds = new DDSkeleton(dm);

	// Read all nodes and edges
	ReadNodesAndEdges(is, *dds);
	
	// Read piecewise skeletion approximation
	m_lineSegments.ReadRecursive(is);

	return dds;
}

void McGillSkeleton::WriteDDSkeleton(std::ostream& os, DDSkeleton& dds)
{
	SimpleShape* ss = (SimpleShape*)dds.getShape();
	Curve* p = *(ss->getCurves()->begin());
	DiscreteSegCurve* c = dynamic_cast<DiscreteSegCurve*>(p);
	LineSeg* pLineSeg;
	double xmin, xmax, ymin, ymax;
	bool closed;

	ASSERT(c);

	//Read Contour
	ss->getBounds(&xmin, &xmax, &ymin, &ymax);
	os.write((char*) &xmin, sizeof(xmin));
	os.write((char*) &xmax, sizeof(xmax));
	os.write((char*) &ymin, sizeof(ymin));
	os.write((char*) &ymax, sizeof(ymax));

	int size = c->segments.size();
	os.write((char*) &size, sizeof(size));

	std::vector<CurveSeg*>::iterator I;
	for(I = c->segments.begin(); I != c->segments.end(); I++)
	{
		pLineSeg = dynamic_cast<LineSeg*>(*I);
		ASSERT(pLineSeg);

		os.write((char*) &pLineSeg->startLength, sizeof(pLineSeg->startLength));
		os.write((char*) &pLineSeg->startPt, sizeof(pLineSeg->startPt));
		os.write((char*) &pLineSeg->endPt, sizeof(pLineSeg->endPt));
	}

	closed = c->isClosed();
	os.write((char*) &closed, sizeof(closed));

	// Write all nodes and edges
	WriteNodesAndEdges(os, dds);
	
	// Write piecewise skeletion approximation
	m_lineSegments.WriteRecursive(os);
}

void McGillSkeleton::ApproxSkeletonWithLines(double dMinError, double dMinSlope, int nMaxSegments, double dMaxYDiff)
{
	std::vector<DiscreteDivergenceSkeletonEdge*>::iterator edgeIt;
	int i, n;
	
	ASSERT(pMAT);
	
	DDSEdgeVect& edges = pMAT->getEdges();
	
	m_lineSegments.ReSize(edges.size());

	for(edgeIt = edges.begin(), n = 0; edgeIt != edges.end(); edgeIt++, n++)
	{
		FluxPointList& flux_points = (*edgeIt)->getFluxPoints();
		
		POINTS data(flux_points.size());
		
		for (i = 0; i < data.GetSize(); i++)
			data[i].Set(flux_points[i].p.x, flux_points[i].p.y);
		
		ApproxPolyTLS poly(data.GetSize() / dMinError, dMinSlope, nMaxSegments);
		
		poly.Fit(data);
		
		// Copy all the line segments
		m_lineSegments[n].ReSize(poly.m_knots.GetSize());
		
		for (int i = 0; i < poly.m_knots.GetSize(); i++)
			m_lineSegments[n][i] = poly.m_knots[i].seg;
	}
}

