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

// This class serves as MGAppearance update action
// It defines the following operations:
//		void Do();
//			--Notify the undo manager,
//			--Perform operation
//
//		void Undo();
//			-- delete the added attrib and add removed attrib.
//
//		void Redo();
//			-- delete the removed attrib and add added attrib.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "mg/Group.h"
#include "mgGL/Appearance.h"
#include "fugenView.h"
#include "Undo/GelAttribUpdateAction.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//Void action.
CGelAttribUpdateAction::CGelAttribUpdateAction(fugenDoc *doc)
:CDocAction(doc),m_remove_type(MGALL_TID){;}

//Add or replace action.
//When objs already have the same type attribute as attrib,
//this will replace the old MGGLAttrib, otherwise add attrib to MGAttribedGel.
CGelAttribUpdateAction::CGelAttribUpdateAction(
	fugenDoc *doc,
	const MGGelPositions& objs,//MGAttribedGel that the following attrib will include.
	const MGGLAttrib& attrib//adding attrib when gel does not have the same type MGGLAttrib,
				//and replacing when gel have the same type.
):CDocAction(doc),m_remove_type(MGALL_TID){
	m_added.emplace_back(attrib.clone());
	size_t n=objs.size();
	for(size_t i=0; i<n; i++){
		const MGGelPosition& gp=objs[i];
		const MGAttribedGel* agel=dynamic_cast<const MGAttribedGel*>(gp.leafAttribedGel());
		if(!agel)
			continue;
		m_agels.push_back(gp);
	}
}

CGelAttribUpdateAction::CGelAttribUpdateAction(
	fugenDoc *doc,
	const MGGelPositions& objs,//MGAttribedGel that the following attrib will include.
	UniqueGLAttribVec&& attribs//adding attrib when gel does not have the same type MGGLAttrib,
		//and replacing when gel have the same type.
		//The ownership of attribs will be transfered to CGelAttribUpdateAction.
):CDocAction(doc),m_remove_type(MGALL_TID){
	m_added=std::move(attribs);
	size_t n=objs.size();
	for(size_t i=0; i<n; i++){
		const MGGelPosition& gp=objs[i];
		const MGAttribedGel* agel=dynamic_cast<const MGAttribedGel*>(gp.leafAttribedGel());
		if(!agel)
			continue;
		m_agels.push_back(gp);
	}
}

//Remove action. Remove the MGGLAttrib whose type is type_id.
CGelAttribUpdateAction::CGelAttribUpdateAction(
	fugenDoc *doc,
	const MGGelPositions& objs,//MGAttribedGel that the following attrib will be removed from.
	MGGEL_TID type_id  //MGAttrib id that should be removed.
):CDocAction(doc),m_remove_type(type_id){
	size_t n=objs.size();
	for(size_t i=0; i<n; i++){
		const MGGelPosition& gp=objs[i];
		const MGAttribedGel* agel=dynamic_cast<const MGAttribedGel*>(gp.leafAttribedGel());
		if(!agel)
			continue;
		m_agels.push_back(gp);
	}
}

UniqueGLAttribVec& CGelAttribUpdateAction::added_attrib() { return m_added; }
const UniqueGLAttribVec& CGelAttribUpdateAction::added_attrib()const { return m_added; }
MGGelPositions& CGelAttribUpdateAction::agels() { return m_agels; }
const MGGelPositions& CGelAttribUpdateAction::agels()const { return m_agels; }
std::vector<UniqueGLAttribVec>& CGelAttribUpdateAction::removed() { return m_removed; }
const std::vector<UniqueGLAttribVec>& CGelAttribUpdateAction::removed()const { return m_removed; }
long CGelAttribUpdateAction::remove_type()const { return m_remove_type; }

//process for MGAttribedGel.
void CGelAttribUpdateAction::do_action(ACTION_TYPE a){
	size_t n=m_agels.size();
	if(a==ACTION_DO){
		for(size_t i=0; i<n; i++){
			MGGelPosition& gp=m_agels[i];
			MGAttribedGel* agel=gp.targetGel();

			UniqueGLAttribVec remvdAtrib;
			size_t natrib=m_added.size();
			if(natrib){
				for(size_t j=0; j<natrib; j++){
					MGAppearance* appr=agel->ensure_appearance();
					MGGLAttrib* newone=m_added[j]->clone();
					std::unique_ptr<MGGLAttrib> oldone(appr->set_attrib_with_old(newone));
					remvdAtrib.emplace_back(oldone.release());
				}
			}else if(m_remove_type!=MGALL_TID){
				MGAppearance* appr=agel->appearance();
				if(appr){
					std::unique_ptr<MGGLAttrib> oldone(appr->release_attrib(m_remove_type));
					remvdAtrib.emplace_back(oldone.release());
				}
			}
			m_removed.push_back(std::move(remvdAtrib));
			agel->setDirty(true);
		}
	}else{
		for (int i = (int)(n - 1); i >= 0; i--) {
			UniqueGLAttribVec& original = m_removed.back();
			if (original.size()) {
				MGGelPosition& gp = m_agels[i];
				MGAttribedGel* agel = gp.targetGel();

				size_t nadded = m_added.size();
				size_t natrib = original.size();
				for (size_t j = 0; j < natrib; j++) {
					MGGLAttrib* attr = original[j].release();
					if (attr)
						agel->set_GLattrib(attr);
					else {
						if (nadded) {
							long atrtid = m_added[j]->identify_type();
							agel->remove_GLattrib(atrtid);
						}
					}
				}
				agel->setDirty(true);
			}
			m_removed.pop_back();
		}
	}
}

void CGelAttribUpdateAction::Do(){
	CDocAction::Do();

// process for MGAttribedGel.
	do_action(ACTION_DO);
	update_all_views(ACTION_DO);
}

void CGelAttribUpdateAction::Undo(){
// process for MGAttribedGel.
	do_action(ACTION_UNDO);
	update_all_views(ACTION_UNDO);
	CDocAction::Undo();
}

void CGelAttribUpdateAction::Redo(){
	CDocAction::Redo();

//process for MGAttribedGel.
	do_action(ACTION_DO);
	update_all_views(ACTION_DO);
}

//Update the views. This will be invoked by CDocAction::update_all_views,
//when DO, Undo, or Redo operations are invoked.
void CGelAttribUpdateAction::view_update(ACTION_TYPE at){
	size_t n=m_agels.size();
	for(size_t i=0; i<n; i++){
		MGGelPosition& gp=m_agels[i];
		MGAttribedGel* agel=gp.targetGel();

		//agel->make_display_list(MGCL::WIRE_AND_SHADING);
		MGAppearance* appri=agel->appearance();
		if(appri){
			mgVBO* vboi=agel->dlist_name();
			vboi->clearStaticAttributes();
			appri->drawAttrib(*vboi);
		}
	}
}
