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

#include "StdAfx.h"
#include "mg/GelPosition.h"
#include "topo/Shell.h"

#if defined(_DEBUG)
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//
//Implements MGGelPosition Class.
//MGGelPosition is a class which expresses which group a gel belongs to.
//


///Void constructor.
MGGelPosition::MGGelPosition()
:m_group(nullptr),m_agel(nullptr), m_object(nullptr){
}

///Constructor of no hierarched group(m_Ghierarcy.size()==0).
MGGelPosition::MGGelPosition(MGGroup* group, MGObject* obj)
:m_group(group),m_agel(obj), m_object(obj){
}

//Equal operator
bool MGGelPosition::operator==(const MGGelPosition& gelp2) const{
	if(m_group!=gelp2.m_group)
		return false;
	if(m_agel!=gelp2.m_agel)
		return false;
	if(m_object!=gelp2.m_object)
		return false;
	size_t n1=m_Ghierarcy.size(), n2=gelp2.m_Ghierarcy.size();
	if(n1!=n2)
		return false;
	for(size_t i=0; i<n1; i++)
		if(m_Ghierarcy[i]!=gelp2.m_Ghierarcy[i])
			return false;
	return true;
}

bool MGGelPosition::operator<(const MGGelPosition& gelp2)const{
	const MGGroup* g1=top_group();
	const MGGroup* g2=gelp2.top_group();
	if(g1!=g2)
		return g1<g2;

	size_t n1=m_Ghierarcy.size(), n2=gelp2.m_Ghierarcy.size();
	if(n2>n1)
		return false;
	if(n1>n2)
		return true;

	for(size_t i=0; i<n1; i++){
		if(m_Ghierarcy[i]!=gelp2.m_Ghierarcy[i])
			return m_Ghierarcy[i]<gelp2.m_Ghierarcy[i];
	}
	if(m_agel && gelp2.m_agel)
		return *m_agel<*(gelp2.m_agel);
	else
		return m_agel<gelp2.m_agel;
}

//String output function.
std::ostream& operator<<(std::ostream& out, const MGGelPosition& gelp){
	out<<"MGGelPosition::top_group="<<gelp.m_group<<",agel="
		<<gelp.m_agel<<",obj="<<gelp.m_object<<std::endl;
	size_t n=gelp.m_Ghierarcy.size();
	if(n){
		out<<"num of mid hierary="<<n<<":";
		for(size_t i=0; i<n; i++){
			out<<i<<":"<<gelp.m_Ghierarcy[i];
			if(i<(n-1))
				out<<", ";
		}
		out<<std::endl;
	}
	return out;
}

///Append lower level group or shell data.
void MGGelPosition::append_lower_gel(MGGel* gel){
	assert(dynamic_cast<MGGroup*>(gel) || dynamic_cast<MGShell*>(gel));
	m_Ghierarcy.push_back(gel);
}

//Get the lowerest level of group pointer of this.
const MGGroup* MGGelPosition::bottom_group_sub()const{
	const MGGroup* grp=m_group;
	int nhierk=(int)m_Ghierarcy.size();
	if(nhierk--){
		const MGGel* gel=m_Ghierarcy[nhierk--];
		const MGGroup* grp2=dynamic_cast<const MGGroup*>(gel);
		if(grp2)
			grp=grp2;
		else{
			if(nhierk>=0){
				gel=m_Ghierarcy[nhierk];
				grp= dynamic_cast<const MGGroup*>(gel);;
			}
		}
	}
	return grp;
}

///Get the group pointer.
const MGGroup* MGGelPosition::bottom_group()const{
	return bottom_group_sub();
}
MGGroup* MGGelPosition::bottom_group(){
	const MGGroup* grp=bottom_group_sub();
	return const_cast<MGGroup*>(grp);
}

//Generate a newed clone object.
MGGelPosition* MGGelPosition::clone()const{
	return new MGGelPosition(*this);
}

///Test if this is null.
bool MGGelPosition::is_null()const{
	if(m_Ghierarcy.size())
		return false;
	return m_agel==0;
}

//Get the shell pointer when this is_shell_face() is true.
//When is_shell_face() is false, behavior is undefined.
MGShell* MGGelPosition::get_shell_of_shell_face()const{
	assert(m_Ghierarcy.size());
	assert(dynamic_cast<const MGFace*>(m_object)&& 
		dynamic_cast<const MGShell*>(m_Ghierarcy.back()));
		return dynamic_cast<MGShell*>(m_Ghierarcy.back());
}

///Test if this MGGelPosition is a member of a group of the input grp.
///Test is done through the hierarchy of this gelposition.
///Returned is null if this is not a member of (or a member group of) grp.
///The parent group pointer is returned if grp has a parentgroup.
///If grp does not have parent group, grp will be returned.
const MGGroup* MGGelPosition::is_a_member_of(const MGGroup* grp)const{
	if(!grp)
		return 0;
	const MGGroup* upGrp=m_group;
	if(grp==upGrp)
		return upGrp;

	size_t nhierk=m_Ghierarcy.size();
	if(!nhierk)
		return 0;

	for(size_t i=0; i<nhierk; i++){
		const MGGroup* grp2=dynamic_cast<const MGGroup*>(m_Ghierarcy[i]);
		if(grp2==grp)
			return upGrp;
		if(!grp2)
			return 0;
		upGrp=grp2;
	}
	return 0;
}

