/*************************************************************************************************/
/*!
   	@file		PathTo.h
	@author 	Fanzo
*/
/*************************************************************************************************/
#pragma		once

///////////////////////////////////////////////////////////////////////////////////////////////////
//include files
#include	"iFace/iPathTo.h"

#pragma pack( push , 8 )		//set align

namespace icubic
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// preprocessor deifne

///////////////////////////////////////////////////////////////////////////////////////////////////
// type define

///////////////////////////////////////////////////////////////////////////////////////////////////
// classes define

/**************************************************************************************************
"PathToOutline" class 
**************************************************************************************************/
class PathToOutline_bs : 
	public IPathToOutline
{
// variable member
private:
	PathLine_bs			m_line_ps;
	PathQuadratic_bs	m_quadratic_ps;
	PathCubic_bs		m_cubic_ps;
	PathArc_bs			m_arc_ps;

// "IPathToOutline" interface functions
public:
//=================================================================================================
void cb_call ToOutline
		(
		iOutline&		outline , 
		const PathInfo&	pathinfo , 
		iOutlineGen&	gen , 
		const faffine&	aff
		)
{
	PathSeg*	sd	= pathinfo.m_first;
	faffine		transform;
	fvector2	pos;
	bool		begin	= false;
	
	while( sd != 0 )
	{
		if( sd->m_type == Move_PathSegType )
		{
			if( begin == true )
			{
				gen->EndOutline();
				begin = false;
			}
			PathSegMove*	ss	= ( PathSegMove* )sd;
			transform	= ss->m_transform;
			pos			= fvector2( ss->m_sx , ss->m_sy );
			gen->BeginOutline( outline , ss->m_close );
			begin	= true;
		}
		if( sd->m_type == Line_PathSegType )
		{
			if( begin == false )
			{
				gen->BeginOutline( outline , false );
				begin	= true;
			}
			PathSegLine*	ss	= ( PathSegLine* )sd;
			m_line_ps.InitializePosition
				( 
				pos.x , 
				pos.y , 
				ss->m_tx , 
				ss->m_ty , 
				aff * pathinfo.m_transform * transform
				);
			gen->GenerateOutline( &m_line_ps );
			pos	= fvector2( ss->m_tx , ss->m_ty );
		}
		else if( sd->m_type == BezierQ_PathSegType )
		{
			if( begin == false )
			{
				gen->BeginOutline( outline , false );
				begin	= true;
			}
			PathSegBezierQ*	ss	= ( PathSegBezierQ* )sd;
			m_quadratic_ps.InitializeBezierQ
				(
				pos.x , 
				pos.y , 
				ss->m_hx , 
				ss->m_hy , 
				ss->m_tx , 
				ss->m_ty , 
				aff * pathinfo.m_transform * transform
				);
			gen->GenerateOutline( &m_quadratic_ps );
			pos	= fvector2( ss->m_tx , ss->m_ty );
		}
		else if( sd->m_type == BezierC_PathSegType )
		{
			if( begin == false )
			{
				gen->BeginOutline( outline , false );
				begin	= true;
			}
			PathSegBezierC*	ss	= ( PathSegBezierC* )sd;
			m_cubic_ps.InitializeBezierC
				(
				pos.x , 
				pos.y , 
				ss->m_h0x , 
				ss->m_h0y , 
				ss->m_h1x , 
				ss->m_h1y , 
				ss->m_tx , 
				ss->m_ty , 
				aff * pathinfo.m_transform * transform
				);
			gen->GenerateOutline( &m_cubic_ps );
			pos	= fvector2( ss->m_tx , ss->m_ty );
		}
		else if( sd->m_type == ArcAngle_PathSegType )
		{
			if( begin == false )
			{
				gen->BeginOutline( outline , false );
				begin	= true;
			}
			PathSegArcAngle*	ss	= ( PathSegArcAngle* )sd;
			m_arc_ps.InitializeEllipseAngle
				(
				pos , 
				fvector2( ss->m_cx , ss->m_cy ) , 
				ss->m_dy , 
				ss->m_angle , 
				aff * pathinfo.m_transform * transform
				);
			gen->GenerateOutline( &m_arc_ps );
			pos	= m_arc_ps.Value( 1.0f );
		}
		else if( sd->m_type == ArcSweep_PathSegType )
		{
			if( begin == false )
			{
				gen->BeginOutline( outline , false );
				begin	= true;
			}
			PathSegArcSweep*	ss	= ( PathSegArcSweep* )sd;
			if( true == m_arc_ps.InitializeArc
						(
						pos , 
						fvector2( ss->m_tx , ss->m_ty ) ,
						ss->m_rx ,
						ss->m_ry , 
						ss->m_x_axis_rot , 
						ss->m_large , 
						ss->m_sweep , 
						aff * pathinfo.m_transform * transform
						) )
			{
				gen->GenerateOutline( &m_arc_ps );
				pos	= m_arc_ps.Value( 1.0f );
			}
			else
			{
				m_line_ps.InitializePosition
					( 
					pos.x , 
					pos.y , 
					ss->m_tx , 
					ss->m_ty , 
					aff * pathinfo.m_transform * transform
					);
				gen->GenerateOutline( &m_line_ps );
				pos	= m_arc_ps.Value( 1.0f );
			}
		}
		sd	= sd->m_next;
	}
	if( begin == true )
		gen->EndOutline();
}
// public functions
//=================================================================================================
PathToOutline_bs()
{
}
};
/**************************************************************************************************
"PathToOutline" class 
**************************************************************************************************/
class PathToOutline : 
	virtual public object_base , 
	public PathToOutline_bs
{
	query_begin();
	iface_hook( IPathToOutline , IPathToOutline_IID )
	query_end( object_base );
};
/**************************************************************************************************
"PathSegmentToOutline_bs" class 
**************************************************************************************************/
class PathSegmentToOutline_bs : 
	public IPathSegmentToOutline
{
// variable member
private:
	iOutline			m_outline;
	iOutlineGen			m_gen;
	faffine				m_affine;
	fvector2			m_startpos;
	bool				m_begin;
	
	PathLine_bs			m_line_ps;
	PathQuadratic_bs	m_quadratic_ps;
	PathCubic_bs		m_cubic_ps;
	PathArc_bs			m_arc_ps;
	
// "IPathTo" interface functions
public:
//=================================================================================================
void cb_call Move
		(
		const fvector2&		pos , 
		const faffine&		transform , 
		bool				close
		)
{
	if( m_begin == true )
		m_gen->EndOutline();
	m_begin		= true;
	m_gen->BeginOutline( m_outline , close );
	m_startpos	= pos;
	m_affine	= transform;
}
//=================================================================================================
void cb_call Move
		(
		int				pntnum , 
		const float*	pnt , 		//!< [in] sx , sy
		const faffine&	transform , 
		bool			close
		)
{
	cb_assert( pntnum % 2 == 0 , L"pntnum is invalid." );
	Move( fvector2( pnt[0] , pnt[1] ) , transform , close );
}
//=================================================================================================
void cb_call Line
		(
		const fvector2&		tp
		)
{
	Line( 1 , &tp );
}
//=================================================================================================
void cb_call Line
		(
		int					pntnum , 
		const fvector2*		pnt			//!< [in] tp,tp,...
		)
{
	cb_assert( m_begin == true , L"Line called before Move" );
	int		pntoff;
	for( pntoff = 0 ; pntoff < pntnum ; pntoff++ )
	{
		m_line_ps.InitializePosition( m_startpos , *pnt , m_affine );
		m_gen->GenerateOutline( &m_line_ps );
		m_startpos	= *pnt;
		pnt++;
	}
}
//=================================================================================================
void cb_call Line
		(
		int				pntnum , 
		const float*	pnt				//!< [in] tx , ty , ...
		)
{
	cb_assert( m_begin == true , L"Line called before Move" );
	cb_assert( pntnum % 2 == 0 , L"pntnum is invalid." );

	int		pntoff;
	pntnum /= 2;
	for( pntoff = 0 ; pntoff < pntnum ; pntoff++ )
	{
		m_line_ps.InitializePosition( m_startpos.x , m_startpos.y , *pnt , *(pnt+1) , m_affine );
		m_gen->GenerateOutline( &m_line_ps );
		m_startpos	= fvector2( *pnt , *(pnt+1) );
		pnt+=2;
	}
}
//=================================================================================================
void cb_call BezierQ
		(
		const fvector2&		hp , 
		const fvector2&		tp
		)
{
	fvector2	pnt[] = { hp , tp };
	BezierQ( 2 , pnt );
}
//=================================================================================================
void cb_call BezierQ
		(
		int				pntnum , 
		const fvector2*	pnt			//!< [in] hp , tp , ...
		)
{
	cb_assert( m_begin == true , L"BezierQ called before Move" );
	cb_assert( pntnum % 2 == 0 , L"pntnum is invalid." );

	pntnum /= 2;
	int		pntoff;
	for( pntoff = 0 ; pntoff < pntnum ; pntoff++ )
	{
		m_quadratic_ps.InitializeBezierQ( m_startpos , *pnt , *(pnt+1) , m_affine );
		m_gen->GenerateOutline( &m_quadratic_ps );
		m_startpos	= *(pnt+1);
		pnt+=2;
	}
}
//=================================================================================================
void cb_call BezierQ
		(
		int				pntnum , 
		const float*	pnt		//!< [in] hx , hy , tx , ty...
		)
{
	cb_assert( m_begin == true , L"BezierQ called before Move" );
	cb_assert( pntnum % 4 == 0 , L"pntnum is invalid." );

	pntnum /= 4;
	int		pntoff;
	for( pntoff = 0 ; pntoff < pntnum ; pntoff++ )
	{
		m_quadratic_ps.InitializeBezierQ( m_startpos.x , m_startpos.y , *pnt , *(pnt+1) , *(pnt+2) , *(pnt+3) , m_affine );
		m_gen->GenerateOutline( &m_quadratic_ps );
		m_startpos	= fvector2( *(pnt+2) , *(pnt+3) );
		pnt+=4;
	}
}
//=================================================================================================
void cb_call BezierC
		(
		const fvector2&		h0p , 
		const fvector2&		h1p , 
		const fvector2&		tp
		)
{
	fvector2	pnt[] = { h0p , h1p , tp };
	BezierC( 3 , pnt );
}
//=================================================================================================
void cb_call BezierC
		(
		int					pntnum , 
		const fvector2*		pnt			//!< [in] h0p , h1p , tp , ...
		)
{
	cb_assert( m_begin == true , L"BezierC called before Move" );
	cb_assert( pntnum % 3 == 0 , L"pntnum is invalid." );

	pntnum /= 3;
	int		pntoff;
	for( pntoff = 0 ; pntoff < pntnum ; pntoff++ )
	{
		m_cubic_ps.InitializeBezierC( m_startpos , *pnt , *(pnt+1) , *(pnt+2) , m_affine );
		m_gen->GenerateOutline( &m_cubic_ps );
		m_startpos	= *(pnt+2);
		pnt+=3;
	}
}
//=================================================================================================
void cb_call BezierC
		(
		int				pntnum , 
		const float*	pnt		//!< [in] h0x , h0y , h1x , h1y , tx , ty...
		)
{
	cb_assert( m_begin == true , L"BezierC called before Move" );
	cb_assert( pntnum % 6 == 0 , L"pntnum is invalid." );

	pntnum /= 6;
	int		pntoff;
	for( pntoff = 0 ; pntoff < pntnum ; pntoff++ )
	{
		m_cubic_ps.InitializeBezierC( m_startpos.x , m_startpos.y , *pnt , *(pnt+1) , *(pnt+2) , *(pnt+3) , *(pnt+4) , *(pnt+5) , m_affine );
		m_gen->GenerateOutline( &m_cubic_ps );
		m_startpos	= fvector2( *(pnt+4) , *(pnt+5) );
		pnt+=6;
	}
}
//=================================================================================================
void cb_call Arc
		(
		const fvector2&		cp , 
		float				dy , 
		float				angle
		)
{
	cb_assert( m_begin == true , L"Arc called before Move" );
	
	m_arc_ps.InitializeEllipseAngle( m_startpos , cp , dy , angle , m_affine );
	m_gen->GenerateOutline( &m_arc_ps );
	m_startpos			= m_affine.Inverse().Transform( m_arc_ps.Value( 1.0f ) );
}
//=================================================================================================
void cb_call Arc
		(
		float				rx , 
		float				ry , 
		float				x_axis_rot , 
		bool				large , 
		bool				sweep , 
		const fvector2&		tp
		)
{
	if( true == m_arc_ps.InitializeArc( m_startpos , tp , rx , ry , x_axis_rot , large , sweep , m_affine ) )
		m_gen->GenerateOutline( &m_arc_ps );
	else
	{
		m_line_ps.InitializePosition( m_startpos , tp , m_affine );
		m_gen->GenerateOutline( &m_line_ps );
	}
	m_startpos	= tp;
}
//=================================================================================================
fvector2 cb_call GetEndPos()const
{
	return m_startpos;
}

// "ISegmentToOutline" interface functions
public:
//=================================================================================================
void cb_call Begin
		(
		iOutline&		outline , 
		iOutlineGen&	gen
		)
{
	if( m_begin == true )
		End();
	m_begin		= false;
	m_outline	= outline;
	m_gen		= gen;
	m_affine	= faffine();
}
//=================================================================================================
void cb_call CustomPath
		(
		iPathSegment&	ps		//!< [in] it need to effect affine.
		)
{
	m_gen->GenerateOutline( ps );
	m_startpos	= ps->Value( 1.0f );
}
//=================================================================================================
fvector2 cb_call End()
{
	if( m_begin == true )
		m_gen->EndOutline();
	m_begin	= false;
	m_gen.release();
	m_outline.release();
	return m_startpos;
}
// public functions
public:
//=================================================================================================
PathSegmentToOutline_bs() : m_begin( false )
{
}
};
/**************************************************************************************************
"PathSegmentToOutline" class 
**************************************************************************************************/
class PathSegmentToOutline : 
	virtual public object_base , 
	public PathSegmentToOutline_bs
{
	query_begin();
	iface_hook( IPathSegmentToOutline , IPathSegmentToOutline_IID )
	query_end( object_base );
};
/**************************************************************************************************
"PathToEdgemap_bs" class 
**************************************************************************************************/
class PathToEdgemap_bs : 
	public IPathToEdgemap
{
	cb_copy_impossible( PathToEdgemap_bs );
	
// member class
private:
	class Allocator : public IMemAllocLump
	{
		MemAllocLump_inc	m_allocator;
	public:
		Allocator() : m_allocator( 1024 ){}
		void* cb_call Allocate
				(
				uint32		size
				)
		{
			return m_allocator.Allocate( size );
		}
		~Allocator()
		{
			m_allocator.DeallocateAll();
		}
		void cb_call DeallocateAll()
		{
		}
		void Reset()
		{
			m_allocator.DeallocateAll();
		}
	};
// variable member
private:
	Allocator				m_allocator;
	PathToOutline_bs		m_to_outline;
	instance<Outline>	m_outline;

// private functions
private:
//=================================================================================================
//!	node to edgemap
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void NodeToEdgemap
		(
		IEdgemapOutline*	edgemap , 
		const PathNode*		node , 
		const faffine&		affine ,
		iOutlineGen&		gen , 
		PathFillRule		rule , 
		const faffine&		ltod , 
		const irect&		area , 
		antialias			anti , 
		bool*				boundbox_f , 
		frect*				boundbox , 
		int					callcount
		)
{
	if( node == 0 || edgemap == 0 )
		return;
	if( --callcount < 0 )
		return;
	if( node->m_type == Factor_PathNodeType )
	{
		PathNodeFactor*	e = ( PathNodeFactor* )node;
		m_outline->Reset();
		m_to_outline.ToOutline( (iOutline)m_outline , e->m_info , gen , affine );
		
		OutlineInfo		oi;
		if( true == m_outline->GetOutlineInfo( &oi ) && boundbox != 0 && boundbox_f != 0 )
		{
			if( *boundbox_f == true )
				*boundbox	|= oi.m_boundbox;
			else
			{
				*boundbox	= oi.m_boundbox;
				*boundbox_f	= true;
			}
		}
		edgemap->BeginOutline();
		edgemap->SetOutline( (iOutline)m_outline , ltod );
		edgemap->EndOutline( rule );
	}
	else if( node->m_type == Expr_PathNodeType )
	{
		PathNodeExpr*		e = ( PathNodeExpr* )node;

		//left
		NodeToEdgemap( edgemap , e->m_left , e->m_transform * affine , gen , rule , ltod , area , anti , boundbox_f , boundbox , callcount );
		
		// right
		EdgemapOutline_bs	r_edgemap( &m_allocator );
		r_edgemap.Initialize( anti , area );
		NodeToEdgemap( &r_edgemap , e->m_right , e->m_transform * affine , gen , rule , ltod , area , anti , boundbox_f , boundbox , callcount );
		
		// logic
		edgemap->Operate( e->m_ope , &r_edgemap );
	}
}
// "IPathToEdgemap" functions
public:
//=================================================================================================
void cb_call ToEdgemap
		(
		IEdgemapOutline*	edgemap , 
		IPathLogicInfo*		path , 			//!< [in]  convert path
		const faffine&		affine , 		//!< [in]  convert affine
		iOutlineGen&		gen ,  			//!< [in]  outline gen
		PathFillRule		rule ,
		const faffine&		ltod ,			//!< [in]  logical to device
		const irect&		area , 
		antialias			anti , 
		frect*				boundbox = 0
		)
{
	edgemap->Initialize( anti , area );
	const PathLogicInfo*	info = path->GetPathLogicInfo();

	bool	bb_f	= false;
	frect	bb;
	NodeToEdgemap( edgemap , info->m_node , affine , gen , rule , ltod , area , anti , &bb_f , &bb , 20 );
	m_allocator.Reset();
	
	store( boundbox , bb );
	return;	
}
//=================================================================================================
void cb_call ToEdgemap
		(
		IEdgemapOutline*	edgemap , 
		IPathLogicInfo*		path ,
		const faffine&		path_affine ,
		iOutlineGen&		path_gen ,
		PathFillRule		path_rule ,
		IPathLogicInfo*		clip_path ,
		const faffine&		clip_affine ,
		iOutlineGen&		clip_gen ,
		PathFillRule		clip_rule ,
		const faffine&		ltod , 
		const irect&		area , 
		antialias			anti , 
		frect*				boundbox = 0
		)
{
	edgemap->Initialize( anti , area );
	
	frect	bb;
	{
		bool	bb_f	= false;
		const PathLogicInfo*	path_info = path->GetPathLogicInfo();
		NodeToEdgemap( edgemap , path_info->m_node , path_affine * path_info->m_transform , path_gen , path_rule , ltod , area , anti , &bb_f , &bb , 20 );
		m_allocator.Reset();
	}

	if( clip_path != 0 )
	{
		{
			const PathLogicInfo*	clip_info = clip_path->GetPathLogicInfo();
			EdgemapOutline_bs	r_edgemap( &m_allocator );
			r_edgemap.Initialize( anti , area );
			NodeToEdgemap( &r_edgemap , clip_info->m_node , clip_affine * clip_info->m_transform , clip_gen , clip_rule , ltod , area , anti , 0 , 0 , 20 );
			edgemap->Operate( And_PathOperator , &r_edgemap );
		}
		m_allocator.Reset();
	}
	store( boundbox , bb );
	return;	
}
// public functions
public:
//=================================================================================================
PathToEdgemap_bs()
{
}
};
/**************************************************************************************************
"PathToEdgemap" class 
**************************************************************************************************/
class PathToEdgemap : 
	virtual public object_base , 
	public PathToEdgemap_bs
{
	query_begin();
	iface_hook( IPathToEdgemap , IPathToEdgemap_IID )
	query_end( object_base );
};
/**************************************************************************************************
"OutlineToOutline" class 
**************************************************************************************************/
class OutlineToOutline_bs : 
	public IOutlineToOutline
{
private:
	PathLine_bs		m_line_ps;

// "IOutlineToOutline" interface functions
public:
//=================================================================================================
//!	Outline
//!	@retval			---
//-------------------------------------------------------------------------------------------------
void cb_call ToOutline
		(
		iOutline&		out_outline , 	//!< [mod] output outline
		iOutline&		in_outline , 	//!< [in] input outline
		const faffine&	affine , 
		iOutlineGen&	gen
		)
{
	OutlineInfo		oi;
	if( false == in_outline->GetOutlineInfo( &oi ) )
		return;

	const OutlineSeg*	os = oi.m_first;
	if( os == 0 )
		return;

	while( os != 0 )
	{
		const OutlineNode	*on	= os->m_node;
		
		if( on != 0 )
		{
			gen->BeginOutline( out_outline , true );
			
			fvector2	startpos	= fvector2( on->x , on->y );
			on			= on->m_next;
			while( on != 0 )
			{
				fvector2	tp( on->x , on->y );
				PathLine	ps;
				m_line_ps.InitializePosition( startpos , tp , affine );
				gen->GenerateOutline( &m_line_ps );
				
				startpos	= tp;
				on			= on->m_next;
			}
			gen->EndOutline();
		}
		os	= os->m_next;
	}
}
// public functions
public:
//=================================================================================================
OutlineToOutline_bs()
{
}
};
/**************************************************************************************************
"OutlineToOutline" class 
**************************************************************************************************/
class OutlineToOutline : 
	virtual public object_base , 
	public OutlineToOutline_bs
{
	query_begin();
	iface_hook( IOutlineToOutline , IOutlineToOutline_IID )
	query_end( object_base );
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// global variable define

///////////////////////////////////////////////////////////////////////////////////////////////////
// global functions define

};	//namespace

//using namespace icubic;		

#pragma pack( pop )			//release align
