/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. 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 .
 */
/*
 * Action.cpp
 *
 *  Created on: Dec 8, 2009
 *      Author: crueger
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include 
#include 
#include 
#include "Actions/Action.hpp"
#include "Actions/ActionExceptions.hpp"
#include "Actions/ActionRegistry.hpp"
#include "Actions/ActionHistory.hpp"
#include "Actions/OptionRegistry.hpp"
#include "Actions/OptionTrait.hpp"
#include "UIElements/Dialog.hpp"
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/MemDebug.hpp"
#include "UIElements/UIFactory.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Verbose.hpp"
using namespace MoleCuilder;
Action::state_ptr getEmptyState() {
  return Action::state_ptr(Memory::ignore(new ActionState()));
}
void Action::removeStaticStateEntities()
{
  Action::success.reset();
  Action::failure.reset();
}
void Action::createStaticStateEntities()
{
  Action::success = getEmptyState();
  Action::failure = getEmptyState();
}
// An empty state to indicate success, these are (de)initialized by ActionHistory
Action::state_ptr Action::success;
Action::state_ptr Action::failure;
Action::Action(const ActionTrait &_Traits, bool _doRegister) :
    Traits(_Traits)
{
  // register with ActionRegistry
  if(_doRegister){
    ActionRegistry::getInstance().registerInstance(this);
  }
  // register with OptionRegistry
  for (ActionTrait::options_const_iterator optioniter = Traits.getBeginIter();
      optioniter != Traits.getEndIter();
      ++optioniter) {
    // options may have been re-used by other Actions, so check beforehand whether adding is needed
    if (!OptionRegistry::getInstance().isOptionPresentByName((optioniter->first))) {
      OptionRegistry::getInstance().registerInstance(optioniter->second);
    } else { // if present, ASSERT that types coincide
#ifndef NDEBUG
      OptionTrait const * const PresentOption = OptionRegistry::getInstance().getOptionByName(optioniter->first);
#endif
      ASSERT(PresentOption->getType() == optioniter->second->getType(),
          ("Action::Action() - option to add "+
              std::string(optioniter->first)+
              " of Action "+
              std::string(getName())+
              " is already present with different type: "
              +toString(PresentOption->getType())+" != "+toString(optioniter->second->getType())
          )
      );
      ASSERT(PresentOption->getDefaultValue() == optioniter->second->getDefaultValue(),
          ("Action::Action() - option to add "+
              std::string(optioniter->first)+
              " of Action "+
              std::string(getName())+
              " is already present with different default value: "
              +PresentOption->getDefaultValue()+" != "+optioniter->second->getDefaultValue()
          )
      );
      ASSERT(PresentOption->getShortForm() == optioniter->second->getShortForm(),
          ("Action::Action() - option to add "+
              std::string(optioniter->first)+
              " of Action "+
              std::string(getName())+
              " is already present with different short form: "
              +PresentOption->getShortForm()+" != "+optioniter->second->getShortForm()
          )
      );
    }
  }
}
Action::~Action()
{
  // unregister us if still registered
  if(ActionRegistry::getInstance().isActionPresentByName(getName()))
    if (ActionRegistry::getInstance().getActionByName(getName()) == this)
      ActionRegistry::getInstance().unregisterInstance(this);
  for (ActionTrait::options_const_iterator optioniter = Traits.getBeginIter();
      optioniter != Traits.getEndIter();
      ++optioniter) {
    // unregister option if still registered
    if(OptionRegistry::getInstance().isOptionPresentByName((optioniter->first)))
      if (OptionRegistry::getInstance().getOptionByName((optioniter->first)) == optioniter->second) {
        OptionRegistry::getInstance().unregisterInstance(optioniter->second);
    }
  }
}
const string Action::getName() const
{
  return Traits.getName();
}
const std::string Action::help() const
{
  std::stringstream outputstream;
  outputstream << "Description for Action '" << getName() << "': " << Traits.getDescription()
      << std::endl;
  if (!Traits.hasOption(getName())) {
    outputstream << "\tNote that this Action does not take an argument." << std::endl;
  }
  outputstream << "Options available for action '" << getName() << "':" << std::endl;
  for (ActionTrait::options_const_iterator iter = Traits.getBeginIter();
      iter != Traits.getEndIter();
      ++iter) {
    outputstream << "Option '" << iter->first << "':" << std::endl;
    outputstream << "\tDescription: " << iter->second->getDescription() << "." << std::endl;
    outputstream << "\tArgument's type: " << iter->second->getTypeName() << "." << std::endl;
    outputstream << "\tDefault value: ";
    if (iter->second->hasDefaultValue()) {
      outputstream << "Yes, is '" << iter->second->getDefaultValue() << "'";
    } else {
      outputstream << "None";
    }
    outputstream << "." << std::endl;
  }
  return outputstream.str();
}
Dialog * Action::createDialog(){
  Dialog *dialog = UIFactory::getInstance().makeDialog();
  return fillDialog(dialog);
}
void Action::call(enum QueryOptions flag){
  if(!isActive()){
    return;
  }
  // forward to private virtual
  if (flag == Interactive) {
    Dialog* dialog = createDialog();
    if (dialog->hasQueries()) {
      if (!dialog->display())
        // dialog error or aborted -> throw exception
        throw ActionFailureException() << ActionNameString(getName());
    }
    delete(dialog);
  }
  state_ptr state = Action::failure;
//  try {
    startTimer();
    state = performCall();
    endTimer();
//  } catch(MissingValueException&) {
//    ELOG(0, "There is a value missing for action " << getName());
//  };
  if(shouldUndo() && state != failure){
    if(canUndo()){
      ActionHistory::getInstance().addElement(this,state);
    }
    else{
      ActionHistory::getInstance().clear();
    }
  }
  // throw an exception that can be caught in case of failure
  if (state == Action::failure)
    throw ActionFailureException() << ActionNameString(getName());
}
Action::state_ptr Action::undo(state_ptr _state) {
  // forward to private virtual
  return performUndo(_state);
}
Action::state_ptr Action::redo(state_ptr _state) {
  // forward to private virtual
  return performRedo(_state);
}
bool Action::isActive(){
  return true;
}