/********************************************************************/
/* Copyright (c) 2019 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/* ***************************************************** */
/********************************************************************/
// 'From planar curves' classes member functions

#include "stdafx.h"
#include "Calc/curve.h"
#include "Calc/planar2.h"
#include "mg/Plane.h"
#include "mg/Straight.h"
#include "mg/Ellipse.h"
#include "mg/CCisects.h"
#include "mg/Group.h"
#include "topo/Face.h"

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

namespace mgcalc{
	
	// MGPlanarCurveGroup

	const int OTHER                = 0x0000;
	const int ADD_CHILD            = 0x0001;
	const int REMOVE_CHILD         = 0x0002;
	const int ADD_SIBLING          = 0x0004;
	const int MODIFY_LOOP_BEFORE   = 0x0010;
	const int MODIFY_LOOP_AFTER    = 0x0020;
	const int MODIFY_PARENT_BEFORE = 0x0040;
	const int MODIFY_PARENT_AFTER  = 0x0080;
	const int CLEAR_ALL            = 0x0100;

	// ルートを作成
	MGPlanarCurveGroup::MGPlanarCurveGroup()
		 : m_loop(0),
		   m_parent(0) // root扱い
	{
		assert(is_root());
	}

	// 子グループを生成
	MGPlanarCurveGroup::MGPlanarCurveGroup(
		const MGCurve*      loop,
		MGPlane*            plane,
		MGPlanarCurveGroup* parent // = 0 でよい
		)
		 : m_loop(loop),
		   m_plane(plane),
		   m_parent(parent)
	{
		assert(loop);
		assert(parent->is_root() || parent->loop());
		assert(!is_null());
	}

	// 子グループを追加する
	MGPlanarCurveGroup* MGPlanarCurveGroup::add_child(
		const MGCurve*  curve,
		MGPlane*        plane
		){
		assert(is_root() || m_loop); // ????
		assert(curve);
		assert(plane);

		// 要改良箇所と思われる...
		//--------------------------------------------------------------
		// 子グループだけを調べる
		iterator first = begin(), last = end();
		for(; first != last; ++first){
			MGPlanarCurveGroup* parent = (*first)->insert(curve, plane);
			if(parent){
				return parent;
			}
		}
		//--------------------------------------------------------------
		
		// どの子グループの子にもならなかったので
		// ここに追加
		m_children.emplace_back(new MGPlanarCurveGroup(curve, plane, this));
		notify(ADD_CHILD);
		
		return this;
	}

	// 親グループを挿入する
	MGPlanarCurveGroup* MGPlanarCurveGroup::add_parent(
		const MGCurve*  curve,
		MGPlane*        plane
		){
		assert(!is_null());
		assert(!is_root());
		assert(curve);
		assert(plane);

		// 1. 曲線と平面を交代
		const MGCurve* old_parent_loop = loop();
		MGPlane* old_plane = this->plane(); // 仮引数と名前がかぶった
		m_plane.release();
		set_loop(curve, plane);

		// 2. 新しくPCGを生成し、そのループが古いループとなる
		std::unique_ptr<MGPlanarCurveGroup> node(
			new MGPlanarCurveGroup(old_parent_loop, old_plane, this)
		);

		// 3. 子グループ引っ越し
		MYLST tmp;
		tmp.swap(m_children);
		notify(OTHER);
		assert(m_children.empty());
		
		m_children.emplace_back(node.release());
		notify(ADD_CHILD);
		node->m_children.swap(tmp);
		node->notify(OTHER);
		assert(m_children.size() == 1);
		assert(tmp.empty());

		// 5. 新しいPCGのすべての子グループの
		// 親グループを更新
		iterator first = node->begin(), last = node->end();
		for(; first != last; ++first){
			(*first)->set_parent(node.get());
		}
		// VC++ の std::mem_fun が使いものにならない

		// 親を返す
		return parent();
	}

	// 兄弟グループを追加する
	MGPlanarCurveGroup* MGPlanarCurveGroup::add_sibling(
		const MGCurve*  curve,
		MGPlane*        plane
		){
		assert(!is_null());
		assert(curve);
		assert(plane);

		MGPlanarCurveGroup* parent_group = parent();
		assert(parent_group);
				
		parent_group->m_children.emplace_back(new MGPlanarCurveGroup(curve, plane, parent_group));

		parent_group->notify(ADD_CHILD);
		notify(ADD_SIBLING);
		
		return parent_group;
	}

	// オブジェクトをクリアする
	void MGPlanarCurveGroup::clear(){
		m_plane.reset();
		m_loop = 0;
		m_parent = 0;
		m_children.clear();

		notify(CLEAR_ALL);
		assert(is_null());
	}

