package com.actelion.research.chem.docking.scoring.plp;

import com.actelion.research.chem.Coordinates;
import com.actelion.research.chem.conf.Conformer;
import com.actelion.research.chem.potentialenergy.PotentialEnergyTerm;

import java.util.HashMap;
import java.util.Map;

/**
 * piecewise linear potential (PLP) as described in doi:10.1021/ci800298z
 * parameters from model 3 (M3)
 * @author wahljo1
 *
 */

public class PLPTerm implements PotentialEnergyTerm {
	
	public static final Map<String, Double> HBOND_TERM  = new HashMap<String, Double>();
	static {
		HBOND_TERM.put("A", 2.3);
		HBOND_TERM.put("B", 2.6);
		HBOND_TERM.put("C", 3.1);
		HBOND_TERM.put("D", 3.4);
		HBOND_TERM.put("E", -1.0);
		HBOND_TERM.put("F", 20.0);
	};
	
	public static final Map<String, Double> METAL_TERM  = new HashMap<String, Double>();
	static {
		METAL_TERM.put("A", 1.4);
		METAL_TERM.put("B", 2.2);
		METAL_TERM.put("C", 2.6);
		METAL_TERM.put("D", 2.8);
		METAL_TERM.put("E", -1.0);
		METAL_TERM.put("F", 20.0);
	};
	
	public static final Map<String, Double> BURIED_TERM  = new HashMap<String, Double>();
	static {
		BURIED_TERM.put("A", 3.4);
		BURIED_TERM.put("B", 3.6);
		BURIED_TERM.put("C", 4.5);
		BURIED_TERM.put("D", 5.5);
		BURIED_TERM.put("E", -0.1);
		BURIED_TERM.put("F", 20.0);
	};
	
	public static final Map<String, Double> NONPOLAR_TERM  = new HashMap<String, Double>();
	static {
		NONPOLAR_TERM.put("A", 3.4);
		NONPOLAR_TERM.put("B", 3.6);
		NONPOLAR_TERM.put("C", 4.5);
		NONPOLAR_TERM.put("D", 5.5);
		NONPOLAR_TERM.put("E", -0.4);
		NONPOLAR_TERM.put("F", 20.0);
	};
	
	private int recAtom;
	private int ligAtom;
	private double A;
	private double B;
	private double C;
	private double D;
	private double E;
	private double F;
	private double D_sq;
	private Conformer ligand;
	private Conformer receptor;

	
	private PLPTerm(Conformer receptor, Conformer ligand, int recAtom, int ligAtom, Map<String,Double> term) {
		A = term.get("A");
		B = term.get("B");
		C = term.get("C");
		D = term.get("D");
		E = term.get("E");
		F = term.get("F");
		D_sq = D*D;
		this.recAtom = recAtom;
		this.ligAtom = ligAtom;
		this.ligand = ligand;
		this.receptor = receptor;
		
	}
	
	public static PLPTerm create(Conformer receptor, Conformer ligand, int recAtom, int ligAtom, Map<String,Double> term) {
		return new PLPTerm(receptor, ligand, recAtom, ligAtom, term);
	}

	@Override
	public double getFGValue(double[] gradient) {
		final Coordinates ci = receptor.getCoordinates(recAtom);		
		final Coordinates ck = ligand.getCoordinates(ligAtom);				
		final Coordinates cr = ci.subC(ck);
		double r2 = cr.distSq();		
		Coordinates grad = new Coordinates();
		double energy = 0.0;
		if(r2>D_sq) {
			energy = 0; 
		} 
		else {
			double prefactor = 0.0;
			double r = Math.sqrt(r2);
			if(r<A) {
				prefactor = (-F/A)*(1.0/r);
				grad = cr.scaleC(prefactor);
				energy = (F*(A-r))/A;
			}
			else if(r<B) {
				prefactor = (E/(B-A))*(1.0/r);
				grad = cr.scaleC(prefactor);
				energy = (E*(r-A))/(B-A);
			}
			else if(r<C) {
				prefactor = 0.0;
				grad = cr.scaleC(prefactor);
				energy = E;
			}
			else if(r<=D) {
				prefactor = (-E/(D-C))*(1.0/r);
				grad = cr.scaleC(prefactor);
				energy = (E*(D-r))/(D-C);
			}
		}
		gradient[3*ligAtom]-= grad.x;
		gradient[3*ligAtom+1]-= grad.y;
		gradient[3*ligAtom+2]-= grad.z;
		return energy;
	}
}


