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

/** 
 *  @file evalcs.h
 *  @brief Classes for 'Deviation between a curve and a surfce' 
 */
#if !defined(__EVALCS_H__)
#define __EVALCS_H__

#include <iosfwd>  // for std::ostream
#include <memory> // for std::unique_ptr
#include "mg/Position.h"

class MGCurve;
class MGFSurface;

namespace mgcalc{

	/** 
	 *  @brief  点情報クラス
	 *
	 *  曲線と曲面の計測位置を保持するクラスである。
	 */
	class MGEvalCSDevInfo{
	private:
		/**
		 *  @brief  ヌルオブジェクトを生成
		 */
		MGEvalCSDevInfo();

		/**
		 *  @brief  すべてのパラメータを与えてオブジェクトを生成
		 *  @param  t  曲線上のパラメータ
		 *  @param  u  曲面上のパラメータ u 成分
		 *  @param  v  曲面上のパラメータ v 成分
		 */
		MGEvalCSDevInfo(
			double t,
			double u,
			double v
			);

		/**
		 *  @brief  すべてのパラメータを与えてオブジェクトを生成
		 *  @param  t   曲線上のパラメータ
		 *  @param  uv  曲面上のパラメータ
		 */
		MGEvalCSDevInfo(
			double t,
			const MGPosition& uv
			);

	public:
		/**
		 *  @brief  ある地点における曲線と曲面の離れを返す
		 */
		double distance() const;

		/**
		 *  @brief  距離と位置を計算してメンバにセットする
		 *  @param  curve   評価するための曲線
		 *  @param  surf    評価するための曲面
		 */
		void evaluate(const MGCurve& curve, const MGFSurface& surf);

		/**
		 *  @brief  距離が無効かどうか
		 */
		bool is_distance_null() const{ return m_null_distance;}
		
		/**
		 *  @brief  パラメータが無効かどうか
		 */
		bool is_param_null() const{ return m_null_param;}
		
		/**
		 *  @brief  位置情報が無効かどうか
		 */
		bool is_position_null() const{ return m_null_pos;}

		/**
		 *  @brief  曲線上の点におけるパラメータを返す
		 */
		double param_curve() const;

		/**
		 *  @brief  曲面上の点におけるパラメータを返す
		 */
		MGPosition* param_surf() const;

		//@{
		/**
		 *  @brief  パラメータに対応する位置を返す
		 */
		const MGPosition& position_curve() const;
		const MGPosition& position_surf() const;
		//@}

	private:
		/**
		 *  @brief  このオブジェクトを無効化する
		 */
		void set_null();

		//@{
		/**
		 *  @brief  曲線と曲面のパラメータを設定する
		 */
		void set_param(double t, const MGPosition& uv);
		void set_param(double t, double u, double v);
		//@}

	private:
		/// 曲線上の点と曲面上の点との距離
		double m_distance;

		/// 曲線上の点におけるパラメータ
		double m_t;

		/// 曲面上の点におけるパラメータ
		double m_u;

		/// 曲面上の点におけるパラメータ
		double m_v;

		/// 曲線上の点の世界座標
		MGPosition m_pos_curve;

		/// 曲面上の点の世界座標
		MGPosition m_pos_surf;

		//@{
		/// 各種フラグ
		mutable bool m_null_distance : 1;
		mutable bool m_null_param    : 1;
		mutable bool m_null_pos      : 1;
		//@}

		friend class MGEvalCSInfoGroup;
		friend std::ostream& operator<<(std::ostream&, const MGEvalCSDevInfo&);
	};

	/**
	 *  @brief  評価クラス
	 *
	 *  曲線と曲面の距離的な離れを評価するクラスである。
	 *  評価した位置データを保存する。
	 */
	class MGEvalCSInfoGroup{
	public:
		using MYELM=std::unique_ptr<MGEvalCSDevInfo>;
		using MYLST=std::list<MYELM>;

		typedef MYLST::iterator       iterator;
		typedef MYLST::const_iterator const_iterator;

	private:
		const MGCurve*                 m_curve; ///< 評価の対象となる曲線
		const MGFSurface*              m_surf;  ///< 評価の対象となる曲面
		MYLST               m_info;  ///< 評価データ

	public:
		/**		 *  @brief  ヌルオブジェクトを生成。 */
		MGEvalCSInfoGroup();

		/**
		 *  @brief  オブジェクトを生成する。
		 *  @param  curve  評価する曲線
		 *  @param  surf   評価する曲面
		 *
		 *  各ポインタの所有権は委譲されない。すなわち集積。
		 */
		MGEvalCSInfoGroup(const MGCurve* curve, const MGFSurface* surf);

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

		/**		 *  @brief  曲線を返す		 */
		const MGCurve* curve() const;

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

		/**		 *  @brief  評価済かどうかを返す		 */
		bool done() const{ return !m_info.empty();}

		/**		 *  @brief  曲線の細分、距離計測を行う
		 *  @param  maxdev   離れとみなさない距離の上限値。
		 *  @param  div      ノット間の分割数。		 */
		void evaluate(double maxdev, int div);

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

		/**		 *  @brief  評価する曲線を設定する
		 *  @oaram  評価する曲線		 */
		void set_curve(const MGCurve* curve);

		/**		 *  @brief  このオブジェクトを無効にする		 */
		void set_null();

		/**		 *  @brief  評価に使う曲面を設定する
		*  @param  評価する曲面		 */
		void set_surf(const MGFSurface* surf);

		/**		 *  @brief  曲面を返す		 */
		const MGFSurface* surf() const;
		

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

		friend std::ostream& operator<<(std::ostream&, const MGEvalCSInfoGroup&);
	};
	
} // namespace mgcalc

#endif // __EVALCS_H__
