/*
* Copyright (c) 2009 Stanford University, School of Medicine All rights reserved. 
* 
* STANFORD ACADEMIC SOFTWARE LICENSE AGREEMENT 
* FOR AngioLocalize SOURCE CODE: 
* 
* 1.  This is a legal agreement between you, RECIPIENT, and STANFORD UNIVERSITY.  
*       By accepting, receiving, and using AngioLocalize.including any accompanying information, materials or 
*       manuals (Program), you are agreeing to be bound by the terms of this Agreement.  
*       If you do not agree to the terms of this Agreement, promptly return the Program to the STANFORD.

* 2.  STANFORD grants to RECIPIENT a royalty-free, nonexclusive, and nontransferable license to use the Program 
*       and accompanying source code furnished hereunder, upon the terms and conditions set out below. 
*       documentation and/or other materials provided with the distribution. 
* 3.  RECIPIENT acknowledges that the Program is a research tool still in the development stage and that 
*       it is being supplied as is, without any accompanying services, support or improvements from STANFORD.
* 4.  RECIPIENT agrees to use the Program solely for internal non-commercial purposes. 
* 5.  RECIPIENT may create derivatives and further transfer the Program under the following terms and conditions: 
*        A) Recipient agrees to not commercially exploit the Program and agrees all uses will be for non-commercial purposes; and,
*        B) Recipient may not mark any derivatives with the title of Program and may not to use iPad mark for any derivatives; and,
*        C) Recipient agrees to mark and acknowledge all derivatives as follows: "This is a derivative work based on AngioLocalize:
*           Frameless AVM localization with 2D Digital Subtraction Angiography (DSA) and 3D Rotational Angiography (DRA) Images developed
*           by Dimitre Hristov, Lina Liu.
*        D) Recipient agrees to make all improvements, enhancements and derivatives available under 
*           similar terms and conditions to the research community. 
* 6.   RECIPIENT warrants that RECIPIENT will not remove or export any part of the software or Program from the United 
*       States except in full compliance with all United States and other applicable laws and regulations.
* 7.  RECIPIENT will use the Program in compliance with all applicable laws, policies and regulations including, but not 
*       limited to, any approvals, informed consent and patient confidentiality principles.
* 8.   RECIPIENT will indemnify, hold harmless, and defend Stanford against any claim of any kind arising out of or
*       related to the exercise of any rights granted under this Agreement or the breach of this Agreement by RECIPIENT.
* 9.  Title and copyright to the Program and any associated documentation shall at all times remain with STANFORD, 
*       and RECIPIENT agrees to preserve same.
*/ 
//----------------------------------------------------------------------------------
//! The ML module class PerspectiveTransformation.
/*!
// \file    mlPerspectiveTransformation.cpp
// \author  Lina L.
// \date    2009-12-09
//
// 
*/
//----------------------------------------------------------------------------------

// Local includes
#include "mlPerspectiveTransformation.h"

ML_START_NAMESPACE


//! Implements code for the runtime type system of the ML
ML_BASEOP_CLASS_SOURCE(PerspectiveTransformation, BaseOp);

