/*
 * Copyright (c) 2005, Stanford University. All rights reserved. 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions
 * are met: 
 *  - Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 *  - Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 *  - Neither the name of the Stanford University nor the names of its 
 *    contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE. 
 */

/*
 * Created on Sep 20, 2005
 * Original author: Christopher Bruns
 */
package org.simtk.isimsu;

import java.io.*;

//Model in which to store adjustable parameters for computation
class APBSParameters {
    
  private double temperature = Double.NaN;
  private double solventPermittivity = 78.00;
  private double moleculePermittivity = 2.0;
  
  private int[] dimension = {97, 97, 97};
  private double[] molGridLength = {10.0, 10.0, 10.0};
  private double[] ionGridLength = {10.0, 10.0, 10.0};
  private double[] useGridLength = molGridLength;
//  private int gridLengthX;
//  private int gridLengthY;
//  private int gridLengthZ;
  
  private SaltCondition saltCondition;
  
  private File pqrInputFile;
  private File potentialOutputFile;
  
  private ISIMTask chooseIonsTask;
  private ISIMTask adjustAPBSParametersTask;
  private ISIMTask loadMacromoleculeTask;
  
  public APBSParameters() {
      System.out.println("APBSParameters constructor");
  }
  public APBSParameters( APBSParameters apbsParameters ) {
	  
	  temperature                 = apbsParameters.getTemperature();
	  solventPermittivity         = apbsParameters.getSolventPermittivity();
	  moleculePermittivity        = apbsParameters.getMoleculePermittivity();
	  
	  int [] newDimension         = apbsParameters.getDimension();
	  double[] newIonGridLength   = apbsParameters.getIonGridLength();
	  double[] newMolGridLength   = apbsParameters.getMolGridLength();
	  double[] newUseGridLength   = apbsParameters.getGridLength();

	  
	  for( int ii = 0; ii < 3; ii++ ){
		  
	     dimension[ii]     = newDimension[ii];
	
	     ionGridLength[ii] = newIonGridLength[ii];
	     molGridLength[ii] = newMolGridLength[ii];
	     useGridLength[ii] = newUseGridLength[ii];
	  }
//	  
      System.out.println("APBSParameters constructor");
  }
  
  public void setTemperature(double temperature) {
      this.temperature = temperature;
      if (adjustAPBSParametersTask != null)
          adjustAPBSParametersTask.setStatus(DependentTask.CURRENT_AND_COMPLETE, "set temperature");
  }
  
  public void setSolventPermittivity(double solventPermittivity) {
      this.solventPermittivity = solventPermittivity;
      if (adjustAPBSParametersTask != null)
          adjustAPBSParametersTask.setStatus(DependentTask.CURRENT_AND_COMPLETE, "set solvent permittivity");
  }
  
  public void setMoleculePermittivity(double moleculePermittivity) {
      this.moleculePermittivity = moleculePermittivity;
      if (adjustAPBSParametersTask != null)
          adjustAPBSParametersTask.setStatus(DependentTask.CURRENT_AND_COMPLETE, "set molecule permittivity");      
  }
  
  public void setPqrInputFile(File pqrInputFile) {
      if (! pqrInputFile.equals(this.pqrInputFile)) {
          this.pqrInputFile = pqrInputFile;
          if (loadMacromoleculeTask != null)
              loadMacromoleculeTask.setStatus(DependentTask.CURRENT_AND_COMPLETE, pqrInputFile.getName());
      }
  }
  
  public void setDependentTasks(ISIMTask chooseIonsTask, ISIMTask adjustAPBSParametersTask, ISIMTask loadMacromoleculeTask) {
      this.chooseIonsTask = chooseIonsTask;
      this.adjustAPBSParametersTask = adjustAPBSParametersTask;
      this.loadMacromoleculeTask = loadMacromoleculeTask;
  }
  
  // Number of grid points along each edge
  public void setDimension(int x, int y, int z) {
      dimension[0] = x;
      dimension[1] = y;
      dimension[2] = z;
      
      if (adjustAPBSParametersTask != null)
          adjustAPBSParametersTask.setStatus(DependentTask.CURRENT_AND_COMPLETE, "set grid length");
  }

  /**
   * Select the larger of the grids suggested by molecule size and ion concentration
   *
   */
  private void chooseLargestGrid() {
      if (molGridLength[0] < ionGridLength[0]) useGridLength = ionGridLength;
      else useGridLength = molGridLength;
  }
  
  // Length of each edge of the volume
  public void setMolGridLength(double x, double y, double z) {
      molGridLength[0] = x;
      molGridLength[1] = y;
      molGridLength[2] = z;
      chooseLargestGrid();
      if (adjustAPBSParametersTask != null)
          adjustAPBSParametersTask.setStatus(DependentTask.CURRENT_AND_COMPLETE, "set grid length");
  }
  
