//#################################################################################
//
// BASE IMPLEMENTATION CLASS OF DLL interface Modification
//
//#################################################################################
//
//

#include "stdafx.h"
#include "ACP_Base.h"
#include "ITF.h"

#include "TAC.h"
#include "TFa.h"
#include "TAn.h"

#include "ACModif.hpp"
#include "ACInterf.hpp"
#include "x:\Cpa\Main\inc\_EditID.h"
#include "x:\Cpa\tempgrp\tia\inc\ai_intf.hpp"


///////////////////////////////////////////////////////////////////////////////
// MACROS
///////////////////////////////////////////////////////////////////////////////
#define M_ForEachElement(oList,p_oElement,Pos)\
            for( p_oElement = (oList) . GetHeadElement( Pos ) ;\
                 p_oElement ;\
                 p_oElement = (oList) . GetNextElement( Pos )\
                )

#define M_DeleteList(oList)\
    while( ! (oList) . IsEmpty() )\
        delete (oList) . RemoveHead();

#define M_FreeList(oList)\
    while( ! (oList) . IsEmpty() )\
        free( (oList) . RemoveHead() );

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// class Action_New

Action_New::Action_New(TAction_Interface *_p_oParentDLL, CPA_Family *_p_oFamily, char *_szNewName)
           :CPA_Modif(0, "New action", FALSE)
{
    m_p_oParentDLL = _p_oParentDLL ;
    m_p_oFamily = _p_oFamily ;
    m_szName = (char*) malloc ( strlen ( _szNewName ) + 1 ) ;
    strcpy ( m_szName , _szNewName )  ;
    m_p_oAction = NULL ;
}

Action_New::~Action_New()
{
    if( m_szName )
        free(m_szName);
    if( !HasBeenDone() && m_p_oAction )
        delete m_p_oAction;
}

BOOL Action_New::Do()
{
    // make data modif
    if( m_p_oAction == NULL )
    {
        // create action
        m_p_oAction = new CPA_Action( m_p_oParentDLL, m_p_oFamily, m_szName );
        m_p_oAction -> SetExistingSection( FALSE );
    }
    else
    {
        // Re-register action
        free(m_szName);
        m_szName = (char*) malloc ( m_p_oAction -> GetName() . GetLength() + 1 ) ;
        strcpy ( m_szName , (char*)(LPCTSTR)m_p_oAction -> GetName() )  ;
        m_p_oAction -> fn_bValidate();
    }
    // notify save
    m_p_oAction -> fn_vNotifySave();
    // add action to family
    m_p_oFamily -> mfn_vAddAction( m_p_oAction );
    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionCreated( m_p_oFamily , m_p_oAction );

    return TRUE;
}