	// 目的のものを生成する
	MGGroup* MGPlanarCurveGroup::create_face() const{
		assert(!is_null());
		std::unique_ptr<MGGroup> gp(new MGGroup);

		const_iterator first = begin(), last = end();
		for(; first != last; ++first){
			MGPlanarCurveGroup& child = **first;
			
			// 1. ベースとなる face を生成する
			int ret = 0;
			std::unique_ptr<MGFace> face(new MGFace(*child.plane()));
			ret = face->trim_projection(*child.loop());

			// 2. 孫ループでくりぬく
			const_iterator first2 = child.begin(), last2 = child.end();
			for(; first2 != last2; ++first2){
				MGPlanarCurveGroup& grandchild = **first2;

				if(child.plane()->normal() % grandchild.plane()->normal() > 0){
					std::unique_ptr<MGCurve> negloop(grandchild.loop()->clone());
					negloop->negate();
					
					ret = face->trim_projection(*negloop);
				}
				else{
					ret = face->trim_projection(*grandchild.loop());
				}
			}
			// 3. gp に追加する
			gp->append(face.release());
		}

		first = begin();
		last = end();

		// 3. ひ孫をsplice
		for(; first != last; ++first){
			const_iterator first2 = (*first)->begin();
			const_iterator last2  = (*first)->end();
			for(; first2 != last2; ++first2){
				std::unique_ptr<MGGroup> gp2((*first2)->create_face());
				if(gp2.get() && !gp2->empty()){
					// gp2 を gp に連結させて切り離す
					for(auto i = gp2->begin(), iend = gp2->end(); i!=iend;i++){
						gp->append(i->release());
					}
					gp2->clear();
				}
			}
		}

		assert(!is_null());
		return gp->empty() ? 0 : gp.release();
	}

	// 子グループを検索する
	MGPlanarCurveGroup::iterator MGPlanarCurveGroup::find_child(MGPlanarCurveGroup::const_iterator i){
		iterator first = begin(), last = end();
		for(; first != last; ++first){
			iterator ret = (*first)->find_child(i);
			if(ret != (*first)->end()){
				return ret;
			}
		}
		return last;
	}

	// 子グループを検索する
	MGPlanarCurveGroup::const_iterator MGPlanarCurveGroup::find_child(MGPlanarCurveGroup::const_iterator i) const{
		const_iterator first = begin(), last = end();
		for(; first != last; ++first){
			const_iterator ret = (*first)->find_child(i);
			if(ret != (*first)->end()){
				return ret;
			}
		}
		return last;
	}

	// 子グループを追加する
	MGPlanarCurveGroup* MGPlanarCurveGroup::insert(
		const MGCurve*  curve,
		MGPlane*        plane
		){
		assert(!is_null());
		assert(curve);
		assert(plane);
		
		if(is_root()){
			// 確実に子グループが追加される
			return add_child(curve, plane);
		}
		else{
			assert(m_loop);
			
			// loop() と比較
			// 1. isect
			const MGCurve& loop_parent = *loop();
			MGCCisects ls = curve->isect(loop_parent);
			if(ls.empty()){
				// 交点がないので box の関係を調べる
				const MGBox& box = curve->box();
				const MGBox& b2 = loop_parent.box();

				// 包含関係のチェック
				// box << b2 or b2 << box or not.
				if(box << b2){
					// this の 子グループになりそう
					return add_child(curve, plane);
				}
				else if(b2 << box){
					// this の 親グループになりそう
					return add_parent(curve, plane);
				}
				else{
					return 0;
				}
			}
			else{
				// 交点があるので sibling とする
				return add_sibling(curve, plane);
			}
		}
		assert(!is_null());
		return 0;
	}

	// オブジェクトが有効かどうか
	bool MGPlanarCurveGroup::is_null() const{
		if(is_root()){
			return false;
		}
		return !m_plane.get();
	}
	
	// ルートグループかどうか
	bool MGPlanarCurveGroup::is_root() const{
		return !m_plane.get() && loop() == 0;
	}

	// ツリー構造に変更が生じたときに呼び出される
	void MGPlanarCurveGroup::notify(int what) const{
/*		// デバッグ用...
		switch(what){
		case OTHER:
			COUT << loop() << " スワップ？" << std::endl;
			break;
		case ADD_CHILD:
			COUT << loop() << " child:" << back()->loop() << std::endl;
			break;
		case REMOVE_CHILD:
			COUT << loop() << " :子が削除" << std::endl;
			break;
		case ADD_SIBLING:
			COUT << loop() << " sibling: " << parent()->back()->loop() << std::endl;
			break;
		case MODIFY_LOOP_BEFORE:
			COUT << loop() << "→" << std::endl;
			break;
		case MODIFY_LOOP_AFTER:
			COUT << "→ " << loop() << std::endl;
			break;
		case MODIFY_PARENT_BEFORE:
			COUT << loop() << "親変更前 : " << parent()->loop() << std::endl;
			break;
		case MODIFY_PARENT_AFTER:
			COUT << loop() << "親変更後 : " << parent()->loop() << std::endl;
			break;
		case CLEAR_ALL:
			COUT << "初期化" << std::endl;
			break;
		}*/
	}

