package calhoun.analysis.crf.io;

import calhoun.util.Assert;
import calhoun.util.Util;

import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**an {@link InputHandler}used when the input is in several files within a single directory.  A single {@link InputComponentIO}is used for each
 * fileName.  A map associates each fileName name with its {@link InputComponentIO}.  For training, hidden sequences are stored in a separate fileName in the directory whose name is set with the hiddenSequenceFile property.
 * For this {@link InputHandler}, the location passed is the path to the directory containing the input data.
 */
public class InputHandlerOrderedDirectory extends InputHandlerBase {

    List inputReaders;
    TrainingSequenceIO hiddenStateReader;
    String hiddenSequenceFile = "hidden.dat";

    public Iterator<? extends InputSequence<?>> readInputData(String location) throws IOException {
        List<Map<String, InputSequence<?>>> inputs = new ArrayList();

        // Read in all of the inputs
        int i = 0;
        String fileName = null;
        for (Object value : inputReaders) {
            if(i%2 == 0){
                Assert.a(value instanceof String,"Every odd value has to be String");
                fileName = (String)value;
            } else {
                Assert.a(value instanceof InputComponentIO,"Every odd value has to be InputComponentIO");
                ((InputComponentIO)value).readInputSequences(new File(location, fileName).getPath(), inputs);
            }
            i++;
        }

        return createCompositeInput(inputs);
    }

    public List<? extends TrainingSequence<?>> readTrainingData(String location) throws IOException {
        return readTrainingData(location, false);
    }

    public List<? extends TrainingSequence<?>> readTrainingData(String location, boolean predict) throws IOException {
        String trainingLocation = new File(location, hiddenSequenceFile).getPath();

        return readTrainingData(location, trainingLocation, hiddenStateReader, predict);
    }

    public void writeInputData(String location, Iterator<? extends InputSequence<?>> data) throws IOException {
        // Collect all the values from the iterator into a list
        // Then for each composite, separate it into a map of its component pieces for handing to the IO class
        List<Map<String, InputSequence<?>>> compList = new ArrayList<Map<String, InputSequence<?>>>();
        Util.addAll(compList, new IteratorAdapterInputComponent(data));
        
        int i = 0;
        String fileName = null;
        for (Object value : inputReaders) {
            if(i%2 == 0){
                Assert.a(value instanceof String,"Every odd value has to be String");
                fileName = (String)value;
            } else {
                Assert.a(value instanceof InputComponentIO,"Every odd value has to be InputComponentIO");
                ((InputComponentIO)value).writeInputSequences(new File(location, fileName).getPath(), compList);
            }
            i++;
        }
    }

    public void writeTrainingData(String location, List<? extends TrainingSequence<?>> data) throws IOException {
        writeInputData(location, new IteratorAdapterTrainingSequenceInput(data.iterator()));

        List<int[]> trainingSeqs = new ArrayList<int[]>();
        for (TrainingSequence<?> t : data) {
            trainingSeqs.add(t.getY());
        }

        hiddenStateReader.writeTrainingSequences(new File(location, hiddenSequenceFile).getPath(), trainingSeqs.iterator());
    }

    /**gets the reader used to read in results for training data.
     * @return the {@link TrainingSequenceIO}used to read in the hidden sequences for training
     */
    public TrainingSequenceIO getHiddenStateReader() {
        return hiddenStateReader;
    }

    /**sets the reader used to get hidden sequences.  Must be set to read in training data.
     * @param hiddenStateReader the reader that will be used to access hidden states
     */
    public void setHiddenStateReader(TrainingSequenceIO hiddenStateReader) {
        this.hiddenStateReader = hiddenStateReader;
    }

    /**gets the readers used to read in input sequences.  Must be set before any of the <code>read</code> methods are called.
     * @return the reader used to read in input sequences.
     */
    public List getInputReaders() {
        return inputReaders;
    }

    /**sets the readers used to read in input sequences.  Must be set before any of the <code>read</code> methods are called.
     * the value is a map that associates filenames within the directory to input components.
     * @param inputReader the reader used to read in input sequences.
     */
    public void setInputReaders(List inputReader) {
        this.inputReaders = inputReader;
    }

    /**gets the name of the hidden sequence fileName.  This is the name of the fileName within the directory where training data will be located.
     * @return the name of the hidden sequence fileName.
     */
    public String getHiddenSequenceFile() {
        return hiddenSequenceFile;
    }

    /**sets the name of the hidden sequence fileName.  This is the name of the fileName within the directory where training data will be located.
     * @param hiddenSequenceFile the name of the hidden sequence fileName within the input directory.
     */
    public void setHiddenSequenceFile(String hiddenSequenceFile) {
        this.hiddenSequenceFile = hiddenSequenceFile;
    }
    
}