BOOL Action_New::Undo()
{
    POSITION    xPos;

    // make data modif
    // remove action from family
    xPos = m_p_oFamily -> m_oListOfActions . Find ( m_p_oAction ) ;
    if( xPos )
        m_p_oFamily -> m_oListOfActions . RemoveAt ( xPos ) ;
    // unregister action
    m_p_oAction -> fn_bUnValidate();
    m_p_oAction -> fn_vNotifyUnSave();

    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionDeleted ( m_p_oFamily ) ;

    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// class Action_Copy

Action_Copy::Action_Copy(TAction_Interface *_p_oParentDLL, CPA_Family *_p_oFamily, CPA_Action *_p_oAction)
           :CPA_Modif(0, "Copy action", FALSE)
{
    m_p_oParentDLL = _p_oParentDLL ;
    m_p_oFamily = _p_oFamily ;
    m_p_oActionSource = _p_oAction ;
    m_p_oActionTarget = NULL ;
}

Action_Copy::~Action_Copy()
{
    if( !HasBeenDone() && m_p_oActionTarget )
    {
        // delete states
        while( ! m_p_oActionTarget -> m_oListOfStates . IsEmpty() )
        {
            delete m_p_oActionTarget -> m_oListOfStates . RemoveHead();
        }
        // delete action
        delete m_p_oActionTarget ;
    }
}

BOOL Action_Copy::Do()
{
    POSITION    xPos;
    CPA_State   *p_oState;

    // make data modif
    if( m_p_oActionTarget == NULL )
    {
        // create copy of action
        m_p_oActionTarget = m_p_oActionSource -> mfn_p_oDuplicateAction () ;

    }
    else
    {
        // Re-register action
        m_p_oActionTarget -> fn_bValidate();
        // Re-register states
        M_ForEachElement( m_oListOfStates, p_oState, xPos )
        {
            p_oState -> fn_bValidate();
            p_oState -> fn_vNotifySave();
            m_p_oActionTarget -> mfn_vAddState( p_oState );
        }
    }
    // notify to save state
    m_p_oActionTarget -> fn_vNotifySave();
    // add action to family
    m_p_oFamily -> mfn_vAddAction( m_p_oActionTarget );
    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionCreated( m_p_oFamily , m_p_oActionTarget );

    return TRUE;
}

BOOL Action_Copy::Undo()
{
    CPA_State   *p_oState;

    // make data modif
    m_oListOfStates . RemoveAll();
    // remove states from action
    while ( ! m_p_oActionTarget -> m_oListOfStates . IsEmpty() )
    {
        p_oState = m_p_oActionTarget -> m_oListOfStates . GetHead();
        //
        m_oListOfStates . AddTail( p_oState );
        //
        p_oState -> fn_bUnValidate();
        p_oState -> fn_vNotifyUnSave();
        //
        m_p_oActionTarget -> mfn_vRemoveState( p_oState );
    }
    // remove action from family
    m_p_oFamily -> mfn_vRemoveAction( m_p_oActionTarget ) ;
    // unregister action and state
    m_p_oActionTarget -> fn_bUnValidate();
    m_p_oActionTarget -> fn_vNotifyUnSave();

    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionDeleted ( m_p_oFamily ) ;

    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// class Action_Rename

Action_Rename::Action_Rename(TAction_Interface *_p_oParentDLL, CPA_Action *_p_oAction, char *_szNewName)
              :CPA_Modif(0, "Rename action", FALSE)
{
    m_p_oParentDLL = _p_oParentDLL ;
    m_p_oAction = _p_oAction ;
    m_szNewName = (char*) malloc ( strlen ( _szNewName ) + 1 ) ;
    strcpy ( m_szNewName , _szNewName )  ;
}

Action_Rename::~Action_Rename()
{
    if( m_szNewName )
        free(m_szNewName);
}

// actions
BOOL Action_Rename::Do()
{
    char *szLastName;

    // make data modif
    szLastName = (char*) malloc ( strlen ( m_p_oAction -> GetName () ) + 1 ) ;
    strcpy ( szLastName , m_p_oAction -> fn_p_szGetName () ) ;
    if( m_p_oAction -> fn_eRename ( m_szNewName ) != E_mc_None )
    {
        // error
        free( szLastName );
        return FALSE;
    }

    free ( m_szNewName ) ;
    m_szNewName = szLastName;
    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionCreated( (CPA_Family*) m_p_oAction -> GetOwner () , m_p_oAction ) ;

    return TRUE;
}

BOOL Action_Rename::Undo()
{
    return Do();
}

///////////////////////////////////////////////////////////////////////////////
// class Action_Delete

Action_Delete::Action_Delete(TAction_Interface *_p_oParentDLL, CPA_Family *_p_oFamily, CPA_Action *_p_oAction)
              :CPA_Modif(0, "Delete action", FALSE)
{
    m_p_oParentDLL = _p_oParentDLL ;
    m_p_oFamily = _p_oFamily ;
    m_p_oAction = _p_oAction ;
    m_p_oOACDLL = m_p_oParentDLL ? m_p_oParentDLL -> GetMainWorld() -> GetObjectDLLWithName( C_szDLLActorName ) : NULL;
    m_p_oDefaultState = _p_oAction -> mfn_p_oGetDefaultState();
}

Action_Delete::~Action_Delete()
{
    if( HasBeenDone() && m_p_oAction )
    {
        // delete states of action
        M_DeleteList( m_oListOfStates );
        // delete transitional states
		while( ! m_oListOfTransitionalStates . IsEmpty() )
		{
			tdstTSIState *p_oTSIState = m_oListOfTransitionalStates . RemoveHead();
			free( p_oTSIState -> p_stTSI );
			free( p_oTSIState );
		}
        // delete action
        delete m_p_oAction;
    }
}

// actions
BOOL Action_Delete::Do()
{
    POSITION    xPos;
    CPA_State   *p_oState;

    // make data modif
    m_oListOfStates . RemoveAll();
    m_oListOfTransitionalStates . RemoveAll ();
    // remove all states of action from family
    while( ! m_p_oAction -> m_oListOfStates . IsEmpty() )
    {
        p_oState = m_p_oAction -> m_oListOfStates . GetHead();
        // save State
        m_oListOfStates . AddTail( p_oState );
        // remove state from action
        m_p_oAction -> mfn_vRemoveState( p_oState );
        // unregister state
        p_oState -> fn_bUnValidate();
        p_oState -> fn_vNotifyUnSave();
    }
    // remove action from family
    m_p_oFamily -> mfn_vRemoveAction( m_p_oAction );
    // remove action from all transitional states
    M_ForEachElement( m_p_oFamily -> m_oListOfStates, p_oState, xPos )
    {
        tdstTransitionStateInfo *p_stTSI = p_oState -> mfn_p_stGetTransitionStateInfo( m_p_oAction );
        if( p_stTSI )
        {
			// backup
			tdstTSIState *p_stTSIState = (tdstTSIState*) malloc( sizeof tdstTSIState);
			p_stTSIState -> p_oState = p_oState;
			p_stTSIState -> p_stTSI = (tdstTransitionStateInfo*) malloc( sizeof tdstTransitionStateInfo );
			memcpy( p_stTSIState -> p_stTSI, p_stTSI, sizeof tdstTransitionStateInfo );
			m_oListOfTransitionalStates . AddTail( p_stTSIState );
            // remove from state
            p_oState -> mfn_vRemoveActionFromTransition( m_p_oAction );
            // notify modification
            if( p_oState -> fn_bIsValid() )
                p_oState -> fn_vNotifySave();
        }
    }
	
	// save al IA using this Action
    CPA_BaseObject *p_oFamily = m_p_oAction->GetOwner();
	CPA_List<CPA_BaseObject> oModelsList;

	m_p_oParentDLL->GetMainWorld()->fn_lFindObjects(&oModelsList, "", C_szActorModelTypeName, p_oFamily);

    g_oCoherenceManager . m_fn_vDeleteObject( m_p_oAction );
    if( m_p_oOACDLL )
        m_p_oOACDLL -> OnQueryAction( m_p_oParentDLL, C_uiActor_ActionDeleted, (LPARAM) m_p_oAction );
    // unregister action
    m_p_oAction -> fn_bUnValidate();
    m_p_oAction -> fn_vNotifyUnSave();

    if (oModelsList.GetCount())
    {
		CPA_DLLBase	*pclIADLL = (CPA_DLLBase*)(m_p_oParentDLL->GetMainWorld()->GetToolDLLWithName(C_szDLLAIEditorName));
		tdstActorsIACom	stCom;

        POSITION xPos = oModelsList.GetHeadPosition();
        while (xPos)
        {
            CPA_BaseObject *p_oBsObj = oModelsList.GetNext(xPos);
			stCom.pvThis=p_oBsObj;
			stCom.pvData=NULL;
			pclIADLL->OnQueryAction(m_p_oParentDLL,C_uiAI_ProcessIA, (LPARAM)&stCom);
        }
    }

    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionDeleted( m_p_oFamily );

    return TRUE;
}

BOOL Action_Delete::Undo()
{
    CPA_State      *p_oState;
    POSITION        xPos;

    // make data modif
    // restore action
    m_p_oFamily -> mfn_vAddAction( m_p_oAction ) ;
    // Re-register action
    m_p_oAction -> fn_bValidate();
    m_p_oAction -> fn_vNotifyRestore();
    // restore saved active transitional state
	while( ! m_oListOfTransitionalStates . IsEmpty() )
	{
		tdstTSIState *p_stTSIState = m_oListOfTransitionalStates . RemoveHead();
		if( p_stTSIState -> p_oState -> fn_bIsValid() )
		{
			p_stTSIState -> p_oState -> mfn_vSetTransitionStateInfo( p_stTSIState -> p_stTSI -> p_oAction,
																	 p_stTSIState -> p_stTSI -> eState,
																	 p_stTSIState -> p_stTSI -> p_oStateToGo );
			p_stTSIState -> p_oState -> fn_vNotifySave();
		}
		free( p_stTSIState -> p_stTSI );
		free( p_stTSIState );
	}

    // restore state
    M_ForEachElement( m_oListOfStates, p_oState, xPos )
    {
        m_p_oAction -> mfn_vAddState( p_oState ) ;
        // Re-register state
        p_oState -> fn_bValidate();
        p_oState -> fn_vNotifyRestore();
    }
    // restore default state
    m_p_oAction -> mfn_vSetDefaultState( m_p_oDefaultState );
    //
    g_oCoherenceManager . m_fn_vRestoreObject( m_p_oAction );
    if( m_p_oOACDLL )
        m_p_oOACDLL -> OnQueryAction( m_p_oParentDLL, C_uiActor_ActionRestored, (LPARAM) m_p_oAction );
    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionCreated( m_p_oFamily , m_p_oAction );

    return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// class Action_DelAllStates

Action_DelAllStates::Action_DelAllStates(TAction_Interface *_p_oParentDLL, CPA_Family *_p_oFamily, CPA_Action *_p_oAction)
                    :CPA_Modif(0, "Delete all states", FALSE)
{
    m_p_oParentDLL = _p_oParentDLL ;
    m_p_oFamily = _p_oFamily ;
    m_p_oAction = _p_oAction ;
    m_p_oDefaultState = _p_oAction -> mfn_p_oGetDefaultState();
}

Action_DelAllStates::~Action_DelAllStates()
{
    if( HasBeenDone() )
    {
        M_DeleteList( m_oListOfStates );
    }
}

// actions
BOOL Action_DelAllStates::Do()
{
    CPA_State      *p_oState;

    // copy all states of action
    m_oListOfStates . RemoveAll () ;
    while( ! m_p_oAction -> m_oListOfStates . IsEmpty() )
    {
        CPA_State      *p_oStateI;
        POSITION        xPosI;

        p_oState = m_p_oAction -> m_oListOfStates . GetHead();
        // copy state
        m_oListOfStates . AddTail ( p_oState ) ;
        // unregister state
        p_oState -> fn_bUnValidate() ;
        // remove from family
        m_p_oAction -> mfn_vRemoveState( p_oState );
        // remove from all states in whitch this state is the next state
        M_ForEachElement( p_oState -> m_oListOfUsedAsNextState, p_oStateI, xPosI )
        {
            if( p_oStateI -> mfn_p_oGetAction() != m_p_oAction )
                p_oStateI -> mfn_vSetNextState( NULL );
        }
        // remove from all state in whitch this state is a transitional state
        M_ForEachElement( p_oState -> m_oListOfUsedAsTransitional, p_oStateI, xPosI )
        {
			tdstTransitionStateInfo *p_stTSI =  p_oStateI -> mfn_p_stGetTransitionStateInfo( m_p_oAction );
			if( p_stTSI && ( p_stTSI -> eState == E_ts_Allowed ) )
			{
				p_oStateI -> mfn_vSetTransitionStateInfo( m_p_oAction, p_stTSI -> eState, NULL );
				if( p_oStateI -> fn_bIsValid() )
					p_oStateI -> fn_vNotifySave();
			}
        }
        p_oState -> fn_vNotifyUnSave();
    }
    // remove default state
    m_p_oAction -> mfn_vSetDefaultState( NULL );
    // Notify
    m_p_oAction -> fn_vNotifySave();

// Shaitan => optimisation lists in the level
	// set modification flag in the corresponding family
	((CPA_Family *) m_p_oAction->GetOwner())->mfn_vSetModifiedStateList();
// End Shaitan => optimisation lists in the level

    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionCreated( m_p_oFamily , m_p_oAction );

    return TRUE;
}

BOOL Action_DelAllStates::Undo()
{
    CPA_State      *p_oState;
    POSITION        xPos;

    // restore States
    M_ForEachElement( m_oListOfStates, p_oState, xPos )
    {
        CPA_State      *p_oStateI;
        POSITION        xPosI;

        // add state to action (and to family)
        m_p_oAction -> mfn_vAddState( p_oState );
        // Re-register state
        p_oState -> fn_bValidate();
        p_oState -> fn_vNotifyRestore();
        // restore from all state in whitch this state is the next state
        M_ForEachElement( p_oState -> m_oListOfUsedAsNextState, p_oStateI, xPosI )
        {
            if( p_oStateI -> mfn_p_oGetAction() != m_p_oAction )
                p_oStateI -> mfn_vSetNextState( p_oState );
        }
        // restore from all states in whitch this state is a transitional state
        M_ForEachElement( p_oState -> m_oListOfUsedAsTransitional, p_oStateI, xPosI )
        {
            if( p_oStateI -> mfn_p_oGetAction() != m_p_oAction )
            {
				tdstTransitionStateInfo *p_stTSI =  p_oStateI -> mfn_p_stGetTransitionStateInfo( m_p_oAction );
				if( p_stTSI && ( p_stTSI -> eState == E_ts_Allowed ) )
				{
					p_oStateI -> mfn_vSetTransitionStateInfo( m_p_oAction, p_stTSI -> eState, p_oState );
					if( p_oStateI -> fn_bIsValid() )
						p_oStateI -> fn_vNotifySave();
				}
            }
        }
    }

    // restore default state
    m_p_oAction -> mfn_vSetDefaultState( m_p_oDefaultState );
    // Notify
    m_p_oAction -> fn_vNotifySave();

// Shaitan => optimisation lists in the level
	// set modification flag in the corresponding family
	((CPA_Family *) m_p_oAction->GetOwner())->mfn_vSetModifiedStateList();
// End Shaitan => optimisation lists in the level

    // update display
    if( m_p_oParentDLL -> fn_bIsCurrentEditor () )
        m_p_oParentDLL -> mfn_vActionCreated( m_p_oFamily , m_p_oAction );

    return TRUE;
}
