/*
 * RunWrapper.java
 *
 * Created on January 26, 2006, 4:52 PM
 *
 */
package usda.weru.wmrm;

import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.file.TFileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import usda.weru.weps.*;

import java.util.*;
import usda.weru.util.ReportContext;

/**
 * Wraps the RunFileData class in order to provide access 
 * and formatting to run data.
 * @author Joseph Levin
 */
public class RunWrapper extends RunFileData {

    private final static org.apache.log4j.Logger LOGGER = org.apache.log4j.Logger.getLogger(RunWrapper.class);

	/**
	 *
	 */
	public enum DataTag {

		/**
		 *
		 */
		RunName("RunName"),

		/**
		 *
		 */
		RunLocation("RunLocation"),

		/**
		 *
		 */
		ManagementName("ManagementName"),

		/**
		 *
		 */
		SoilName("SoilName"),

		/**
		 *
		 */
		FieldSize("FieldSize");
        private final String[] c_tags;

        DataTag(String... tags) {
            c_tags = tags;
        }

		/**
		 *
		 * @param tag
		 * @return
		 */
		public boolean accept(String tag) {
            if (tag == null) {
                return false;
            }
            for (String validTag : c_tags) {
                if (tag.equalsIgnoreCase(validTag)) {
                    return true;
                }
            }
            return false;
        }

		/**
		 *
		 * @return
		 */
		public String getFirstTag() {
            if (c_tags.length > 0) {
                return c_tags[0];
            } else {
                return null;
            }
        }
    }
    /**
     * Run top level directory.
     */
    private final TFile c_runDirectory;
    private TFile c_soilFile;
    private RunDataAge c_age;
    List<String> c_headers;
    /**
     * Stores the annual totals for the run data against a key 
     * string of the header name.
     */
    private Map<String, Double> c_outputData = null;

    /**
     * Creates a new instance of RunWrapper
     * @param pathName The run file directory.
     */
    public RunWrapper(String pathName) {
        c_displayMessages = false;
        c_runDirectory = new TFile(pathName);
        updateData();

    }

    /**
     * Looks up the DataTag enum object for the given string.
     * @param key String value to find a DataTag for.
     * @return DataTag enum or null value if no tag is found.
     */
    private DataTag getDataTagFromKey(String key) {
        for (DataTag tag : DataTag.values()) {
            if (tag.accept(key)) {
                return tag;
            }
        }
        return null;
    }

    /**
     * Top level method for getting a value.  If a DataTag enum is found 
     * for the given key then the value is returned from a wrapper method.
     * Otherwise the value is return from the output data.
     * @param key String key to lookup and return a value for.
     * @return The value as an object.
     */
    public Object getValue(String key) {
        DataTag tag = getDataTagFromKey(key);
        if (tag != null) {
            return getWrapperValue(tag);
        } else {
            return getOutputValue(key);
        }
    }

