package miningmart.storedProcedures;
import java.sql.SQLException;
import java.util.Vector;

/** This class realizes the M4 operator &quot;Windowing&quot;.*/
public class ToWindow {

    private final DbConnector connector;
    private final Windowing  window;   // for the input
    private final OutputColumnset out; // for the output
    private final int windowSize;

    private static final int INDEXONATTRIB = 2;


    protected Windowing getWindow() {
	return this.window;
    }

    protected OutputColumnset getOutputColumnset() {
	return this.out;
    }

    protected int getWindowSize() {
	return this.windowSize;
    }

    /**
     * Constructor
     * @param dbc source of database connection if not used as stored procedure
     * @param source source table
     * @param time   time column of source table
     * @param column value column of source table
     * @param target name of the target table to be created
     * @param outTimeStart name of the interval start attribute for
     * the output table
     * @param outTimeEnd name of the interval end attribute for the
     * output table
     * @param targetColumns String array containing the name of the
     * windowed output columns. Specifies the window size!
     * @throws SQLException
     * @throws TimeOperatorException
    */
    public ToWindow(BusinessDbConnectionSource dbc,
    				String source, String time, String column,
					String target, String outTimeStart, String outTimeEnd,
					String[] targetColumns, int distance)
		throws SQLException, TimeOperatorException
	{
		this.connector = new DbConnector(dbc);
		this.windowSize = targetColumns.length;
		final short timeDT =
			DataType.getColumnDataType(source, time, this.connector);
		final short columnDT =
			DataType.getColumnDataType(source, column, this.connector);

		// Input:
		final Attribute timeAttr = new Attribute(time, timeDT, timeDT);
		final Attribute columnAttr =
			new Attribute(column, columnDT, DataType.NUMBER);
		if (!timeAttr.isConvertable())
			DataType.wrongDataType(target, timeAttr);
		if (!columnAttr.isConvertable())
			DataType.wrongDataType(target, columnAttr);

		// Output:
		final Attribute timeStart = new Attribute(outTimeStart, timeDT, timeDT);
		final Attribute timeEnd = new Attribute(outTimeEnd, timeDT, timeDT);
		final Attribute[] allAttributes =
			new Attribute[targetColumns.length + 2];
		allAttributes[0] = timeStart;
		allAttributes[1] = timeEnd;
		for (int i = 0; i < targetColumns.length; i++)
			allAttributes[i + 2] =
				new Attribute(targetColumns[i], columnDT, columnDT);
		this.out =
			new OutputColumnset(
				target,
				allAttributes,
				this.connector.getConnection());

		// Prepare Windowing:
		this.window =
			new Windowing(targetColumns.length, distance, this.connector);
		if (!window
			.open(
				"SELECT "
					+ columnAttr.getConvertedInput()
					+ ", "
					+ timeAttr.getConvertedInput()
					+ " FROM "
					+ source)) {
			DbConnector.infoOutput("Error while opening " + source);
			throw new TimeOperatorException(
				"Could not read from table '" + source + "'!");
		}
	}

    /**
     * Method calc() reads from the column(s) specified in the constructor
     * and writes to the specified target, using windowing.
     * <!-- Finally an index is created on the target table. -->
     */
    public void calc() throws TimeOperatorException {
       	String[] ret = null;
		do {
		    ret = getWindow().getNextWnd();
	    	String start = window.getTimeFirstEntry();
		    String end   = window.getTimeLastEntry();
		    insert(start, end, ret);
		} while (ret != null);

		// this.getOutputColumnset().createIndexOn(INDEXONATTRIB);
		// *** No index is created in this compiler version! ***
	
		this.getWindow().close();
		this.connector.close();
    }


    /* description: inserts a window of values into the target-table
     * @param start point int time where the interval starts
     * @param end   point int time where the interval ends
     * @param value vector of values to be entered 
    */
    private void insert(String start, String end, String[] value)
	throws TimeOperatorException
    {
	if (value == null)
	    return;

	final String[] allValues = new String[this.getWindowSize()+2];
	allValues[0] = start;
	allValues[1] = end;
	for (int i=0; i<value.length; i++)
	    allValues[i+2] = value[i];

	this.getOutputColumnset().insert(allValues);
    }

    
    /** 
     * --- dbWindow ---
     * description : this function has to be used in the database as stored procedure
     *		   for rerepresenting a time series using windows.
     *		   the datatypes will be mapped to SQL datatypes.
     * in : see the constructor for more information
     * targetColumns: space separated list of target column names
     */
    public static void dbWindow(Object dbc,
    			String sourceTable, String timeAttrib, String valueAttrib,
				String targetTable, String outTimeStart, String outTimeEnd,
				String outWindowedCols, int distance)
		throws SQLException, TimeOperatorException
	{
		String[] tcArray = parseNamesList(outWindowedCols);
		BusinessDbConnectionSource dbcCasted =
			(dbc instanceof BusinessDbConnectionSource) ?
				((BusinessDbConnectionSource) dbc) : null;
		
		ToWindow tw =
			new ToWindow(dbcCasted, sourceTable, timeAttrib, valueAttrib,
						 targetTable, outTimeStart, outTimeEnd,
						 tcArray, distance);
		tw.calc();
		tw = null;
	}
	
    /** private helper method */
    private static String[] parseNamesList (String s) {
		if (s == null) {
	    	return null;
		}
				
		Vector values = new Vector();
		int stop = 0;
		for (int from = 0; from < s.length(); from = stop) {
			from = nextNonWhitespace(s, from);
			if (from == s.length())
				break;
			stop = nextWhitespace(s, from);
			String sub = s.substring(from, stop);
			values.add(sub);
		}
		String names[] = new String[values.size()];
		for (int arg = 0; arg < names.length; arg++)
			names[arg] = (String) values.elementAt(arg);
		return names;
	}

    /** private helper method */
    private static int nextNonWhitespace(String s, int index) {
		for (;((index<s.length())&&(Character.isWhitespace(s.charAt(index))));index++);
		return (index);
    }

    /** private helper method */
    private static int nextWhitespace(String s, int index) {
		for (;((index<s.length())&&(!Character.isWhitespace(s.charAt(index))));index++);
		return (index);
    }

}
