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

public class SMF {

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

    private static final int INDEXONATTRIB = 2;

    /**
     * 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 timeStartOut name of the interval start attribute
     * for the output table
     * @param timeEndOut name of the interval end attribute
     * for the output table
     * @param columnOut name of the output value attribute
     * @param windowSize size of the window
     * @param distance step size for windowing
     * @throws SQLException
     * @throws TimeOperatorException
    */    
    public SMF(BusinessDbConnectionSource dbc,
				String source, String time, String column, String target,
				String timeStartOut, String timeEndOut, String columnOut,
				int windowSize, int distance)
		throws SQLException, TimeOperatorException
	{
		this.connector = new DbConnector(dbc);
		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 timeStartAttr =
			new Attribute(timeStartOut, timeDT, timeDT);
		final Attribute timeEndAttr = new Attribute(timeEndOut, timeDT, timeDT);
		final Attribute columnOutAttr =
			new Attribute(columnOut, columnDT, DataType.NUMBER);
		Attribute[] allAttributes =
			new Attribute[] { timeStartAttr, timeEndAttr, columnOutAttr };
		this.out =
			new OutputColumnset(
				target,
				allAttributes,
				this.connector.getConnection());

		// Prepare Windowing:
		window = new Windowing(windowSize, 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();
		    if (ret != null) {
				Long start = new Long(this.getWindow().getTimeFirstEntry());
				Long   end = new Long(this.getWindow().getTimeLastEntry());
				Double val = new Double(calcSum(ret) / ret.length);
				this.getOutputColumnset().insert(new Object[] { start, end, val });
	    	}
		} while (ret != null);

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

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

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


    /** calculates the sum of all values stored in an array */
    private double calcSum(String array[]) {
		double ret = 0;		 // the value to return
		for (int i = 0; i < array.length; i++) {
	    	ret+= Double.parseDouble(array[i]);
		}
		return (ret);
    }


    /**
     * This function has to be used in the database as a stored
     * procedure for calculating an SMA.
    */
    public static void dbSMF(Object dbc,
    						 String source, String time, String column, 
							 String target, String timeStartOut, String timeEndOut,
							 String columnOut, int windowSize, int distance)
		throws SQLException, TimeOperatorException
    {
    	BusinessDbConnectionSource dbcCasted =
			(dbc instanceof BusinessDbConnectionSource) ?
				((BusinessDbConnectionSource) dbc) : null;
		
    	
		SMF smf = new SMF(dbcCasted, source, time, column, target, timeStartOut,
						  timeEndOut, columnOut, windowSize, distance);
		smf.calc();
		smf = null;
    }
}