	/**
	 *
	 * @param key
	 * @return
	 */
	public String getToolTipText(String key) {
        DataTag tag = getDataTagFromKey(key);
        if (tag != null) {
            TFile file = null;
            switch (tag) {
                case RunLocation:
                    file = fileObject.getParentFile();
                    if (file != null) {
                        return file.getAbsolutePath();
                    } else {
                        return null;
                    }

                case SoilName:
                    file = c_soilFile;
                    if (file != null) {
                        return file.getAbsolutePath();
                    } else {
                        return null;
                    }
                case ManagementName:
                    file = new TFile(getData(RunFileData.ManageFile));
                    if (file != null) {
                        return file.getAbsolutePath();
                    } else {
                        return null;
                    }

                default:
                    return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 
	 * @param header
	 * @return 
     */
    public double getOutputValue(String header) {
        try {
            Double result = c_outputData.get(header);
            if (result == null) {
                return Double.NaN;
            } else {
                return result;
            }
        } catch (NullPointerException npe) {
            return Double.NaN;
        }
    }

	/**
	 *
	 * @param tag
	 * @return
	 */
	public Object getWrapperValue(DataTag tag) {
        if (tag == null) {
            return null;
        }
        try{
            switch (tag) {
                case RunName:
                    if (fileObject == null) {
                        return "";
                    }
                    String runName = fileObject.getParentFile().getName();
                    runName = runName.replaceFirst(RunFileData.RunSuffix, "");
                    return runName;
                case RunLocation:
                    TFile location = fileObject.getParentFile().getParentFile();
                    if (location != null) {
                        return location.getName();
                    } else {
                        return null;
                    }
                case ManagementName:
                    TFile managementFile = new TFile(getData(RunFileData.ManageFile));
                    String managementName = managementFile.getName();
                    managementName = managementName.replaceFirst(".man", "");
                    return managementName;
                case SoilName:
                    String soilName = c_soilFile.getName();
                    soilName = soilName.replaceFirst(".ifc", "");
                    return soilName;
                case FieldSize:
                    double xLen = Double.parseDouble(super.getData(RunFileData.XLength));
                    double yLen = Double.parseDouble(super.getData(RunFileData.YLength));
                    return xLen * yLen;
            }
        }
        catch(NumberFormatException e){
            LOGGER.error("Unable to get data: " + tag);
        }
        return null;
    }

	/**
	 *
	 * @return
	 */
	public List<String> getDataKeys() {
        return c_headers != null ? c_headers : Collections.<String>emptyList();
    }

	/**
	 *
	 * @return
	 */
	public TFile getSoilFile() {
        return c_soilFile;
    }

	/**
	 *
	 */
	public void updateData() {
        ReportContext.enter();
        try {
            LOGGER.debug("Updating: " + c_runDirectory.getAbsolutePath());
            //clear first
            initialize();

            readRunFile(c_runDirectory.getAbsolutePath());
            c_soilFile = new TFile(getData(RunFileData.SoilFile));
            c_outputData = loadOutputData(new TFile(c_runDirectory, RunFileData.WepsOutput));
            updateAge();
        } catch (Exception e) {
            LOGGER.error("Unable to load data: " + c_runDirectory.getAbsolutePath(), e);
        } finally {
            ReportContext.exit();
        }
    }

    private Map<String, Double> loadOutputData(TFile file) {
        Map<String, Double> values = new Hashtable<String, Double>();
        Vector<String> lines = new Vector<String>();
        Vector<String> headers = new Vector<String>();
        String inLine;
        if (!file.exists()) {
            //the data file does not exist.
            return Collections.emptyMap();
        }
        //read the file one line at a time into a vector for processing
        BufferedReader in;
        try {
            in = new BufferedReader(new TFileReader(file));
        } catch (FileNotFoundException ex) {
            Logger.getLogger(RunWrapper.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex.toString());
        }
        try {
            do {
                inLine = in.readLine();
                if (inLine != null) {
                    lines.add(inLine);
                }
            } while (inLine != null);
            in.close();

        } catch (IOException ex) {
            Logger.getLogger(RunWrapper.class.getName()).log(Level.SEVERE, null, ex);
            return values;
        }

        for (String line : lines) {
            //We have the header line.
            if (line.startsWith("key")) {
                StringTokenizer st = new StringTokenizer(line, "|");
                while (st.hasMoreTokens()) {
                    headers.add(st.nextToken().trim());
                }
                c_headers = headers;
            } //We have the totals line.
            else if (line.trim().startsWith("T")) {
                String[] st = line.split("\\|");
                if (st.length != headers.size()) {
                    continue;
                }
                for (int index = 0; index < st.length; index++) {
                    Double newValue;
                    String value = st[index].trim();
                    try {
                        newValue = Double.parseDouble(value);
                    } catch (NumberFormatException nfe) {
                        newValue = Double.valueOf(0);
                    }
                    values.put(headers.get(index), newValue);
                }
            }
        }
        return values;
    }

	/**
	 *
	 * @return
	 */
	public TFile getDirectory() {
        return c_runDirectory;
    }

	/**
	 *
	 * @return
	 */
	@Override
    public String toString() {
        return getWrapperValue(DataTag.RunName).toString();
    }

	/**
	 *
	 * @return
	 */
	public boolean upToDate() {
        RunDataAge temp = new RunDataAge(c_runDirectory);
        return temp.equals(c_age);
    }

	/**
	 *
	 * @return
	 */
	public boolean exists() {
        return c_runDirectory.exists();
    }

    private void updateAge() {
        c_age = new RunDataAge(c_runDirectory);
    }

	/**
	 *
	 */
	protected static class RunDataAge {

        private long c_ageMin;
        private long c_ageMean;
        private long c_ageMax;
        private int c_n;

		/**
		 *
		 * @param dir
		 */
		public RunDataAge(TFile dir) {
            if (dir == null) {
                return;
            }
            c_ageMin = dir.lastModified();
            c_ageMax = c_ageMin;
            c_ageMean = c_ageMin;
            c_n = 1;
            TFile[] children = dir.listFiles();
            if (children != null) {
                for (TFile child : children) {
                    c_ageMin = Math.min(c_ageMin, child.lastModified());
                    c_ageMax = Math.max(c_ageMax, child.lastModified());
                    c_ageMean = ((c_ageMean * c_n++) + child.lastModified()) / c_n;
                }
            }
        }

		/**
		 *
		 * @param obj
		 * @return
		 */
		@Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final RunDataAge other = (RunDataAge) obj;
            if (this.c_ageMin != other.c_ageMin) {
                return false;
            }
            if (this.c_ageMean != other.c_ageMean) {
                return false;
            }
            if (this.c_ageMax != other.c_ageMax) {
                return false;
            }
            if (this.c_n != other.c_n) {
                return false;
            }
            return true;
        }

		/**
		 *
		 * @return
		 */
		@Override
        public int hashCode() {
            int hash = 5;
            hash = 79 * hash + (int) (this.c_ageMin ^ (this.c_ageMin >>> 32));
            hash = 79 * hash + (int) (this.c_ageMean ^ (this.c_ageMean >>> 32));
            hash = 79 * hash + (int) (this.c_ageMax ^ (this.c_ageMax >>> 32));
            hash = 79 * hash + this.c_n;
            return hash;
        }
    }
}
