/********************************************************************/
/* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno          */
/* All rights reserved.                                             */
/********************************************************************/
#include "MGCLStdAfx.h"
#include "mg/Box.h"
#include "mg/Ifstream.h"
#include "mg/Ofstream.h"
#include "mg/Group.h"
#include "mg/Attrib.h"
#include "mg/GelFactory.h"
#include "mg/LBRep.h"
#include "mgGL/Context.h"
#include "mgGL/Name.h"

#if defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//
//Implements MGGroup Class.
//MGGroup is a class which constains MGGel elements.
//MGGroup provides functions:
//(1) Serialization of MGGel elements.
//

//////////Constructor//////////

//Void constructor(初期化なしでオブジェクトを作成する。)
MGGroup::MGGroup(){;}
MGGroup::MGGroup(MGGel* gel){m_gels.push_back(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::MGGroup(const TCHAR* file, int& error){
	MGIfstream infile;
	error=infile.open(file,false);
	if(error) return;
	infile>>(*this);
}

///////////////operator overloaded//////////////

//Assignment.
//When the leaf objects of this and gel2 are not equal, this assignment
//does nothing.
MGGroup& MGGroup::operator=(const MGGroup& gel2){
	if(this==&gel2)
		return *this;

	MGAttribedGel::operator=(gel2);
	m_gels.clear();
	const_iterator i=gel2.begin(), ie=gel2.end();	
	for(; i!=ie; i++){
		m_gels.push_back((*i)->clone());
	}
	return *this;
}
MGGroup& MGGroup::operator=(const MGGel& gel2){
	const MGGroup* gel2_is_this=dynamic_cast<const MGGroup*>(&gel2);
	if(gel2_is_this)
		operator=(*gel2_is_this);
	return *this;
}

bool MGGroup::operator<(const MGGroup& gel2)const{return size()<gel2.size();};
bool MGGroup::operator<(const MGGel& gel2)const{
	const MGGroup* gel2_is_this=dynamic_cast<const MGGroup*>(&gel2);
	if(gel2_is_this)
		return operator<(*gel2_is_this);
	return false;
}

//////////Member Function//////////

//Get the MGAppearance pointer in this group. If not defined, null will be
//returned.
MGAppearance* MGGroup::appearance(){
	if(!size())
		return 0;
	MGGel* gel=front();
	MGAppearance* app=dynamic_cast<MGAppearance*>(gel);
	if(app)
		return app;		
	MGContext* cnt=dynamic_cast<MGContext*>(gel);
	if(cnt)
		return cnt->appearance();		
	return 0;
}

const MGAppearance* MGGroup::appearance()const{
	MGGroup* grp=const_cast<MGGroup*>(this);
	return grp->appearance();
}

//Get the box of the group.
//If no objects were included in this group, null box will be returned.
MGBox MGGroup::box()const{
	MGBox bx;
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++){
		const MGObject* obj=dynamic_cast<const MGObject*>(*i);
		if(obj){
			const MGBox& bxobj=obj->box();
			if(bxobj.finite())
				bx|=bxobj;
			continue;
		}
		const MGGroup* grp=dynamic_cast<const MGGroup*>(*i);
		if(grp){
			bx|=grp->box();
			continue;
		}
	}
	return bx;
}

//Generate copied gel of this gel.
//Returned is a newed object. User must delete the object.
MGGroup* MGGroup::clone()const{
	MGGroup* gel=new MGGroup;
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++){
		gel->m_gels.push_back((**i).clone());
	}
	return gel;
}

//Get the MGContext pointer stored in this group. If not defined, null will be
//returned.
MGContext* MGGroup::context(){
	if(!size()) return 0;
	return dynamic_cast<MGContext*>(front());
}
const MGContext* MGGroup::context()const{
	if(!size()) return 0;
	return dynamic_cast<const MGContext*>(front());
}

//////display member function.
void MGGroup::display_arrows(mgSysGL& sgl)const{
	const_iterator i=begin(), ie=end();
	for(; i!=ie; ++i)
		(**i).display_arrows(sgl);
}
void MGGroup::display_break_points(mgSysGL& sgl)const{
	MGGroup::const_iterator i=begin(), ie=end();
	for(; i!=ie; ++i)
		(**i).display_break_points(sgl);
}
void MGGroup::display_control_polygon(mgSysGL& sgl)const{
	const_iterator i=begin(), ie=end();
	for(; i!=ie; ++i)
		(**i).display_control_polygon(sgl);
}
void MGGroup::display_curvatures(
	mgSysGL& sgl,
	double	scale,	//scaling of the graph.
	int		density,//densitiy of the graph.
	bool	use_radius//true:radius display, false:curvature display.
)const{
	const_iterator i=begin(), ie=end();
	for(; i!=ie; ++i)
		(**i).display_curvatures(sgl,scale,density,use_radius);
}