	// デバッグ用
	void MGPlanarCurveGroup::out(std::ostream& os) const{
		if(is_root()){
			os << "<root>\n";
			os << "<plane address=\"" << plane()
				<< "\" center=\"" << plane()->center()
				<< "\" normal=\"" << plane()->normal()
				<< "\" distance=\"" << plane()->distance()
				<< "\" />\n";
		}
		else{
			os << "<loop address=\"" << loop() << "\" box=\"" << box() << "\">\n";
		}

		if(has_children()){
			os << "<leaf size=\"" << m_children.size() << "\">\n";
			const_iterator first = begin(), last = end();
			for(; first != last; ++first){
				(*first)->out(os);
				os << std::endl;
			}
			os << "</leaf>\n";
		}

		if(is_root()){
			os << "</root>";
		}
		else{
			os << "</loop>";
		}
	}

	// 子グループをごっそり削除する
	void MGPlanarCurveGroup::remove_child(MGPlanarCurveGroup::iterator i){
		m_children.erase(i); // remove() ではない!
		notify(REMOVE_CHILD);
	}

	// 平面曲線をセット
	void MGPlanarCurveGroup::set_loop(const MGCurve* curve, MGPlane* plane){
		assert(curve);
		assert(plane);

		notify(MODIFY_LOOP_BEFORE);
		m_loop = curve;
		m_plane.reset(plane);
		notify(MODIFY_LOOP_AFTER);
	}

	// 親グループをセット
	void MGPlanarCurveGroup::set_parent(MGPlanarCurveGroup* parent){
		notify(MODIFY_PARENT_BEFORE);
		m_parent = parent;
		notify(MODIFY_PARENT_AFTER);
	}

	// デバッグ用
	std::ostream& operator<<(std::ostream& os, const MGPlanarCurveGroup& gp){
		gp.out(os);
		return os;
	}

	// MGPCGPlanePair

	MGPCGPlanePair::MGPCGPlanePair(MGPlanarCurveGroup* root, MGPlane* plane)
		 : m_root(root),
		   m_plane(plane)
	{
		assert(root);
		assert(plane);
	}

	void MGPCGPlanePair::clear(){
		m_root.reset();
		m_plane.reset();
		assert(!m_root.get());
		assert(!m_plane.get());
	}

	bool MGPCGPlanePair::is_null() const{
		return !m_root.get() && !m_plane.get();
	}

	// MGPlanarSurfManager

	bool MGPlanarSurfManager::add(const MGCurve* curve){
		if(curve == 0) return false;
		if(!curve->is_closed()) return false;
		MGPlane plane;
		if(!curve->is_planar(plane)) return false;

		if(m_pcg.empty()){
			// 新規登録
			std::unique_ptr<MGPlanarCurveGroup> gp(new MGPlanarCurveGroup);
			gp->insert(curve, new MGPlane(plane));
			m_pcg.emplace_back(new PCGPair(gp.release(), new MGPlane(plane)));
			return true;
		}else{
			// 同じ平面上に曲線があるというものをさがし
			// もしあればそこのグループのツリーに追加
			PCGroup::iterator i = find(plane);
			if(i != m_pcg.end()){
				MGPlanarCurveGroup* group = (*i)->root();
				assert(group);
				return group->insert(curve,  new MGPlane(plane)) != 0;
			}else{
			// なければ新規に追加
				std::unique_ptr<MGPlanarCurveGroup> gp(new MGPlanarCurveGroup);
				gp->insert(curve, new MGPlane(plane));
				m_pcg.emplace_back(new PCGPair(gp.release(),  new MGPlane(plane)));
				return true;
			}
		}
		return false;
	}

	MGGroup* MGPlanarSurfManager::create_face(){
		std::unique_ptr<MGGroup> group(new MGGroup);
		iterator first = begin(), last = end();
		for(; first != last; ++first){
			std::unique_ptr<MGGroup> group2((*first)->root()->create_face());
			// null check
			if(group2.get() && !group2->empty()){
				for(auto i = group2->begin(), iend = group2->end(); i!=iend;i++){
					group->append(i->release());
				}
				group2->clear();
			}
		}
		return group->empty() ? nullptr : group.release();
	}

	MGPlanarSurfManager::iterator MGPlanarSurfManager::find(const MGPlane& plane){
		return std::find_if(
			m_pcg.begin(), m_pcg.end(),
			[&](const UniquePCGPair& i) {return i->plane()->normal().parallel(plane.normal())
												&& i->plane()->distance() == plane.distance();}
		);
	}
	
	MGPlanarSurfManager::const_iterator MGPlanarSurfManager::find(const MGPlane& plane) const{
		return std::find_if(
			m_pcg.begin(), m_pcg.end(),
			[&](const UniquePCGPair& i) {return i->plane()->normal().parallel(plane.normal())
												&& i->plane()->distance() == plane.distance(); }
		);
	}

} // namespace mgcalc
