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

#include <iosfwd>
#include <vector>
#include "mg/MGCL.h"
#include "mg/Group.h"
#include "mg/AttribedGel.h"
class MGShell;

//
//Define MGGelPosition Class.

/** @file */
/** @addtogroup GelRelated
 *  @{
 */

///MGGelPosition is a class to locate where a gel is in a group hierarchy.

/// MGGelPosition is a class to locate where a gel(MGObject(including MGShell)
/// or MGGroup) is in a group hierarchy. Generally, A group includes other groups,
/// and the included groups include other groups. In this way, the groups make
/// a group hierachy. MGGelPosition represents this hierarcy.
/// m_group is the top MGGroup that includes the object m_object if m_Ghierarcy.size()==0.
/// If m_Ghierarcy.size()>0, m_group includes m_Ghierarcy[0].
///	Let n=m_Ghierarcy.size(), then group m_Ghierarcy[i-1] includes
/// m_Ghierarcy[i] for n=0,...,n-2. m_Ghierarcy[n-1] includes m_object(if m_object!=0).
/// m_object is the leaf MGObject pointer if exist. When n>0 and m_object=0, this means
/// that leaf_is_group() is true and the positioning target is MGGroup.
/// Although m_Ghierarcy[i] for i=0,...,n-2 are always MGGroup, m_Ghierarcy[n-1] may be
/// MGShell that includes MGFace. In this case, is_shell_face() is true and
/// m_object is the MGFace.
/// 
class MG_DLL_DECLR MGGelPosition{

public:

///String stream function.
MG_DLL_DECLR friend std::ostream& operator<< (std::ostream&, const MGGelPosition&);


////////Special member functions/////////
MGGelPosition();
virtual ~MGGelPosition()=default;
MGGelPosition(const MGGelPosition&)=default;//Copy constructor.
MGGelPosition& operator= (const MGGelPosition&)=default;///Copy assignment.
MGGelPosition(MGGelPosition&&)=default;		///Move constructor.
MGGelPosition& operator= (MGGelPosition&&)=default;///Move assignment.


///Constructor of no hierarched group(m_Ghierarcy.size()==0).
explicit MGGelPosition(MGGroup* group, MGObject* obj=0);

///Equal operator
bool operator== (const MGGelPosition& gelp2) const;
bool operator!= (const MGGelPosition& gelp2) const{return !(*this==gelp2);}

///Comparison operator.
bool operator<(const MGGelPosition& gp2)const;
bool operator>(const MGGelPosition& gp2)const{return gp2<(*this);};
bool operator<=(const MGGelPosition& gp2)const{return !((*this)>gp2);};
bool operator>=(const MGGelPosition& gp2)const{return !(gp2>(*this));};

////////////Member Function////////////

///Append lower level group or shell data.
void append_lower_gel(MGGel* gel);

///Return the MGGel i;
const MGGel* gel(int i)const{return m_Ghierarcy[i];};
MGGel* gel(int i){return m_Ghierarcy[i];};

///Generate a newed clone object.
virtual MGGelPosition* clone()const;

///perform add operation of this gel position.
///(push back the object of m_object to the group that includes m_object)
void do_add();

///perform remove operation of this gel position.
///(Release the object of m_object from the group that includes m_object,
///but does not delete the gel).
void do_remove();

///Get the group pointer that includes leaf_gel();
const MGGroup* bottom_group()const;
MGGroup* bottom_group();

///Get the top group pointer.
const MGGroup* top_group()const{return m_group;};
MGGroup* top_group(){return m_group;};

//Get the shell pointer when this is_shell_face() is true.
//When is_shell_face() is false, behavior is undefined.
MGShell* get_shell_of_shell_face()const;

///Test if this MGGelPosition is a member of a group of the input grp.
///Test is done through the hierarchy of the group grp.
///Returned is null if not a member of (or a member group of) grp.
///The parent groupt pointer is returned if this geposiiton is a member of
///a group.
const MGGroup* is_a_member_of(const MGGroup* grp)const;

///Test if this is null.
bool is_null() const;

///Test if this is MGGelpotion that point shell and the member face.
///That is, m_object is MGFace and top_object() is MGShell.
bool is_shell_face()const;

//Test if this is this is one of the type of types.
bool is_type(const MGAbstractGels& types)const;

//Test if this leaf is MGGroup.
bool leaf_is_group()const;

///Get the top object pointer of this.
///Returned is MGObject of m_object or
///MGShell that is the last member of m_Ghierarcy.
const MGObject* top_object()const;
MGObject* top_object();

///Get the leaf object pointer of this.
///Returned is MGObject excluding Shell.
const MGObject* leaf_object()const{return m_object;};
MGObject* leaf_object(){return m_object;};

///Get the leaf AttribedGel pointer of this.
///Returned is MGAttribedGel excluding Shell.
const MGAttribedGel* leafAttribedGel()const{return m_agel;};
MGAttribedGel* leafAttribedGel(){return m_agel;};

///Set the leaf object data.
void set_attribedGel(MGAttribedGel* agel);

///Set the leaf object data.
void set_leaf_object(MGObject* obj);

///Set the group data.
void set_top_group(MGGroup* group){m_group=group;};

///Set this as null.
void set_null();

///Test if this is symmetric to gel2.
///Symmetric means:
///Both leaf objects are MGObject and they have the same manifold dimension.
bool symmetric(const MGGelPosition& gp2)const;

///Get the target attribed gel to update.
///I.e., if this is shell_face, return the shell.
///Else, return the leaf AttribedGel.
MGAttribedGel* targetGel();

protected:
	MGGroup* m_group;///<The top group pointer which includes
		///the object m_object if m_Ghierarcy.size()==0.
		///If m_Ghierarcy.size()>0, m_group includes m_Ghierarcy[0].
	std::vector<MGGel*> m_Ghierarcy;//The group hierarcy. Let n=m_Ghierarcy.size(),
		///then group m_Ghierarcy[i-1] includes m_Ghierarcy[i] for n=0,...,n-2.
		///m_Ghierarcy[n-1] includes m_object;
		/// Although m_Ghierarcy[i] for i=0,...,n-2 are always MGGroup,
		/// m_Ghierarcy[n-1] may be MGShell that includes MGFace.
		/// In this case, m_object is the MGFace.
	MGAttribedGel* m_agel;///<The leaf MGObject or
		/// MGAttribedGel(MGGroup for attribute update) pointer.
	MGObject* m_object;	///<The leaf MGObject pointer if m_agel is an MGObject.
		///When m_agel is MGObject, m_object is always set. In this
		///case m_agel=m_object.

private:
	//Get the object pointer of this.
	const MGObject* top_object_sub()const;

	//Get the lowerest level of group pointer of this.
	const MGGroup* bottom_group_sub()const;
};

/** @} */ // end of GelRelated group
#endif
