/********************************************************************/
/* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
#ifndef _MGGroup_HH_
#define _MGGroup_HH_

#include "mg/Plist.h"
#include "mg/Pvector.h"
#include "mg/Object.h"
#include "mg/Gel.h"
#include "mg/AttribedGel.h"

//
//Define MGGroup Class.
//
class MGIfstream;
class MGOfstream;
class MGBox;
class MGObject;
class MGAttrib;
class MGGLAttrib;
class MGAppearance;
class MGContext;

/** @addtogroup GelRelated
 *  @{
 */

///MGGroup is a class which constains MGGel elements.

///MGGroup provides functions:
///(1) a container of MGGel(MGAtrribedGel(MGObject or MGGroup), MGAttrib) elements.
///(2) To attach an appearance to the context of the group.
class MG_DLL_DECLR MGGroup:public MGAttribedGel{

public:
///	別名定義
	typedef MGPlist<MGGel>::iterator iterator ;
	typedef MGPlist<MGGel>::const_iterator const_iterator;
	typedef MGPlist<MGGel>::reverse_iterator reverse_iterator ;
	typedef MGPlist<MGGel>::const_reverse_iterator const_reverse_iterator;

////////////Constructor////////////

///Void constructor(初期化なしでオブジェクトを作成する。)
MGGroup();
MGGroup(MGGel* gel);

///Construct MGGroup from the file made by MGOfstream.
///error contains error flag as:
///=0: open succeeded.
///=1: file not found, or could not be opened.
///=2: file found, but, the format is not MGCL format.
///When error!=0, MGGroup contains no MGGel's.
MGGroup(const TCHAR* file, int& error);

///Copy constructor.
///MGGroup(const MGGroup& obj2);

///Virtual Destructor
///virtual ~MGGroup();

//////////////////operator overloaded////////////////

///Assignment.
///When the leaf object of this and gel2 are not equal, this assignment
///does nothing.
virtual MGGroup& operator=(const MGGel& gel2);
virtual MGGroup& operator=(const MGGroup& gel2);

///comparison
virtual bool operator<(const MGGroup& gel2)const;
virtual bool operator<(const MGGel& gel2)const;

////////////Member Function////////////

///Get the MGAppearance pointer in this group. If not defined, null will be
///returned.
MGAppearance* appearance();
const MGAppearance* appearance()const;

///Judge if the display list for vmode is made or not.
//virtual bool displayList_is_made(MGCL::VIEWMODE vmode)const;

/// Return(but does not remove) last element in the group.
/// If list is empty, behavior is undefined.
const MGGel* back() const{return m_gels.back();};
MGGel* back(){return m_gels.back();};

/// Return const_iterator at the beginning of list.
const_iterator begin() const{return m_gels.begin();}
iterator begin(){return m_gels.begin();}

///Get the box of the group.
///If no objects were included in this group, null box will be returned.
MGBox box()const;

/// clear list, that is, erase(delete) all the elements in the MGGel.
void clear(){m_gels.clear();};

///Generate copied gel of this gel.
///Returned is a newed object. User must delete the object.
virtual MGGroup* clone()const;

///Get the MGContext pointer stored in this group. If not defined, null will be
///returned.
MGContext* context();
const MGContext* context()const;

///////display member function.
virtual void display_arrows(mgSysGL& sgl)const;
virtual void display_break_points(mgSysGL& sgl)const;
virtual void display_control_polygon(mgSysGL& sgl)const;
virtual void display_curvatures(
	mgSysGL& sgl,///<Sgl to make pictures in.
	double	scale,	///<scaling of the graph.
	int		density,///<densitiy of the graph.
	bool	use_radius///<true:radius display, false:curvature display.
)const;

///make this group has appearance and get the MGAppearance pointer.
MGAppearance* ensure_appearance();

///Return true (1) if there are no items in the MGGel,
/// false(0) otherwise.
bool empty() const{return m_gels.empty();};

/// Return const_iterator at the end of MGGel.
const_iterator end() const{return m_gels.end();};
iterator end(){return m_gels.end();};

/// erase element x. Function's return value is the following iterator
/// of the erased element x.
iterator erase(iterator x){return m_gels.erase(x);};

/// erase sequence [first, last). Function's return value is the following iterator
/// of the erased elements.
iterator erase(iterator first, iterator last){return m_gels.erase(first,last);};

///Find the position of the gel in the gel list and the group pointer
///which includes the gel. Searching will be done into the member group gel
///of this list.
const_iterator find(
	const MGGel* gel,///<The target gel.
	const MGGroup*& grp	///<The group that includes gel will be returned.
						///<When not found grp=null will be returned
)const;

///Find the position of the gel in the gel list and the group pointer
///which includes the gel. Searching will be done into the member group gel
///of this list.
iterator find(
	MGGel* gel,///<The target gel.
	MGGroup*& grp///<The group that includes gel will be returned,
			///<When not found grp=null will be returned.
);

///Count up how many MGGroup members are included in this group except MGAppearance.
///Function's return value is the number of member group.
///Only the members of this group are counted.
int getMemberGroupNumber()const;

///Get i-th MGGroup pointer of this memeber group except MGAppearance.
///If i-th MGGroup except MGAppearance is not found, null will be returned.
const MGGroup* get_i_th_MemberGroup(int i)const;
MGGroup* get_i_th_MemberGroup(int i);

///Find the position of the 1st member gel of type tid in this MGGroup.
///**********Searching is NOT performed into the member group.
iterator search_by_id(MGGEL_TID tid);
const_iterator search_by_id(MGGEL_TID tid)const;

///Find the 1st gel(MGGroup or MGObject) member in this MGGroup
///that has the given name.
///**********Searching IS performed into the member group recursively.
void nameSearch(
	const MGName& name,///<The target name.
	MGGroup*& groupIncluding,///<If found the group including the gel of the name is set,
					///else, null will be returned.
	MGAttribedGel*& gelFound///<found gel will be returned if found, else null.
);

///Find the 1st gel(MGGroup or MGObject) member in this MGGroup
///that has the given name.
///**********Searching IS performed into the member group recursively.
void nameSearch(
	const MGName& name,///<The target name.
	const MGGroup*& groupIncluding,///<If found, the group including the gel of the name is set,
					///else, null will be returned.
	const MGAttribedGel*& gelFound///<found gel will be returned if found, else null.
)const;

/// Return(but does not remove) first element in the MGGel.
/// If list is empty, behavior is undefined.
const MGGel* front() const{return m_gels.front();};
MGGel* front(){return m_gels.front();};

///Return MGGroup pointer if this MGGel is an MGGroup, else return null.
MGGroup* group(){return this;};
const MGGroup* group()const{return this;};

/// Return This object's typeID
virtual long identify_type() const{return MGGROUP_TID;};

///Test if this gel includes an object.
///Function's return value is the 1st object found in the gel list of this
///if this includes an object. Otherwise null will be returned.
const MGObject* includes_object()const;
MGObject* includes_object();

///insert an element x before the position it.
///Function's return value is the iterator of x after inserted.
iterator insert(iterator it, MGGel* x){return m_gels.insert(it,x);};

///Make an MGOfstream file which contains this group.
///The file generated by make_file can be retrieved by the constructor
///MGGroup(const char* file, int error);
///Function's return value is:
///=0: the file is successfully made.
///=1: file could not be opened.
int make_file(const TCHAR* file);

///Make a display list of this gel.
void make_display_list(
	MGCL::VIEWMODE vmode=MGCL::DONTCARE
)const;

///Get manifold dimension.
///MGGroup returns right one, MGGroup return 2, and others return -1.
virtual int manifold_dimension() const{return 2;};

///Delete the mgVBO of the i-th element.
void delete_displayList(
	const_iterator x
)const;

///Delete display list of the sequence [first, last).
void delete_displayList(
	const_iterator first, const_iterator last
)const;

///Delete the mgVBO of gels_to_delete
void delete_displayList(
	const std::vector<const MGGel*>& gels_to_delete
)const;

///Process of draw or render attributes.
void drawAttrib(
	mgVBO& vbo,///<The target graphic object.
	bool no_color=false	///if true, color attribute will be neglected.
)const;

///Returns the size of maximum size.
int max_size() const{return int(m_gels.max_size());};

///Get the number of objects included in thie group.
int num_of_objects() const;

///IGES output function.
virtual int out_to_IGES(
	MGIgesOfstream& igesfile,
	int SubordinateEntitySwitch=0
)const;

/// Output virtual function.
virtual std::ostream& out(std::ostream&) const;

/// pop last element.
void pop_back(){m_gels.pop_back();};

/// pop first element.
void pop_front(){m_gels.pop_front();};

void push_appearance(MGAppearance* appr);
void push_context(MGContext* cntx);

/// push element x at the end.
void push_back(MGGel* x);

///push elements in objs at the end. All of the object pointers are
///transfered to this. On return, objs will have no object pointer in it.
void push_back(MGPvector<MGObject>& objs);
void push_back(MGPlist<MGObject>& objs);
void push_back(MGPvector<MGGel>& gels);
void push_back(MGPlist<MGGel>& gels);

/// push element x at the first.
///x must be a newed object, and the ownership will be transfered to thisMGGroup.
void push_front(MGGel* x);

/// Return const_reverse_iterator at the beginning of list.
const_reverse_iterator rbegin() const{return m_gels.rbegin();};
reverse_iterator rbegin(){return m_gels.rbegin();};

/// Return const_reverse_iterator at the end of list.
const_reverse_iterator rend() const{return m_gels.rend();};
reverse_iterator rend(){return m_gels.rend();};

///Release the gel at the position i.
///Returned will be the position after the relesed gel.
iterator release(iterator i){return m_gels.release(i);};

///Release all the gelof this group. The released memebes are newed objects.
///User must take care to delete them.
void releaseAll();

///Remove the MGAppearance of this MGAttribedGel.
void remove_appearance();

///Remove the MGGel* and return the MGGel*. If i is not valid, 
/// behavior is undefined.
///現在の所有権を放棄し、ただのポインタを返す。
MGGel* removeAt(iterator x){return m_gels.removeAt(x);};
	
///reverse sequence.
void reverse(){m_gels.reverse();};

///Execute polar-scaling to all the MGCurve and MGFace of this group.
///curve's (x,y) are updated. No other coordinates are unchanged.
///The updated result curve is always MGLBRep.
///For MGFace, the boundaries are polar-scaled.
///
///Rotation is performed from the angle range (angleBase,angle1) to
///(angleBase,angle2).
///That is, when angle1=angle2, no change is done.
///When angle2 is angleBase, all the data will lie on the straight of from origin to
///(cos(angleBase), sin(angleBase)).
///angle1-angleBase must be >MGTolerance::angle_zero().
///IF a member gel is not MGCurve nor MGFace, it is unchanged.
void scalePolar(
	double angleBase,	///<base angle.
	double angle1,		
	double angle2
);

//set the copy of appr2 to this MGAttribedgel.
void set_appearance(const MGAppearance& appr2);

/// Return the number of items that are in the list.
int size() const{return int(m_gels.size());};

/// グループ最下層のデータを取得する
void getSmallGroupData(
	std::vector<MGObject*>& outputObjects	///グループ最下層のデータを入れる
);

/// グループに含まれるすべてのobjectを取得する
void getAllObjects(
	std::vector<MGObject*>& allObjects	///オブジェクトデータ
);

/// Equivalent to call std::list::sort().
void sort(){ m_gels.sort();}
	
template<class Traits> 
void sort(Traits _Comp){m_gels.sort(_Comp);}

///Transform the gel by the argument.

///translation
virtual void transform(const MGVector& v);

///scaling.
virtual void transform(double scale);

///matrix transformation.
virtual void transform(const MGMatrix& mat);

///general transformation.
virtual void transform(const MGTransf& tr);

virtual std::string whoami()const{return "Group";};

protected:

///Read all member data.
virtual void ReadMembers(MGIfstream& buf);

///Write all member data
virtual void WriteMembers(MGOfstream& buf)const;

private:
	
///member data. A list of MGGel*.
		MGPlist<MGGel> m_gels;

///Find the 1st gel(MGGroup or MGObject) member in this MGGroup
///that has the given name.
///**********Searching IS performed into the member group recursively.
void search_by_nameSub(
	const MGName& name,
	MGGroup*& groupIncluding,///<If found, the group including the gel of the name is set,
					///else, null will be returned.
	MGAttribedGel*& gelFound///<found gel will be returned if found, else null.
);


friend class MGIfstream;
friend class MGOfstream;
};

///Construct a null newed MGGroup from the type id TID.
MG_DLL_DECLR MGGroup* MGNullGroup(long TID);

/** @} */ // end of GelRelated group
#endif
