/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
// mgfunctor.h
// declaration for functor structures and classes
#if !defined( __MGFUNCTOR_H__)
#define __MGFUNCTOR_H__

#include "mg/Curve.h"
#include "mg/Point.h"
#include "mg/GelPositions.h"
#include "mg/Tolerance.h"
#include "topo/Shell.h"
#include "topo/Face.h"
#include "Common/LocateState.h"

class MGVector;
class MGPlane;
class MGCurve;

namespace mgcalc{
	/// struct MGFunctor
	/// Defines a container object which may hold objects that are output of
	/// derived functor.
	///
	/// typename Holder may be STL container or MGGroup.
	template<typename Holder>
	struct MGFunctor{
		typedef Holder  holder_type;
		typedef Holder& reference;
	protected:
		MGFunctor(reference holder)
			: m_holder(holder){}

		reference m_holder;
	};

	/// class MGContourLine
	///
	/// Contour line extraction.
	template<typename H>
	class MGContourLine : public MGFunctor<H>{
	public:
		/// pl is cutting plane
		/// pitch is the distance of between two adjacent planes.
		/// ndiv is the number of division of contours.
		MGContourLine(const MGPlane& pl, const MGVector& pitch, int ndiv, reference gp)
			: MGFunctor<holder_type>(gp), __pl(pl), __pitch(pitch), __ndiv(ndiv){}

		/// operator() takes MGSurface or MGFace as the argument.
		template <typename SurfOrFaceType>
		void operator()(SurfOrFaceType* f){
			MGPlane plane = __pl;
			for(int j = 0; j <= __ndiv; j++){
				MGSSisects isects = f->isect(plane);
				MGSSisects::iterator cur = isects.begin(), last = isects.end();
				for(; cur != last; ++cur){
					auto& ssi = isectCast<MGSSisect>(cur);
					m_holder.push_back(ssi.release_line());
				}
				plane += __pitch;
			}
		}

	private:
		const MGPlane&  __pl;    // cutting plane
		const MGVector& __pitch; // pitch
		int             __ndiv;  // how many divisions
	};

	/// class MGPointFromCurveDivide
	///
	/// Extracts points on a curve from start_point toward end_point
	/// with given interval of which is specified by number.
	/// sample:
	/// typedef std::vector<MGCurve*> container;
	/// container dest;
	/// std::for_each(src.begin(), src.end(), MGPointFromCurveDivide<container>(dest, 3)),
	/// where src is curve container.
	template<typename Holder>
	class MGPointFromCurveDivide : public MGFunctor<Holder>{
	public:
		MGPointFromCurveDivide(reference g, int n) :
		  MGFunctor<holder_type>(g), m_n(n){
			ASSERT(m_n >= 1);
		}

		void operator()(const MGCurve* c){
			double step = c->param_span() / m_n;
			double cur = c->param_s();
			for(int i = 0; i <= m_n; i++){
				m_holder.push_back(new MGPoint(c->eval(cur)));
				cur += step;
			}
		}
	private:
		int m_n; // number of intervals.
	};

	/// class MGPointFromCurveLength
	///
	/// Extracts points on a curve from start_point toward end_point
	/// with given interval of which is specified by common  
	/// length of each interval chord.
	/// sample:
	/// typedef std::vector<MGCurve*> container;
	/// container dest;
	/// std::for_each(src.begin(), src.end(), MGPointFromCurveDivide<container>(dest, 12.0)),
	/// where src is curve container.
	template<typename Holder>
	class MGPointFromCurveLength : public MGFunctor<Holder>{
	public:
		MGPointFromCurveLength(reference g, double len) :
		  MGFunctor<holder_type>(g), m_l(len){
			ASSERT(m_l > 0.);
		}

		void operator()(const MGCurve* c){
			double len =  c->length();
			if(len < MGTolerance::wc_zero()) return;
			
			double cur = c->param_s(), last = c->param_e();
			do{
				m_holder.push_back(new MGPoint(c->eval(cur)));
				cur = c->length_param(cur, m_l);
			}while(cur != last);
		}
	private:
		double m_l; // common length of chord
	};
}

#endif //__MGFUNCTOR_H__