//make this group has appearance and get the MGAppearance pointer.
MGAppearance* MGGroup::ensure_appearance(){
	MGAppearance* app=appearance();
	if(!app){
		app=new MGAppearance();
		push_appearance(app);
	}
	return app;
}

//set the copy of appr2 to this MGAttribedgel.
void MGGroup::set_appearance(const MGAppearance& appr2){
	push_appearance(appr2.clone());
}

//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.
MGGroup::const_iterator MGGroup::find(
	const MGGel* gel, const MGGroup*& grp
)const{
	std::vector<const MGGroup*> grps;
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++){
		if(*i==gel){ grp=this; return i;}
		const MGGroup* grp=dynamic_cast<const MGGroup*>(*i);
		if(grp) grps.push_back(grp);
	}
	size_t ngrp(grps.size());
	for(size_t j=0; j<ngrp; j++){
		const MGGroup* gelj=grps[j];
		const_iterator pos=gelj->find(gel,grp);
		if(grp) return pos;
	}
	grp=0;
	return end();
}
MGGroup::iterator MGGroup::find(
	MGGel* gel, MGGroup*& grp
){
	std::vector<MGGroup*> grps;
	iterator i=begin(), ie=end();	
	for(; i!=ie; i++){
		if(*i==gel){ grp=this; return i;}
		MGGroup* grp=dynamic_cast<MGGroup*>(*i);
		if(grp) grps.push_back(grp);
	}
	size_t ngrp(grps.size());
	for(size_t j=0; j<ngrp; j++){
		MGGroup* gelj=grps[j];
		iterator pos=gelj->find(gel,grp);
		if(grp) return pos;
	}
	grp=0;
	return end();
}

MGGroup::iterator MGGroup::search_by_id(MGGEL_TID tid){
	iterator i=begin(), ie=end();
	for(;i!=ie; i++){
		if((*i)->identify_type()==tid) return i;
	}
	return ie;
}
MGGroup::const_iterator MGGroup::search_by_id(MGGEL_TID tid)const{
	const_iterator i=begin(), ie=end();
	for(;i!=ie; i++){
		if((*i)->identify_type()==tid) return i;
	}
	return ie;
}

///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 MGGroup::nameSearch(
	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.
){
	search_by_nameSub(name,groupIncluding,gelFound);
}

///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 MGGroup::nameSearch(
	const MGName& 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{
	MGGroup* grp2;
	MGAttribedGel* gel2;
	MGGroup* group=const_cast<MGGroup*>(this);
	group->search_by_nameSub(name,grp2,gel2);
	groupIncluding=grp2;
	gelFound=gel2;
}

///Find the 1st gel(MGGroup or MGObject) that has the given name in this MGGroup.
///**********Searching IS performed into the member group recursively.
void MGGroup::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.
){
	groupIncluding=0;
	gelFound=0;

	iterator i=begin(), ie=end();
	for(;i!=ie; i++){
		MGGel* geli=*i;
		MGAttribedGel* agel=dynamic_cast<MGAttribedGel*>(geli);
		if(!agel)
			continue;
		const MGName* namei=agel->get_name();
		if(namei){
			if((*namei) == name){
				groupIncluding=this;
				gelFound=agel;
				return;
			}
		}
		MGGroup* grpi=dynamic_cast<MGGroup*>(agel);
		if(grpi){
			grpi->search_by_nameSub(name,groupIncluding,gelFound);
			if(gelFound)
				return;
		}
	}
}

//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* MGGroup::includes_object()const{
	const_reverse_iterator i=rbegin(), ie=rend();	
	for(; i!=ie; i++){
		const MGObject* obj=dynamic_cast<const MGObject*>(*i);
		if(obj)
			return obj;
		const MGGroup* grp=dynamic_cast<const MGGroup*>(*i);
		if(grp){
			obj=grp->includes_object();
			if(obj)
				return obj;
		}
	}
	return 0;
}
MGObject* MGGroup::includes_object(){
	return
		const_cast<MGObject*>(const_cast<const MGGroup*>(this)->includes_object());
}