///Test if this is MGGelpotion that point shell and the member face.
///That is, m_object is MGFace and top_object() is MGShell.
bool MGGelPosition::is_shell_face()const{
	if(!m_object)
		return false;
	if(top_object_sub()==m_object)
		return false;//Since m_object=MGFace and m_Ghierarcy.back()=MGShell.

	assert(dynamic_cast<const MGFace*>(m_object)&& 
		dynamic_cast<const MGShell*>(m_Ghierarcy.back())) ;
	return true;
}

//Test if this is one of the type of types.
bool MGGelPosition::is_type(const MGAbstractGels& types)const{
	if(leaf_is_group()){
		return m_Ghierarcy.back()->type_is(types);
	}
	if(is_shell_face()){
		const MGObject* obji=top_object();
		return  obji->type_is(types);
	}
	if(!m_agel)
        return false;
	return m_agel->type_is(types);
}

//Test if this leaf is MGGroup.
bool MGGelPosition::leaf_is_group()const{
	if(m_agel)
		return false;
	size_t nhierk=m_Ghierarcy.size();
	if(!nhierk)
		return false;
	assert(dynamic_cast<const MGGroup*>(m_Ghierarcy.back())) ;
	return true;
}

///Set the leaf object data.
void MGGelPosition::set_attribedGel(MGAttribedGel* agel){
	m_agel=agel;
	MGObject* obj=dynamic_cast<MGObject*>(agel);
	if(obj)
		m_object=obj;
	else
		m_object=0;
}

///Set the leaf object data.
void MGGelPosition::set_leaf_object(MGObject* obj){
	m_agel=m_object=obj;
}

///Get the target attribed gel to update.
///I.e., if this is shell_face, return the shell.
///Else, return the leaf AttribedGel.
MGAttribedGel* MGGelPosition::targetGel(){
	MGAttribedGel* agel;
	if(is_shell_face()){
		agel=top_object();
	}else
		agel=static_cast<MGAttribedGel*>(leafAttribedGel());
	return agel;
}

//Get the object pointer of this.
const MGObject* MGGelPosition::top_object_sub()const{
	size_t nhierk=m_Ghierarcy.size();
	const MGObject* obj=0;
	if(nhierk)
		obj=dynamic_cast<const MGObject*>(m_Ghierarcy[nhierk-1]);
	if(obj)
		return obj;//This must be MGShell.
	return m_object;
}
const MGObject* MGGelPosition::top_object()const{
	return top_object_sub();
}

MGObject* MGGelPosition::top_object(){
	const MGObject* obj=top_object_sub();
	return const_cast<MGObject*>(obj);
}

///Clear the content.
void MGGelPosition::set_null(){
	m_group=0;
	m_Ghierarcy.clear();
	m_agel=m_object=0;
}

//Test if this is symmetric to gel2.
//Symmetric means:
///Both leaf objects are MGObject and they have the same manifold dimension.
bool MGGelPosition::symmetric(const MGGelPosition& gelp2)const{
	const MGObject* obj1=leaf_object();
	if(!obj1)
		return false;
	const MGObject* obj2=gelp2.leaf_object();
	if(!obj2)
		return false;

	return (obj1->manifold_dimension()==obj2->manifold_dimension());
}

//Perform add operation of this gel position.
//(insert the object of m_object in the lowerest level of group).
//This is valid only for top_object() is not shell
//(unable to add face into shell).
void MGGelPosition::do_add(){
	MGGroup* group_up;
	MGGel* gel_to_add;
	MGGroup* grp=bottom_group();

	if(is_shell_face()){
		group_up=grp;
		gel_to_add=top_object();
	}else if(leaf_is_group()){
		gel_to_add=grp;
		size_t nhierak=m_Ghierarcy.size();
		if(nhierak>=2)
			group_up= dynamic_cast<MGGroup*>(m_Ghierarcy[nhierak-2]);
		else
			group_up=m_group;
	}else{
		group_up=grp;
		gel_to_add=m_agel;
	}
	group_up->append(gel_to_add);
}

//Perform remove operation of this gel position.
//(Release the gel of m_gel from the group of m_group, but does not delete the gel).
void MGGelPosition::do_remove(){
	MGGroup* group_up;
	MGGel* gel_to_remove;
	MGGroup* grp=bottom_group();

	if(is_shell_face()){
		group_up=grp;
		gel_to_remove=top_object();
	}else if(leaf_is_group()){
		gel_to_remove=grp;
		size_t nhierak=m_Ghierarcy.size();
		if(nhierak>=2)
			group_up=dynamic_cast<MGGroup*>(m_Ghierarcy[nhierak-2]);
		else
			group_up=m_group;
	}else{
		group_up=grp;
		gel_to_remove=m_object;
	}

	MGGroup::reverse_iterator i=group_up->rbegin(), iend=group_up->rend(), inext;
	while(i!=iend){
		inext=i; inext++;
		if(i->get()==gel_to_remove){
			i->release();
			group_up->erase(inext.base());//release the found gel
			break;
		}
		i=inext;
	}
}