  // Length of each edge of the volume
  public void setIonGridLength(double x, double y, double z) {
      ionGridLength[0] = x;
      ionGridLength[1] = y;
      ionGridLength[2] = z;
      chooseLargestGrid();
      if (adjustAPBSParametersTask != null)
          adjustAPBSParametersTask.setStatus(DependentTask.CURRENT_AND_COMPLETE, "set grid length");
  }
  
//  APBSParameters(APBSParameters cloneParent) {
//      temperature = cloneParent.temperature;
//      solventPermittivity = cloneParent.solventPermittivity;
//      moleculePermittivity = cloneParent.moleculePermittivity;
//      saltCondition = cloneParent.saltCondition;
//  }
  
  public int[] getDimension() {return dimension;}
  public double[] getGridLength() {return useGridLength;}
  public double[] getMolGridLength() {return molGridLength;}
  public double[] getIonGridLength() {return ionGridLength;}
  
  public SaltCondition getIons() {return this.saltCondition;}
  
  public void setIons(SaltCondition saltCondition) {
      if (saltCondition == null) return;
      // If no change, do nothing
      if (saltCondition.equals(this.saltCondition)) return;
      
      this.saltCondition = saltCondition;

      SaltConditionRange range = saltCondition.getParentRange();      
      setTemperature(range.getTemperature());
      setSolventPermittivity(range.getSolventPermittivity());
      
      // Adjust simulation volume based on concentration of ions
      double minConcentration = 10000; // in millimolar
      for (IonConcentration ionConcentration : saltCondition) {
          double testConcentration = ionConcentration.getConcentration();
          if (testConcentration <= 0) continue; // Zeros don't count
          minConcentration = Math.min(minConcentration, testConcentration);
      }
      // Radius of sphere that holds ten ions of lowest concentration type
      // Constant should be 1660000 I think, but bulk ion count comes to 12 then...
      double minVolume = 10.0 * 14000000.0 / minConcentration; // cubic angstroms
      double minRadius = Math.pow(0.75 * minVolume / Math.PI, 1.0/3.0);
      System.out.println("Min radius = "+minRadius);
      setIonGridLength(minRadius, minRadius, minRadius);
      
      if (chooseIonsTask != null)
          chooseIonsTask.setStatus(DependentTask.CURRENT_AND_COMPLETE, saltCondition.getName());
  }
  
  public int getIonCount() { return saltCondition.getIonCount(); }
  
  public File getPqrInputFile() {return pqrInputFile;}
  public File getPotentialOutputFile() {return potentialOutputFile;}
  public double getMoleculePermittivity() {return moleculePermittivity;}
  public double getSolventPermittivity() {return solventPermittivity;}
  public double getTemperature() {return temperature;}
  
  public void setMacroionPotentialFile(File file) {this.potentialOutputFile = file;}
  
  void copyFrom( APBSParameters otherParameters) {
      setTemperature(otherParameters.temperature);
      setSolventPermittivity(otherParameters.solventPermittivity);
      setMoleculePermittivity(otherParameters.moleculePermittivity);

 
      setIonGridLength(otherParameters.ionGridLength[0], otherParameters.ionGridLength[1], otherParameters.ionGridLength[2]);
      setMolGridLength(otherParameters.molGridLength[0], otherParameters.molGridLength[1], otherParameters.molGridLength[2]);
      setDimension(otherParameters.dimension[0], otherParameters.dimension[1], otherParameters.dimension[2]);
  }
  
  public boolean equals(Object o) {
      if (! (o instanceof APBSParameters) ) return false;
      APBSParameters p2 = (APBSParameters) o;

      if (temperature != p2.temperature) return false;
      if (solventPermittivity != p2.solventPermittivity) return false;
      if (moleculePermittivity != p2.moleculePermittivity) return false;
      if (useGridLength[0] != p2.useGridLength[0]) return false;
      if (useGridLength[1] != p2.useGridLength[1]) return false;
      if (useGridLength[2] != p2.useGridLength[2]) return false;
      if (dimension[0] != p2.dimension[0]) return false;
      if (dimension[1] != p2.dimension[1]) return false;
      if (dimension[2] != p2.dimension[2]) return false;
      
      return true;
  }
  
  public int hashCode() {
      return (new Double(temperature).hashCode() +
              new Double(solventPermittivity).hashCode() +
              new Double(moleculePermittivity).hashCode() +
              useGridLength.hashCode() +
              dimension.hashCode()
              );
  }
}