//----------------------------------------------------------------------------------
//! Constructor
//----------------------------------------------------------------------------------
PerspectiveTransformation::PerspectiveTransformation ()
  : BaseOp(0, 0)
{
  ML_TRACE_IN("PerspectiveTransformation::PerspectiveTransformation ()");

  // Suppress calls of handleNotification on field changes to
  // avoid side effects during initialization phase.
  handleNotificationOff();

  //! Get reference to the container for parameters/fields.
  FieldContainer &fields = *getFieldContainer();

  ML_CHECK(&fields);
  
 //! Add fields to the module and set their values.
 /*_inputCSOStringFld = fields.addString("inputCSOString");

  _copySelectedCSOsFld = fields.addBool("copySelectedCSOs");
  _copySelectedCSOsFld->setBoolValue(true);

  _copiedCSOIdsFld = fields.addString("copiedCSOIds");*/
  
  _matrixFld = fields.addMatrix("matrix");
  _matrixFld->setStringValue("1 0 0 0 0 1 0 0 0 0 0 1 0 0 -1 0");
  
  _useCreatorIdFld = fields.addBool("useCreatorId");
  _useCreatorIdFld->setBoolValue(false);

  _creatorIdFld = fields.addInt("creatorId");
  _creatorIdFld->setIntValue(0);

  _targetGroupFld = fields.addString("targetGroup");
  
  _autoApplyFld = fields.addBool("autoApply");
  _autoApplyFld->setBoolValue(false);

  _applyFld = fields.addNotify("apply");

  static const char * const enumNames[2] = {
    "Off",
    "Auto Update"
  };

  (_updateModeFld = fields.addEnum("updateMode", enumNames, 2))->setEnumValue(0);

  //_workDirectlyOnInputListFld = fields.addBool("workDirectlyOnInputList");

  (_inListFld = fields.addBase("inCSOList"))->setBaseValue(NULL);

  // Generate field for marker list object.
  _outListFld = fields.addBase("outCSOList");

  _inputList = NULL;
  _outputList = NULL;
  // Link marker list field to internal marker list object.
  _outListFld->setBaseValue(NULL);

  (_listenToRepaintNotificationsFld          = fields.addBool("listenToRepaintNotifications"))
    ->setBoolValue(false);
  (_listenToFinishingNotificationsFld        = fields.addBool("listenToFinishingNotifications"))
    ->setBoolValue(true);
  (_listenToSelectionNotificationsFld       = fields.addBool("listenToSelectionNotifications"))
    ->setBoolValue(true);

  // Connect input fields with output field to update connected
  // modules if input images change.
  for (int inIdx=0; inIdx < 0; ++inIdx){
    for (int outIdx=0; outIdx < 0; ++outIdx){
      getInField(inIdx)->attachField(getOutField(outIdx));
    }
  }
  // Reactivate calls of handleNotification on field changes.
  handleNotificationOn();

  // Activate inplace data buffers for output outIndex and input inIndex.
  // setOutImgInplace(outIndex, inIndex);

  // Activate page data bypass from input inIdx to output outIdx.
  // Note that the module must still be able to calculate the output image.
  // setBypass(outIndex, inIndex);

  // Activate parallel execution of calcOutSubImage.
  // setThreadSupport(supportMode);
  // with supportMode =
  //   NO_THREAD_SUPPORT                 //! The module is not thread safe at all.
  //   CALC_OUTSUBIMAGE_ON_STD_TYPES     //! calcOutSubImage can be called in parallel for scalar voxel types.
  //   CALC_OUTSUBIMAGE_ON_CARRIER_TYPES //! calcOutSubImage can be called in parallel for carrier voxel types.
  //   CALC_OUTSUBIMAGE_ON_ALL_TYPES     //! calcOutSubImage can be called in parallel for all voxel types.
  // Warning: You should be familiar with multithreading before activating this feature.

  // Specify whether the module can only process standard scalar voxel types or
  // also registered voxel types (vec2, mat2, complexf, Vector, etc.)
  // setVoxelDataTypeSupport(permittedTypes);
  // with permittedTypes =
  //   ONLY_STANDARD_TYPES               //! Only standard scalar voxels are supported.
  //   FULLY_OPERATIONAL                 //! Scalar and registered voxels types are supported.
  //   MINIMUM_OPERATIONAL               //! Scalar and registered voxel types are supported.
  //                                     //! Voxel operators are not used by algorithm.
  //
  // See ML Programming Guide, "Configuring Image Processing Behaviour of the BaseOp"
  // for further details.
}

