package hw2;

/**
 * Utility to hide the low level formatting of an attribute relation file
 * @author Joshua Nankin
 */

import java.io.*;
import java.util.*;

class ArffException extends Exception{
	public ArffException(String s){
		super(s);
	}
	static final long serialVersionUID = 12345678910L;

}

public class ArffGenerator {

	public static final int NUMERIC = 0;
	public static final int NOMINAL = 1;
	public static final int STRING = 2;
	public static final int DATE = 3;
	public static final String[] types = {"NUMERIC", "", "string", "date"};

	private boolean relationDone = false;
	private boolean dataStarted = false;
	private PrintWriter pr;

	private int attributeCounter = 0;
	private long instanceCounter = 0;
	private String filename;

	public ArffGenerator(String filename){
		this.filename = filename;
		try {
			pr = new PrintWriter(new FileOutputStream(filename));
		}
		catch (Exception e){
			e.printStackTrace();
		}
	}

	public void addComment(String s){
		pr.write("% " + s + "\n");
	}

	public void addRelation(String name){
		pr.write("@RELATION " + name + "\n\n");
		relationDone = true;
	}

	public void addData(ArrayList<String> features) throws ArffException{
		if (!relationDone) throw new ArffException("Cannot add data before adding relation name.");
		if (attributeCounter < 1) throw new ArffException("You must have at least 1 attribute in the file.");
		if (!dataStarted){
			dataStarted = true;
			pr.write("\n\n@DATA\n");
		}
		Object[] featuresArray = features.toArray();
		pr.write(explode(",", featuresArray) + "\n");
		if (featuresArray.length != attributeCounter){
			pr.close();
			throw new ArffException(filename + ": The number of features in this instance does not match the number of attributes in the file. (features:" + featuresArray.length + " attributes:" + attributeCounter + ")" );
		}
		instanceCounter++;
	}

	//add @ATTRIBUTE to the arff
	public void addAttribute(String s, int type) throws ArffException{
		if (!relationDone) throw new ArffException("Cannot add an attribute before adding relation name.");
		if (dataStarted) throw new ArffException("Cannot add an attribute after data has been written.");
		if (type != NUMERIC && type != STRING) throw new ArffException("To add a date or nominal, call addAttribute with other signatures.");
		pr.write("@ATTRIBUTE " + s + " " + types[type] + "\n");
		attributeCounter++;
	}

	//add NOMINAL @ATTRIBUTE to the arff
	public void addAttribute(String s, int type, String[] options) throws ArffException{
		if (!relationDone) throw new ArffException("Cannot add an attribute before adding relation name.");
		if (dataStarted) throw new ArffException("Cannot add an attribute after data has been written.");
		pr.write("@ATTRIBUTE " + s + " {" + explode(",", options) + "}\n");
		attributeCounter++;
	}

	//input an array, output a string separated by commas
	public String explode(String separator, Object[] options){
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i < options.length; i++){
			buffer.append((String)options[i]);
			if ((i + 1) < options.length) buffer.append(separator);
		}
		return buffer.toString();
	}

	public void close(){
		pr.close();
	}

	//number of text articles examined
	public long numberOfInstances(){
		return instanceCounter;
	}

	public int numberOfAttributes(){
		return attributeCounter;
	}
}
