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

/** 
 *  @file planar2.h
 *  @brief Classes for 'From planar curves'
 */
#if !defined(__PLANAR2_H__)
#define __PLANAR2_H__

#include "mg/Curve.h"
class MGCurve;
class MGFace;
class MGGroup;
class MGPlane;

namespace mgcalc{
	///@name MGPlanarCurveGroup::notify(int) の引数
	//@{
	extern const int OTHER;
	extern const int ADD_CHILD;
	extern const int REMOVE_CHILD;
	extern const int ADD_SIBLING;
	extern const int MODIFY_LOOP_BEFORE;
	extern const int MODIFY_LOOP_AFTER;
	extern const int MODIFY_PARENT_BEFORE;
	extern const int MODIFY_PARENT_AFTER;
	extern const int CLEAR_ALL;
	//@}

	/**
	 *  @brief  平面曲線のツリー構造
	 *
	 *  典型的な Composite パターンで構成されるクラスである。
	 *  木構造の親子関係は、平面曲線のバウンディングボックスの
	 *  包含関係によって定義されている。ボックスがより大きいほうが
	 *  親である。
	 *
	 *  また、このグループに属するすべての平面曲線の平面は
	 *  向きを無視すれば一致するが、face をくりぬく際に向きを
	 *  計算するため、それぞれの節、葉ごとに平面オブジェクトを
	 *  もたせてある。
	 *
	 *  ルートには曲線を持たせないが、平面オブジェクトの「代表元」を
	 *  持たせる。
	 */
	class MGPlanarCurveGroup{
	public:
		using MYELM=std::unique_ptr<MGPlanarCurveGroup>;
		using MYLST=std::list<MYELM>;
		using group=MYLST;

		typedef group::iterator             iterator;
		typedef group::const_iterator       const_iterator;
		typedef group::reference            reference;
		typedef group::const_reference      const_reference;

		/**
		 *  @brief  ルートを作成する。
		 */
		MGPlanarCurveGroup();

		reference       back(){ return m_children.back();}
		const_reference back() const{ return m_children.back();}

		iterator       begin(){ return m_children.begin();}
		const_iterator begin() const{ return m_children.begin();}

		/**
		 *  @brief   閉曲線のバウンディングを返す
		 */
		const MGBox& box() const{ return loop()->box();}

		/**
		 *  @brief オブジェクトをクリアする
		 */
		void clear();

		/**
		 *  @brief  目的のものを生成する。
		 */
		MGGroup* create_face() const;

		iterator       end(){ return m_children.end();}
		const_iterator end() const{ return m_children.end();}

		//@{
		/**
		 *  @brief  子グループを検索する
		 *  @param i  ノードを指す反復子
		 */
		iterator       find_child(const_iterator i);
		const_iterator find_child(const_iterator i) const;
		//@}

		reference       front(){ return m_children.front();}
		const_reference front() const{ return m_children.front();}

		/**
		 *  @brief  子グループを追加する]
		 *  @param  loop  平面曲線かつ閉曲線な曲線
		 *  @param  plane  @a loop の乗る平面
		 */
		MGPlanarCurveGroup* insert(
			const MGCurve*  loop,
			MGPlane*        plane
			);

		/**
		 *  @brief  オブジェクトが有効かどうか
		 */
		bool is_null() const;

		/**
		 *  @brief   ルートグループかどうか
		 */
		bool is_root() const;

		/**
		 *  @brief  子グループを有するかどうか
		 *
		 *  @note 子グループの曲線はどれも親グループの曲線の内側にある
		 */
		bool has_children() const{ return !m_children.empty();}

		/**
		 *  @brief 平面曲線を取得
		 */
		const MGCurve* loop() const{ return m_loop;}

		/**
		 *  @brief  デバッグ用
		 *  @param  os  出力用ストリーム
		 */
		void out(std::ostream& os) const;

		//@{
		/**
		 *  @brief  親グループを取得
		 */
		MGPlanarCurveGroup* parent(){ return m_parent;}
		const MGPlanarCurveGroup* parent() const{ return m_parent;}
		//@}

		//@{
		/**
		 *  @brief  共通平面を取得
		 */
		MGPlane* plane(){ return m_plane.get();}
		const MGPlane* plane() const{ return m_plane.get();}
		//@}

		/**
		 *  @brief  子グループをごっそり削除する
		 *  @param  i  ノードを指す反復子
		 */
		void remove_child(iterator i);

		/**
		 *  @brief  親グループをセット
		 *  @param  parent  親となるノード
		 */
		void set_parent(MGPlanarCurveGroup* parent);

		/**
		 *  @brief  平面をセット
		 *  @param  plane  平面
		 */
		void set_plane(MGPlane* plane){ m_plane = std::unique_ptr<MGPlane>(plane);}

	private:
		
		std::unique_ptr<MGPlane>      m_plane;// 平面曲線の共通平面

		// 平面曲線そのもの
		// nullの場合、このグループは'root'である
		const MGCurve*              m_loop;

