package ee.ut.ga.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import ee.ut.ga.model.MatrixKey;
import ee.ut.ga.model.PWM;

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

  /**
   * Loads data from file. Keeps Map of row number in data and DNA sequence. Reads as data all lines that only consist
   * of letters A,T,G and C. All other lines are ignored.
   * 
   * @return <code>Map</code> of (<code>Long</code>, <code>String</code>)
   */
  public static Map loadData(String pathToFile) throws Exception {
    log.debug("*******************************");
    log.debug("Started reading data from file: " + pathToFile);

    Map data = new HashMap();
    try {
      BufferedReader file = new BufferedReader(new FileReader(pathToFile));
      String currentLine = file.readLine();
      int rowNumber = 0;

      while (currentLine != null) {
        currentLine = currentLine.trim();
        if (Pattern.matches("[ATGC]+", currentLine)) {
          data.put(new Long(rowNumber), currentLine);
          rowNumber++;
        }
        currentLine = file.readLine();
      }

      file.close();
    } catch (IOException ie) {
      log.debug("Error reading file: " + pathToFile);
    }

    log.debug("Finished reading data from file: " + pathToFile);
    log.debug("*******************************");
    
    if (data.size() == 0)
      throw new Exception("No data is defined!");
    
    return data;
  }

  /**
   * Loads PAMs from file. Used as initial population.
   * Reads pwm-props exactly after line '>pwm-props' and pam exactly after line '>pam'.
   * Other lines are ignored. 'pwm-props' may not be set.
   * If 'pwm-props' are set, they must be set before pam. 
   * 
   * @return List of <code>PWM</code>.
   */
  public static List loadInitialPopulation(String pathToFile, Map priors) throws Exception {
    log.debug("*******************************");
    log.debug("Started reading initial population from file: " + pathToFile);

    List initialPopulation = new ArrayList();

    try {
      BufferedReader file = new BufferedReader(new FileReader(pathToFile));
      String currentLine = file.readLine();
      String pwmProps = null;
      
      while (currentLine != null) {

        if (currentLine.trim().equals(">pwm-props")) {
          pwmProps = file.readLine();
          currentLine = file.readLine();
        }
        
        if (currentLine.trim().equals(">pam")) {
          currentLine = file.readLine();
          
          Map pam = new HashMap();
          for (int i = 0; i < 4; i++) {
            String ch = currentLine.trim().substring(0, 1).toUpperCase();
            String[] temp = currentLine.trim().substring(1).trim().split("\t");
            for (int j = 0; j < temp.length; j++) {
              pam.put(new MatrixKey(ch, j), new Long(temp[j]));
            }
            currentLine = file.readLine();
          }
          
          PWM pwm = new PWM(pam, priors);
          
          if (pwmProps != null) {
            pwm.setPwmProps(pwmProps);
            pwmProps = null;
          }
          
          initialPopulation.add(pwm);
        }
        
        currentLine = file.readLine();
      }
      file.close();
    } catch (IOException ie) {
      log.debug("Error reading file: " + pathToFile);
    }

    log.debug("Finished reading initial population from file: " + pathToFile);
    log.debug("*******************************");
    
    if (initialPopulation.size() == 0)
      throw new Exception("No initial population is defined!");
    
    return initialPopulation;
  }
  
  /**
   * Writes into given file specified results.
   */
  public static void writeResult(String result, String path, boolean appendToFile) {
    try {
      BufferedWriter out = new BufferedWriter(new FileWriter(path, appendToFile));
      out.write(result + "\n\n");
      out.close();
    } catch (IOException e) {
      log.debug("IOException while writing into file: " + path);
    }
  }
}