//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 MGGroup::getMemberGroupNumber()const{
	const_iterator i=begin(), ie=end();
	int ngroup=0;
	for(; i!=ie; i++){
		const MGAppearance* apr=dynamic_cast<const MGAppearance*>(*i);
		if(apr)
			continue;;
		const MGGroup* grp=dynamic_cast<const MGGroup*>(*i);
		if(grp)
			ngroup++;
	}
	 return ngroup;
}

//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* MGGroup::get_i_th_MemberGroup(int i)const{
	assert(0<=i && i<getMemberGroupNumber());

	const_iterator igrp=begin(), igrpend=end();
	for(int j=0; igrp!=igrpend; igrp++){
		const MGAppearance* apr=dynamic_cast<const MGAppearance*>(*igrp);
		if(apr)
			continue;;
		const MGGroup* grp=dynamic_cast<const MGGroup*>(*igrp);
		if(grp){
			if(j==i)
				return grp;
			j++;
		}
	}
	return 0;
}
MGGroup* MGGroup::get_i_th_MemberGroup(int i){
	const MGGroup* grpC=this;
	const MGGroup* foundG=grpC->get_i_th_MemberGroup(i);
	return const_cast<MGGroup*>(foundG);
}

//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 MGGroup::make_file(const TCHAR* file){
	MGOfstream outfile;
	int error=outfile.open(file,false);
	if(error)
		return error;
	outfile<<(*this);
	return 0;
}

// Output virtual function.
std::ostream& MGGroup::out(std::ostream& ostrm) const{
	ostrm<<"MGGroup::number of gels="<<size()<<std::endl;
	const_iterator i=begin(), ie=end();	
	for(int j=0; i!=ie; i++, j++){
		ostrm<<"gel"<<j<<":"<<(**i)<<std::endl;
	}
	return ostrm;
}

//Get the number of objects included in thie group.
int MGGroup::num_of_objects() const{
	int num=0;
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++){
		const MGObject* obj=dynamic_cast<const MGObject*>(*i);
		if(obj){ num++; continue;}
		const MGGroup* grp=dynamic_cast<const MGGroup*>(*i);
		if(grp){num+=grp->num_of_objects(); continue;}
	}
	return num;
}

void MGGroup::push_appearance(MGAppearance* appr){
	if(empty()){
		m_gels.push_back(appr);
		return;
	}
	MGContext* ctx=dynamic_cast<MGContext*>(front());
	if(ctx){
		ctx->set_appearance(appr);
		return;
	}
	MGAppearance* app2=dynamic_cast<MGAppearance*>(front());
	if(app2)
		pop_front();
	m_gels.push_front(appr);
}

void MGGroup::push_context(MGContext* cntx){
	if(empty()){
		m_gels.push_back(cntx);
		return;
	}
	MGGel* gel=front();
	MGContext* ctx2=dynamic_cast<MGContext*>(gel);
	if(ctx2){
		pop_front();
	}else{
		MGAppearance* app2=dynamic_cast<MGAppearance*>(gel);
		if(app2){
			release(begin());
			MGAppearance* app3=cntx->appearance();
			if(!app3)
				cntx->set_appearance(app2);
			else
				delete app2;
		}
	}
	m_gels.push_front(cntx);
}

//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 MGGroup::push_back(MGPvector<MGObject>& objs){
	size_t n(objs.size());
	for(size_t i=0; i<n; i++){
		MGObject* obj=objs.release(i);
		push_back(obj);
	}
	objs.clear();
}
void MGGroup::push_back(MGPlist<MGObject>& objs){
	size_t n(objs.size());
	for(size_t i=0; i<n; i++){
		MGObject* obj=objs.front();
		push_back(objs.front());
		objs.pop_front();
	}
}
void MGGroup::push_back(MGPvector<MGGel>& gels){
	size_t n(gels.size());
	for(size_t i=0; i<n; i++){
		MGGel* gel=gels.release(i);
		push_back(gel);
	}
	gels.clear();
}
void MGGroup::push_back(MGPlist<MGGel>& gels){
	size_t n(gels.size());
	for(size_t i=0; i<n; i++){
		MGGel* gel=gels.front();
		push_back(gel);
		gels.pop_front();
	}
}

// push element x at the end.
void MGGroup::push_back(MGGel* x){
	MGAppearance* app=dynamic_cast<MGAppearance*>(x);
	if(app){
		push_appearance(app);
		return;
	}
	MGContext* ctx=dynamic_cast<MGContext*>(x);
	if(ctx){
		push_context(ctx);
		return;
	}
	m_gels.push_back(x);
}

