/*************************************************************************
 *    CompuCell - A software framework for multimodel simulations of     *
 * biocomplexity problems Copyright (C) 2003 University of Notre Dame,   *
 *                             Indiana                                   *
 *                                                                       *
 * This program is free software; IF YOU AGREE TO CITE USE OF CompuCell  *
 *  IN ALL RELATED RESEARCH PUBLICATIONS according to the terms of the   *
 *  CompuCell GNU General Public License RIDER 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.               *
 *                                                                       *
 * This program 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 this program; if not, write to the Free Software       *
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.        *
 *************************************************************************/

#include "SurfaceFlexEnergy.h"

#include <CompuCell3D/Automaton/Automaton.h>
#include <CompuCell3D/Field3D/Field3D.h>
#include <CompuCell3D/Field3D/Point3D.h>
#include <CompuCell3D/Potts3D/Potts3D.h>


using namespace CompuCell3D;

#include <XMLCereal/XMLPullParser.h>
#include <XMLCereal/XMLSerializer.h>

#include <BasicUtils/BasicString.h>
#include <BasicUtils/BasicException.h>

#include <string>
using namespace std;

double SurfaceFlexEnergy::localEnergy(const Point3D &pt) {
  return 0;
}

double SurfaceFlexEnergy::diffEnergy(int surface, int diff, unsigned int targetSurface, float lambdaSurface){

  return lambdaSurface *
    (diff * diff + 2 * diff * (surface - (int)targetSurface));

}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
double SurfaceFlexEnergy::changeEnergy(const Point3D &pt,
				  const CellG *newCell,
				  const CellG *oldCell) {

  // E = lambda * (surface - targetSurface) ^ 2
  CellG *nCell;
  
  if (oldCell == newCell) return 0;

  unsigned int token = 0;
  double distance;
  int oldDiff = 0;
  int newDiff = 0;
  Point3D n;
  double energy = 0;
  
 
    

  // Count surface difference
  while (true) {
    n = cellFieldG->getNeighbor(pt, token, distance, false);
    if (distance > 1) break;
    nCell = cellFieldG->get(n);
    
    if (newCell == nCell) newDiff--;
    else newDiff++;

    if (oldCell == nCell) oldDiff++;
    else oldDiff--;
  }
  
  if (newCell)
    energy += diffEnergy(
                           newCell->surface,
                           newDiff,
                           surfaceEnergyParamVector[newCell->type].targetSurface,
                           surfaceEnergyParamVector[newCell->type].lambdaSurface
                        );

  if (oldCell)
    energy += diffEnergy(
                           oldCell->surface,
                           oldDiff,
                           surfaceEnergyParamVector[oldCell->type].targetSurface,
                           surfaceEnergyParamVector[oldCell->type].lambdaSurface
                           );

 
  return energy;
              
}


void SurfaceFlexEnergy::readXML(XMLPullParser &in) {
  in.skip(TEXT);

  
  string type;
  
  
  float lambda;
  unsigned int targetSur;
  
  while (in.check(START_ELEMENT)) {
    if (in.getName() == "SurfaceEnergyParameters") {
      
    type = in.getAttribute("CellType").value;

    typeNameVec.push_back(type);  

      
    targetSur=BasicString::parseUInteger(in.getAttribute("TargetSurface").value);
    lambda= BasicString::parseDouble(in.getAttribute("LambdaSurface").value);

    surfaceEnergyParamVector.push_back(SurfaceEnergyParam(targetSur,lambda));
    
    in.matchSimple();

    
    }else {
      throw BasicException(string("Unexpected element '") + in.getName() + 
			   "'!", in.getLocation());
    }

    in.skip(TEXT);
  }
   
   
   
}

void SurfaceFlexEnergy::writeXML(XMLSerializer &out) {
}


void SurfaceFlexEnergy::initTypeId(Potts3D * potts){

   unsigned char maxType(0);
   
   Automaton * automaton=potts->getAutomaton();

   vector<unsigned char> typeIdVec(typeNameVec.size(),0);
   
   vector<SurfaceEnergyParam> sepVec=surfaceEnergyParamVector;//temporaty storage


      
   //translate type name to type id
   for(unsigned int i =0 ; i < typeNameVec.size() ;++i){
      typeIdVec[i]=automaton->getTypeId(typeNameVec[i]);
      
      if(typeIdVec[i]>maxType)
         maxType=typeIdVec[i];
  }

   //assigning vector lambda targetSur pairs in such a wav that it will be possible to use e.g.vec[cellType].lambda statements
   // note that some of the vector elements migh be left default initialized
   surfaceEnergyParamVector.assign(maxType+1,SurfaceEnergyParam());
  
   for(unsigned int i =0 ; i < typeIdVec.size() ;++i){
      surfaceEnergyParamVector[typeIdVec[i]]=sepVec[i];
   }

}

