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

// GelAction.h: interface for the mgGelAction class.
//mgGelAction provides std::vector<SYSGLS> m_sysgls_vector functions.
//m_sysgls_vector[i] corresponds to m_target_gels[i] for 0<=i<m_target_gels.size().
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <set>
#include "mgGL/SysGL.h"
#include "Undo/mgGelAction.h"
#include "fugenView.h"


mgGelAction::mgGelAction(
	const MGGelPosition& target,
	fugenDoc* doc
):m_doc(doc),m_target_gels(target){;}

mgGelAction::mgGelAction(
	const MGGelPositions& targets,
	fugenDoc* doc
):m_doc(doc),m_target_gels(targets){
}

mgGelAction::~mgGelAction(){
	gls_iterator j=m_sysgls_vector.begin(), jend=m_sysgls_vector.end();
	for(; j!=jend; j++){
		gl_iterator k=(*j).begin(), kend=(*j).end();
		for(; k!=kend; k++){
			delete *k;
		}
	}
}

//Add the target gels into the document.
//appendCurrent specifies if the added object be appended
//to the current objects(that are hilighted).
//When appendCurrent=true, the objects will be appended to the currrent objects.
//Else, the objects will be the only current ones.
void mgGelAction::add_doc(bool appendCurrent){
	MGGelPositions& gels=target_gels();
	MGGelPositions::iterator i=gels.begin(), ie=gels.end();
	for(; i!=ie; i++)
		i->do_add();

	//Modify the current objects.
	fugenDoc& doc=*(document());
	if(appendCurrent)
		doc.append_current_object(gels,false);
	else
		doc.set_current_object(gels,false);
	doc.set_last_added(gels);
}

void mgGelAction::add_view(){
	MGGelPositions::reverse_iterator i=m_target_gels.rbegin(), ie=m_target_gels.rend();
	if(i==ie)
		return;

	MGOpenGLView& glv=glview();
	size_t nglsvec=sysgls_vector_size();
	for(; i!=ie; i++){
		MGAttribedGel* gel=i->top_object();
		if(!gel){//When leaf is MGGroup.
			gel=i->leafAttribedGel();
		}
		MGGroup* grpi=i->bottom_group();
		mgVBO* grpiVbo=grpi->dlist_name();
		grpiVbo->drawGel(*gel);

		if(!nglsvec)
			continue;
		SYSGLS& gls=sysgls_back();
		size_t ngls=gls.size();
		for(size_t k=0; k<ngls; k++){
			mgSysGL* gl=gls[k];
			gl->makeSysGLDisplayList(glv);
		}
		sysgls_pop_back();
	}
}

//Copy gelAction2's sysgl replacing the gel pointer from gelAction2's gel
//to this gel.
void mgGelAction::copy_sysgl(const mgGelAction& gelAction2){
	MGGelPositions& gpos1=target_gels();
	const MGGelPositions& gpos2=gelAction2.target_gels();
	if(!gpos1.symmetric(gpos2))
		return;

	size_t n1=gpos1.size();
	size_t n2=gpos2.size();
	for(size_t i=0; i<n2; i++){
		std::vector<UniqueSysGL> sgls_new;
		const MGGelPosition& gpi2=gpos2[i];
		MGGelPosition& gpi1=gpos1[i];
		const mgGelAction::SYSGLS& sgls2=gelAction2.sysgls(i);
		size_t ngls=sgls2.size();
		const MGGel* gel2=gpi2.top_object();
		const MGGel* gel1=gpi1.top_object();
		for(size_t j=0; j<ngls; j++){
			mgSysGL* sglj=sgls2[j]->clone();
			sglj->replace(gel2,gel1);
			sgls_new.emplace_back(sglj);
		}
		sysgls_push_back(std::move(sgls_new));
	}
}

//Delete the target gels from the document.
void mgGelAction::delete_doc(){
	//Modify the current objects.
	fugenDoc& doc=*(document());
	MGGelPositions& gels=target_gels();
	doc.remove_current(gels);

	MGGelPositions::iterator i=gels.begin(), ie=gels.end();
	for(; i!=ie; i++)
		i->do_remove();
}

void mgGelAction::delete_view(){
	MGGelPositions::iterator i=m_target_gels.begin(), ie=m_target_gels.end();
	if(i==ie)
		return;

	MGOpenGLView& glv=glview();
	sysgls_clear_vector();
	for(; i!=ie; i++){
		std::vector<UniqueSysGL> functions;	//mgSysGL pointer will be output.
		MGAttribedGel* gel=i->top_object();
		if(!gel){
			gel=i->leafAttribedGel();
		}
		MGGroup* grpi=i->bottom_group();
		mgVBO* grpiVbo=grpi->dlist_name();
		grpiVbo->deleteGel(*gel);
		glv.m_sysgllist.delete_lists_by_object_id(gel,functions);
		sysgls_push_back(std::move(functions));
	}
}

void mgGelAction::delete_target_gels(){
	MGGelPositions::reverse_iterator i=m_target_gels.rbegin(), ie=m_target_gels.rend();
	for(; i!=ie; i++){
		MGGel* gel=(*i).top_object();
		delete gel;
	}
}

//Push back a target gel position
void mgGelAction::push_back_gelp(const MGGelPosition& target){
	m_target_gels.push_back(target);
}

//Get the glview of the doc.
MGOpenGLView& mgGelAction::glview()const{
	fugenDoc* doc=document();
	return *(doc->get_main_view());
}

//Replace this GelAction's display list with to_add's.
//Delete the display lists of this action and add into to_add.
//The display lists of mgSysGL will be also replaced.
void mgGelAction::replace_display_list(
	mgGelAction& to_add
){
	delete_view();
	if(sysgls_vector_size()>0 && to_add.sysgls_vector_size()==0){
		//When replace mgSysGL's are not already generated.
		to_add.copy_sysgl(*this);
	}
	to_add.add_view();
}

//append functions to m_sysgls_vector.
//All of the ownership of mgSysGL* of functions are transfered to this action.
void mgGelAction::sysgls_push_back(std::vector<UniqueSysGL>&& functions){
	size_t n=functions.size();
	SYSGLS gls(n);
	for(size_t i=0; i<n; i++){
		gls[i]=functions[i].release();
	}
	m_sysgls_vector.push_back(gls);
}

//clear m_sysgls_vector;
void mgGelAction::sysgls_clear_vector(){
	size_t n=m_sysgls_vector.size();
	for(size_t i=0; i<n; i++){
		SYSGLS& gls=m_sysgls_vector.back();
		gl_iterator k=gls.begin(), kend=gls.end();
		for(; k!=kend; k++){
			delete *k;
		}
		m_sysgls_vector.pop_back();
	}
}