//----------------------------------------------------------------------------------
//! This method use perspective transformation matrix to do the tranformation on inputCSOList
//! The result is put to outListFld.
//----------------------------------------------------------------------------------
void    PerspectiveTransformation::transform (const mat4& matrix)
{
	//! if inputList is NULL, we donot need to go further
	if (_inputList == NULL) {
		_outListFld->setBaseValue(NULL);
		return;
	}

	_outList = new CSOList(*_inputList);
	//! always copy the inputList into outList
	//  and put the result in it.
	/*if (_workDirectlyOnInputListFld->getBoolValue() == false) {
		_outList = new CSOList(*_inputList);
	} else {
		_outList = _inputList;
	}*/
	//! add group to the outputCSO list
	std::string groupName = this->_targetGroupFld->getStringValue();
	CSOGroup* group = NULL;
	if ( groupName!= "") {
		group = _outList->addGroup();
		group->setLabel(groupName);
	}

    //! add creatorID to the output CSO List
	int userID = 0;
	if (_useCreatorIdFld->getBoolValue() == true) {
		userID = _creatorIdFld->getIntValue();
	}

	for (unsigned int i = 0; i < _outList->numCSO(); i++) {
		CSO* cso = _outList->getCSOAt(i);
		if (group) {
			group->addCSO(cso);
		}
		if (userID != 0) {
			cso->setCreatorId(_creatorIdFld->getIntValue());
		}
		for (unsigned int j = 0; j < cso->numPathPointLists(); j ++) {
			CSOPathPoints *point = cso->getPathPointsAt(j);
			std::vector<vec3>  positions;
			point->getWorldPositions(positions);
			for (unsigned int m = 0; m < positions.size(); m++) {
				//apply matrix here for each position in world coordinate
				//positions[m] = (positions[m] * matrix).divideByLastComp();
				positions[m] = (Tvec3<double>(matrix * positions[m].affinePoint(), true)).divideByLastComp();
			}
			point->setWorldPositions(positions);
		}
	}
	_outListFld->setBaseValue(_outList);
}

//! Handle field changes of the field field.
//----------------------------------------------------------------------------------
void PerspectiveTransformation::handleNotification (Field *field)
{
  ML_TRACE_IN("perspectiveTransformation::handleNotification ()")

  //! if auto Update mode is on, we handle all events
  if (_updateModeFld->getEnumValue() == 1 || _autoApplyFld->getBoolValue() == true ||
      field == _applyFld || field == _inListFld){

    if (!_isInNotificationCB) { 
		CSOList::removeNotificationObserverFromAllCSOLists(_csoListNotificationCB, this);
	}

    if (ML_BASE_IS_A((static_cast<Base*>(_inListFld->getBaseValue())), CSOList)){

      _inputList = static_cast<CSOList*>(_inListFld->getBaseValue());

      if (_inputList != NULL){

        if (!_isInNotificationCB) { 
			_inputList->addNotificationObserver(_csoListNotificationCB, this); 
		}
      }
	} else {
      _inputList = NULL;
	}
	//! apply the changes
	transform(_matrixFld->getMatrixValue());
  }
  
  //! Handle changes of module parameters and connectors here.
  BaseOp::handleNotification(field);

}

//----------------------------------------------------------------------------------
//! Update internal module state after load or clone and enable notification handling again.
//----------------------------------------------------------------------------------
void PerspectiveTransformation::activateAttachments ()
{
  ML_TRACE_IN("perspectiveTransformation::activateAttachments ()")
  
  //! Update members to new field state here.
  //! Call super class functionality to enable notification handling again.
  BaseOp::activateAttachments ();
}


void PerspectiveTransformation::_csoListNotificationCB(void* userData, int notificationFlag)
{
  ML_TRACE_IN("void perspectiveTransformation::_csoListNotificationCB()");

  PerspectiveTransformation* thisp = static_cast<PerspectiveTransformation*>(userData);

  thisp->_isInNotificationCB = true;

  if ((notificationFlag & CSOList::NOTIFICATION_CSO_SELECTION) ||
      (notificationFlag & CSOList::NOTIFICATION_GROUP_SELECTION))
  {
	  if (thisp->_updateModeFld->getEnumValue() == 1 && 
		  thisp->_listenToSelectionNotificationsFld->getBoolValue()){
        thisp->transform(thisp->_matrixFld->getMatrixValue());
      }
  }
  if ((notificationFlag & CSOList::NOTIFICATION_CSO_FINISHED) ||
      (notificationFlag & CSOList::NOTIFICATION_GROUP_FINISHED))
  {
	  if (thisp->_updateModeFld->getEnumValue() == 1 &&
		  thisp->_listenToFinishingNotificationsFld->getBoolValue()){
        thisp->transform(thisp->_matrixFld->getMatrixValue());
      }
  }
  if (notificationFlag & CSOList::NOTIFICATION_REPAINT){
	  if (thisp->_updateModeFld->getEnumValue() == 1 && 
		  thisp->_listenToRepaintNotificationsFld->getBoolValue()){
      thisp->transform(thisp->_matrixFld->getMatrixValue());
    }
  }

  thisp->_isInNotificationCB = false;
}

ML_END_NAMESPACE

