/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/* ***************************************************** */

// project.cpp
#include "stdafx.h"
#include "mg/Point.h"
#include "mg/Straight.h"
#include "mg/CSisects.h"
#include "topo/Shell.h"
#include "Calc/mgproject.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

namespace mgcalc{

	// 投影データをセットする
	MGProject::MGProject(
		const MGObject* objective,
		const MGObject* subjective,
		const MGVector& direction
		): m_objective(objective),
		   m_subjective(subjective),
		   m_direction(direction)
		//,  m_own(true)
	{}

	/**
	 *  @brief  投影計算を行う
	 *
	 *  成功すれば true が戻ってくる。
	 *  投影による生成物を @c result() で参照することができる。
	 */
	bool MGProject::calculate(){
		if(!m_objective || !m_subjective || m_objective == m_subjective){
			return false;
		}
		// よくわからない判定...
		if(m_direction.is_zero_vector() && !m_direction.is_null()){
			return false;
		}
		m_result.clear();

		// C++ の教科書的にはダメな実装とされる「dynamic_cast switch」文。
		// MGObject の派生クラスを作る人間がいないのでこれで構わないだろう
		if(const MGFSurface* surf = dynamic_cast<const MGFSurface*>(m_objective)){
			// surf に投影
			if(const MGPoint* point = dynamic_cast<const MGPoint*>(m_subjective)){
				return calc_sp(surf, point);
			}else if(const MGCurve* curve = dynamic_cast<const MGCurve*>(m_subjective)){
				return calc_sc(surf, curve);
			}
		}else if(const MGShell* shell = dynamic_cast<const MGShell*>(m_objective)){
			// シェルに投影
			if(const MGPoint* point = dynamic_cast<const MGPoint*>(m_subjective)){
				return calc_hp(shell, point);
			}else if(const MGCurve* curve = dynamic_cast<const MGCurve*>(m_subjective)){
				return calc_hc(shell, curve);
			}
		}
		// 該当なし扱い
		return false;
	}

	// シェルに曲線を投影する内部処理
	bool MGProject::calc_hc(
		const MGShell* shell,
		const MGCurve* curve
		){
		MGHHisects res;
		shell->project(*curve, res, m_direction);
		if(res.empty()){
			return false;
		}
		// 手作業でワールドカーブを取り出す...
		MGHHisects::iterator first = res.begin(), last = res.end();
		for(; first != last; ++first){
			MGHHisect& is = isectCast<MGHHisect>(first);
			m_result.emplace_back(is.release_line());
		}
		return true;
	}

	// 曲面に曲線を投影する内部処理
	bool MGProject::calc_sc(
		const MGFSurface* surf,
		const MGCurve*    curve
		){
		std::vector<UniqueCurve> curves;
		surf->project(*curve, curves, m_direction);
		if(curves.empty()){
			return false;
		}
		for(auto& curve: curves)
			m_result.emplace_back(curve.release());
		return true;
	}

	// シェルに点を投影する内部処理
	bool MGProject::calc_hp(
		const MGShell* shell,
		const MGPoint* point
		){
		if(m_direction.is_null()){
			// 面直 (perps)
			std::vector<MGFPoint> res = shell->perps(point->position());
			if(res.empty()){
				return false;
			}
			std::vector<MGFPoint>::iterator first = res.begin(), last = res.end();
			for(; first != last; ++first){
				m_result.emplace_back(new MGPoint(first->eval()));
			}
			return true;
		}else{
			// ベクトル (isect)
			MGStraight line(MGSTRAIGHT_UNLIMIT, m_direction, point->position());
			MGCFisects res = shell->isect(line);
			if(res.empty()){
				return false;
			}
			// 手作業で点生成
			MGCFisects::iterator  first = res.begin(), last = res.end();
			for(; first != last; ++first){
				MGCFisect& cf = isectCast<MGCFisect>(first);
				m_result.emplace_back(new MGPoint(cf.point()));
			}
			return true;
		}
	}

	// 曲面に点を投影する処理
	bool MGProject::calc_sp(
		const MGFSurface* surf,
		const MGPoint*    point
		){
		// 面直かベクトル投影かで処理が異なる
		if(m_direction.is_null()){
			// 面直 (perps)
			MGPosition_list ls = surf->perps(point->position());
			if(ls.empty()){
				return false;
			}
			// 手作業で点生成
			MGPosition_list::iterator first = ls.begin(), last = ls.end();
			for(; first != last; ++first){
				m_result.emplace_back(new MGPoint(surf->eval(*first)));
			}
			return true;
		}else{
			// ベクトル (isect)
			MGStraight line(MGSTRAIGHT_UNLIMIT, m_direction, point->position());
			MGCSisects ls = surf->isectFS(line);
			if(ls.empty()){
				return false;
			}
			// 手作業で点生成
			MGCSisects::iterator first = ls.begin(), last = ls.end();
			for(; first != last; ++first){
				MGCSisect& csi = isectCast<MGCSisect>(first);
				m_result.emplace_back(new MGPoint(csi.point()));
			}
			return true;
		}
	}

} // namespace mgcalc
