package ee.ut.ga.util;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.log4j.Logger;

import ee.ut.ga.model.MatrixKey;

public class PwmUtil {
  public static Logger log = Logger.getLogger(PwmUtil.class);

  /**
   * @return weight of the given motif with specified Position-Weigth-Matrix
   */
  public static BigDecimal getValue(Map pwm, String motif) {
    BigDecimal value = new BigDecimal("0");
    char[] motifChar = motif.toCharArray();
    for (int i = 0; i < motifChar.length; i++) {
      if (motifChar[i] == 'A')
        value = value.add((BigDecimal) pwm.get(new MatrixKey("A", i)));
      else if (motifChar[i] == 'C')
        value = value.add((BigDecimal) pwm.get(new MatrixKey("C", i)));
      else if (motifChar[i] == 'G')
        value = value.add((BigDecimal) pwm.get(new MatrixKey("G", i)));
      else
        value = value.add((BigDecimal) pwm.get(new MatrixKey("T", i)));
    }
    return value;
  }

  /**
   * @return Position-Weigth-Matrix (Map<MatrixKey, BigDecimal>), calculated using Position-Align-Matrix
   */
  public static Map getPwm(Map pam, Map priors, int length) {
    Map pwm = new HashMap();
    int quantity = getMotifSetLength(pam);
    for (int i = 0; i < ConstantsEnum.NUCLEOTIDES.length; i++) {
      for (int j = 0; j < length; j++) {
        double prior = ((BigDecimal) priors.get(ConstantsEnum.NUCLEOTIDES[i])).doubleValue();
        double nucleotideQuantity = getPam(pam, ConstantsEnum.NUCLEOTIDES[i], j);
        double pwmVlaue = Math.log((nucleotideQuantity + prior) / ((quantity + 1) * prior));
        pwm.put(new MatrixKey(ConstantsEnum.NUCLEOTIDES[i], j), new BigDecimal(pwmVlaue));
      }
    }
    return pwm;
  }

  /**
   * @return approximate pattern of Position-Weight-Matrix.
   */
  public static String getPattern(Map pwm, int length) {
    StringBuffer sbPattern = new StringBuffer();
    for (int i = 0; i < length; i++) {
      double a = getPwm(pwm, "A", i);
      double c = getPwm(pwm, "C", i);
      double g = getPwm(pwm, "G", i);
      double t = getPwm(pwm, "T", i);

      String ch;
      if (a >= c && a >= g && a >= t)
        ch = "A";
      else if (c >= a && c >= g && c >= t)
        ch = "C";
      else if (g >= a && g >= c && g >= t)
        ch = "G";
      else
        ch = "T";

      sbPattern.append(ch);
    }
    return sbPattern.toString();
  }

  /**
   * @return number of columns in matrix (aka length of pattern)
   */
  public static int getLength(Map matrix) {
    int length = 0;
    for (Iterator i = matrix.keySet().iterator(); i.hasNext();) {
      if (((MatrixKey) i.next()).getRow().equals("A")) {
        length++;
      }
    }
    return length;
  }

  /**
   * @return quantity of DNA sequencies used to calculate matrixes.
   */
  public static int getMotifSetLength(Map pam) {
    return getPam(pam, "A", 0) + getPam(pam, "C", 0) + getPam(pam, "G", 0) + getPam(pam, "T", 0);
  }

  /**
   * @return value of Position-Weight-Matrix in specified row and column.
   */
  private static double getPwm(Map pwm, String nucleotide, int position) {
    return ((BigDecimal) pwm.get(new MatrixKey(nucleotide, position))).doubleValue();
  }

  /**
   * @return value of Position-Align-Matrix in specified row and column.
   */
  private static int getPam(Map pam, String nucleotide, int position) {
    return ((Long) pam.get(new MatrixKey(nucleotide, position))).intValue();
  }
}