// push element x at the first.
void MGGroup::push_front(MGGel* x){
	if(!size()){
		m_gels.push_front(x);
		return;
	}
	MGAppearance* app=dynamic_cast<MGAppearance*>(x);
	if(app){
		push_appearance(app);
		return;
	}
	MGContext* ctx=dynamic_cast<MGContext*>(x);
	if(ctx){
		push_context(ctx);
		return;
	}

	iterator first=begin();
	app=dynamic_cast<MGAppearance*>(*first);
	if(app){
		first++;
		m_gels.insert(first,x);
		return;
	}
	ctx=dynamic_cast<MGContext*>(*first);
	if(ctx){
		first++;
		m_gels.insert(first,x);
		return;
	}
	m_gels.push_front(x);
};

///Release all the gelof this group. The released memebes are newed objects.
///User must take care to delete them.
void MGGroup::releaseAll(){
	m_gels.m_list.clear();
}

//Remove the MGAppearance of this MGAttribedGel.
void MGGroup::remove_appearance(){
	if(!size())
		return;

	MGGel* gel=front();
	MGAppearance* app=dynamic_cast<MGAppearance*>(gel);
	if(app){
		erase(begin());
		return;		
	}
	MGContext* cnt=dynamic_cast<MGContext*>(gel);
	if(cnt)
		cnt->set_appearance(0);		
}

//Read all member data.
void MGGroup::ReadMembers(MGIfstream& buf){
	MGGel::ReadMembers(buf);
	int n;
	buf>>n;
	for(int i=0; i<n; i++)
		push_back(buf.ReadPointer());
}
//Write all member data
void MGGroup::WriteMembers(MGOfstream& buf)const{
	MGGel::WriteMembers(buf);

	int nPlaneImage=0;
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++){
		if((**i).identify_type()==MGPLANEIMAGE_TID)
			nPlaneImage++;
	}

	int n=size()-nPlaneImage;
	buf<<n;
	i=begin();	
	for(; i!=ie; i++){
		if((**i).identify_type()==MGPLANEIMAGE_TID)
			continue;
		buf.WritePointer(*i);
	}
}

//Transform the gel by the argument.
void MGGroup::transform(const MGVector& v)//translation
{
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++)
		(**i).transform(v);

}
void MGGroup::transform(double scale)//scaling.
{
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++)
		(**i).transform(scale);

}
void MGGroup::transform(const MGMatrix& mat)//matrix transformation.
{
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++)
		(**i).transform(mat);

}
void MGGroup::transform(const MGTransf& tr)//general transformation.
{
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++)
		(**i).transform(tr);

}

//Construct a null newed MGGroup from the type id TID.
MGGroup* MGNullGroup(long TID){
	MGGelFactoryRegistry* reg = MGGelFactoryRegistry::get_instance();
	return static_cast<MGGroup*>(reg->create_gel(TID));
}

/// グループ最下層のデータを取得する
void MGGroup::getSmallGroupData(std::vector<MGObject*>& outputObjects){
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++){
		MGGroup* grp=dynamic_cast<MGGroup*>(*i);
		MGAppearance* app=dynamic_cast<MGAppearance*>(*i);
		if (!grp || app){
			continue;
		}

		bool haveChildGroup = false;
		const_iterator i2=grp->begin(), ie2=grp->end();
		// 子グループのループ
		for(; i2!=ie2; i2++){
			MGGroup* childGrp=dynamic_cast<MGGroup*>(*i2);
			MGAppearance* childApp=dynamic_cast<MGAppearance*>(*i2);
			MGObject* obj=dynamic_cast<MGObject*>(*i2);

			if(childGrp && !childApp){
				haveChildGroup = true;
			} else if (obj){
				// 色はグループに持っているので、objectにつける
				const MGColor* color = grp->get_color();
				if (color)
					obj->set_color(*color);
				outputObjects.push_back(obj);
			}
		}

		if(haveChildGroup){
			// 子グループが存在する場合は、再帰的に処理する
			grp->getSmallGroupData(outputObjects);
		}	
	}
};

/// グループに含まれるすべてのobjectを取得する
void MGGroup::getAllObjects(std::vector<MGObject*>& allObjects){
	const_iterator i=begin(), ie=end();	
	for(; i!=ie; i++){
		MGObject* obj=dynamic_cast<MGObject*>(*i);
		if (obj){
			allObjects.push_back(obj);
		}
	}
};

AUTO_GEL_REGISTER(MGGroup, MGGROUP_TID);