		// すぐ上の親グループを指す
		// null の場合、このグループは'root'である
		MGPlanarCurveGroup*         m_parent;

		// 子グループ
		MYLST m_children;

		/**
		 *  @brief  子グループを生成
		 *  @param  loop  平面曲線かつ閉曲線
		 *  @param  plane @a loop の乗る平面
		 *  @param  parent  親ノード
		 */
		MGPlanarCurveGroup(
			const MGCurve*      loop,
			MGPlane*            plane,
			MGPlanarCurveGroup* parent
		);

		/**
		 *  @brief  子グループを追加する
		 *  @param  loop  平面曲線かつ閉曲線
		 *  @param  plane @a loop の乗る平面
		 */
		MGPlanarCurveGroup* add_child(
			const MGCurve*  loop,
			MGPlane*        plane
		);

		/**
		 *  @brief  親グループを挿入する
		 *  @param  loop  平面曲線かつ閉曲線
		 *  @param  plane @a loop の乗る平面
		 */
		MGPlanarCurveGroup* add_parent(
			const MGCurve*  loop,
			MGPlane*        plane
		);

		/**
		 *  @brief  兄弟グループを追加する
		 *  @param  loop  平面曲線かつ閉曲線
		 *  @param  plane @a loop の乗る平面
		 */
		MGPlanarCurveGroup* add_sibling(
			const MGCurve*  loop,
			MGPlane*        plane
		);

		/**
		 *  @brief  ツリー構造に変更が生じたときに呼び出される
		 *  @param  what  どういう変更が生じたのかを示すビットフィールド値。
		 */
		void notify(int what) const;
		
		/**
		 *  @brief  平面曲線をセット
		 *  @param  curve  平面曲線かつ閉曲線
		 *  @param  plane @a loop の乗る平面
		 */
		void set_loop(const MGCurve* curve, MGPlane* plane);

		// 以下のものは実装されない
		MGPlanarCurveGroup(const MGPlanarCurveGroup&)=delete;
		MGPlanarCurveGroup& operator=(const MGPlanarCurveGroup&)=delete;

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

	/**
	 *  @brief 平面曲線ツリーと平面のペアクラス
	 *
	 *  std::pair<T, U> を使うと名前が長くなるため自前でペアクラスを作る...
	 */
	class MGPCGPlanePair{
	public:
		MGPCGPlanePair(){}
		/**
		 *  @brief  平面曲線ツリーと代表的な平面からオブジェクトを生成
		 *  @param  root  平面曲線ツリーのルート
		 *  @param  plane  平面曲線群の共通平面の代表元的オブジェクト
		 */
		MGPCGPlanePair(MGPlanarCurveGroup* root, MGPlane* plane);

		void clear();
		bool is_null() const;

		MGPlane*       plane(){ return m_plane.get();}
		const MGPlane* plane() const{ return m_plane.get();}

		MGPlanarCurveGroup*       root(){ return m_root.get();}
		const MGPlanarCurveGroup* root() const{ return m_root.get();}

	private:
		std::unique_ptr<MGPlanarCurveGroup> m_root;
		std::unique_ptr<MGPlane>            m_plane;

		// これらは実装されない
		MGPCGPlanePair(const MGPCGPlanePair&)=delete;
		MGPCGPlanePair& operator=(const MGPCGPlanePair&) = delete;

		friend class MGPlanarSurfManager;
	};

	/**
	 *  @brief  平面曲線ツリー群を組織するクラス
	 *
	 *  複数の平面曲線(ループ)を集め、それらを共通する平面によって分類し、
	 *  ループによって face群 を作成するクラスである。
	 */
	class MGPlanarSurfManager{
	public:
		typedef MGPCGPlanePair     PCGPair;
		using UniquePCGPair = std::unique_ptr<PCGPair>;
		typedef std::vector<UniquePCGPair> PCGroup;
		
		typedef PCGroup::iterator             iterator;
		typedef PCGroup::const_iterator       const_iterator;
		typedef PCGroup::size_type            size_type;

		MGPlanarSurfManager(){}

		/**
		 *  @brief  曲線をグループに追加する
		 *  @param  curve  曲線オブジェクト
		 */
		bool add(const MGCurve* curve);

		bool empty() const{ return m_pcg.empty(); }
		size_type size() const{ return m_pcg.size(); }

		/**
		 *  @brief  face を生成する
		 */
		MGGroup* create_face();

	private:
		iterator       begin(){ return m_pcg.begin();}
		const_iterator begin() const{ return m_pcg.begin();}

		iterator       end(){ return m_pcg.end();}
		const_iterator end() const{ return m_pcg.end();}

		iterator       find(const MGPlane& plane);
		const_iterator find(const MGPlane& plane) const;

		// これらは実装されない
		MGPlanarSurfManager(const MGPlanarSurfManager& other)=delete;
		MGPlanarSurfManager& operator=(const MGPlanarSurfManager& other) = delete;

		PCGroup m_pcg;
	};

} // namespace mgcalc

#endif // __PLANAR2_H__
