/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2013 University of Bonn. All rights reserved.
 * Copyright (C)  2013 Frederik Heber. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoleCuilder.  If not, see .
 */
/*
 * SubgraphDissectionAction.cpp
 *
 *  Created on: Mar 1, 2013
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Chronos.hpp"
#include "Actions/GraphAction/CreateAdjacencyAction.hpp"
#include "Actions/GraphAction/DestroyAdjacencyAction.hpp"
#include "Actions/GraphAction/UpdateMoleculesAction.hpp"
#include "Actions/GraphAction/SubgraphDissectionAction.hpp"
#include "Actions/ActionRegistry.hpp"
#include "Descriptors/AtomDescriptor.hpp"
#include "SubgraphDissectionAction.def"
// some derived names: if CATEGORY is not given, we don't prefix with it
#ifdef CATEGORY
#define ACTION BOOST_PP_CAT(CATEGORY, BOOST_PP_CAT(ACTIONNAME, Action))
#define COMMAND BOOST_PP_CAT(CATEGORY, ACTIONNAME)
#define PARAMS BOOST_PP_CAT(CATEGORY, BOOST_PP_CAT(ACTIONNAME, Parameters))
#else
#define ACTION BOOST_PP_CAT(ACTIONNAME, Action)
#define COMMAND ACTIONNAME
#define PARAMS BOOST_PP_CAT(ACTIONNAME, Parameters)
#endif
// check if no lists given
#ifndef paramtypes
#define MAXPARAMTYPES 0
#else
#define MAXPARAMTYPES BOOST_PP_SEQ_SIZE(paramtypes)
#endif
#ifndef paramdefaults
#define MAXPARAMDEFAULTS 0
#else
#define MAXPARAMDEFAULTS BOOST_PP_SEQ_SIZE(paramdefaults)
#endif
namespace MoleCuilder {
// static instances
ActionSequence GraphSubgraphDissectionAction::actions;
bool GraphSubgraphDissectionAction::isPrepared = false;
GraphSubgraphDissectionAction::GraphSubgraphDissectionAction() :
  MakroAction(ActionTraits< GraphSubgraphDissectionAction >(), actions, false)
{}
GraphSubgraphDissectionAction::~GraphSubgraphDissectionAction()
{}
void GraphSubgraphDissectionAction::prepare(ActionRegistry &AR)
{
  actions.addAction(AR.getActionByName(std::string("destroy-adjacency")));
  actions.addAction(AR.getActionByName(std::string("create-adjacency")));
  actions.addAction(AR.getActionByName(std::string("update-molecules")));
  isPrepared = true;
}
void GraphSubgraphDissectionAction::unprepare(ActionRegistry &AR)
{
  // empty sequence
  while (actions.removeLastAction() != NULL);
  isPrepared = false;
}
void reselectAtoms(const std::vector &selected_atoms)
{
  World::getInstance().clearAtomSelection();
  for (std::vector::const_iterator iter = selected_atoms.begin();
      iter != selected_atoms.end();
      ++iter)
    World::getInstance().selectAtom(*iter);
}
Action::state_ptr GraphSubgraphDissectionAction::performCall(){
  // we need to "emulate" that all atoms have been selected without destroying
  // current selection
  Chronos::getInstance().startTiming(TOKEN);
  const std::vector selected_atoms = World::getInstance().getSelectedAtoms();
  World::getInstance().selectAllAtoms(AllAtoms());
  Action::state_ptr state(MakroAction::performCall());
  reselectAtoms(selected_atoms);
  Chronos::getInstance().endTiming(TOKEN);
  return state;
}
Action::state_ptr GraphSubgraphDissectionAction::performUndo(Action::state_ptr _state) {
  // we need to "emulate" that all atoms have been selected without destroying
  // current selection
  const std::vector selected_atoms = World::getInstance().getSelectedAtoms();
  World::getInstance().selectAllAtoms(AllAtoms());
  Action::state_ptr state(MakroAction::performUndo(_state));
  reselectAtoms(selected_atoms);
  return state;
}
Action::state_ptr GraphSubgraphDissectionAction::performRedo(Action::state_ptr _state){
  // we need to "emulate" that all atoms have been selected without destroying
  // current selection
  const std::vector selected_atoms = World::getInstance().getSelectedAtoms();
  World::getInstance().selectAllAtoms(AllAtoms());
  Action::state_ptr state(MakroAction::performRedo(_state));
  reselectAtoms(selected_atoms);
  return state;
}
// =========== command for calling action directly ===========
void COMMAND(
#if defined paramtypes && defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
#define BOOST_PP_LOCAL_MACRO(n) type_list(~, n, paramtypes, paramreferences)
#define BOOST_PP_LOCAL_LIMITS  (0, MAXPARAMTYPES-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
)
{
  ACTION *ToCall = dynamic_cast(ActionRegistry::getInstance().getActionByName( TOKEN )); //->clone(params);
  //ACTION::PARAMS params;
#if defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
#define BOOST_PP_LOCAL_MACRO(n) value_print(~, n, paramreferences, ToCall->params.)
#define BOOST_PP_LOCAL_LIMITS  (0, MAXPARAMTYPES-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
  ToCall->call(Action::NonInteractive);
};
void BOOST_PP_CAT( COMMAND, _stringargs)(
#if defined paramtypes && defined paramreferences && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
#define BOOST_PP_LOCAL_MACRO(n) type_list(~, n, BOOST_PP_SEQ_TRANSFORM( type2string, , paramtypes), paramreferences)
#define BOOST_PP_LOCAL_LIMITS  (0, MAXPARAMTYPES-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
  ) {
  ACTION *ToCall = dynamic_cast(ActionRegistry::getInstance().getActionByName( TOKEN )); //->clone(params);
  //ACTION::PARAMS params;
#if defined paramtypes && defined paramtypes && BOOST_PP_NOT_EQUAL(MAXPARAMTYPES,0)
#define BOOST_PP_LOCAL_MACRO(n) valuetype_print(~, n, paramreferences, paramtypes, ToCall->params. )
#define BOOST_PP_LOCAL_LIMITS  (0, MAXPARAMTYPES-1)
#include BOOST_PP_LOCAL_ITERATE()
#endif
  ToCall->call(MoleCuilder::Action::NonInteractive);
};
}