/*
 * MiningMart Version 1.0
 * 
 * Copyright (C) 2006 Martin Scholz, Timm Euler, 
 *                    Daniel Hakenjos, Katharina Morik
 *
 * Contact: miningmart@ls8.cs.uni-dortmund.de
 *
 * A list of contributing developers (other than the copyright 
 * holders) can be found at
 * http://mmart.cs.uni-dortmund.de/downloads/download.html
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program, see the file MM_HOME/LICENSE; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 */
package edu.udo.cs.miningmart.operator;

import java.sql.SQLException;
import java.util.Vector;

import edu.udo.cs.miningmart.compiler.wrapper.DB_SVM_RG;
import edu.udo.cs.miningmart.compiler.wrapper.SVM_RG;
import edu.udo.cs.miningmart.compiler.wrapper.SVM_Wrapper;

import edu.udo.cs.miningmart.exception.M4CompilerError;
import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.m4.BaseAttribute;
import edu.udo.cs.miningmart.m4.Column;
import edu.udo.cs.miningmart.m4.Columnset;
import edu.udo.cs.miningmart.m4.Value;
import edu.udo.cs.miningmart.m4.utils.Print;


/**
 * The class <i>MissingValuesWithRegressionSVM</i>
 * implements the abstract methods createFunction and
 * defineOutputDatatype (from class AttributeOperator).
 * The class uses instances of class <i>Print</i>.
 * The class calls an external wrapper for calling the
 * learning tool SupportVectorMachine.
 *
 * @author Timm Euler
 * @version $Id: MissingValuesWithRegressionSVM.java,v 1.4 2006/04/11 14:10:10 euler Exp $
 */
public class MissingValuesWithRegressionSVM extends MissingValues  {

	private SVM_Wrapper svm;
	private boolean useDatabaseSVM = false;

    /** Generates a sql-function to describe a new column.
     * This method generates a sql-function to describe a new
     * column. The function is generated by an external
     * learning operator. The external wrapper is called
     * in function 'callStatement'. Afterwards the
     * function call to use it is generated.
     *
     * @return the SQL-function call
     * @change March, 11th, 2002 method created
     */
    public String generateValueForOp(Column columnWithMissingValues)
				  throws M4CompilerError
	{
		//Call external learning operator
		this.callStatement(columnWithMissingValues);

		//create statement calling the generated SQL-Function
		String fctCall = svm.getDecisionFunctionName() + "(";

		try {
			for (int i = 0; i < this.getThePredictingAttributes().length; i++)
			    fctCall = fctCall + this.getThePredictingAttributes()[i].getCurrentColumn().getSQLDefinition() + ", ";
		}
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 

		// Delete last ", " and add the closing ")"
		fctCall = fctCall.substring(0,fctCall.length()-2) + ")";

		//return DB.quote(fctCall);
	    return fctCall;
    };

    /** Private function to call the external wrapper for
     * calling the learning algorithm SupportVectorMachine. When
     * the function returns without error, an sql-function
     * exists within the database, which predicts values
     * for missing values.
     *
     * @change December, 13th, 2001 method created
     */
    private void callStatement(Column columnWithMissingValues)
				 throws M4CompilerError
	{
		svm = null;
       	long sample = this.getSampleSize();
		try
    	{
       		if (this.useDatabaseSVM)
	       	{
    	   		BaseAttribute keyBA = this.getKey();
       			String key = null;
       			if (keyBA != null)
	       		{  key = keyBA.getCurrentColumn().getName();  }
       		
       			if (sample == -1)
       			{
    	   			svm = new DB_SVM_RG( this.getM4Db(),
					  			   		 this.getM4Db().getCasePrintObject(),
					    			   	 this.getPrefixForDatabaseObjects(),
					    			   	 this.getDatabaseSchema(),
				    				   	 this.getStep().getId(),
				    				   	 key,
							             this.getLossFunctionPos(),
							             this.getLossFunctionNeg());
       			}
       			else
       			{       				
    	   			svm = new DB_SVM_RG( this.getM4Db(),
					  			   		 this.getM4Db().getCasePrintObject(),
					    			   	 this.getPrefixForDatabaseObjects(),
					    			   	 this.getDatabaseSchema(),
				    				   	 this.getStep().getId(),
				    				   	 key,
				    				   	 sample,
							             this.getLossFunctionPos(),
							             this.getLossFunctionNeg());
       			}       			
	       	}
	       	else
    	   	{        
	        	svm = new SVM_RG( this.getM4Db(),
			     		   		  this.getM4Db().getCasePrintObject(),
			    			   	  this.getPrefixForDatabaseObjects(),
			    			   	  this.getDatabaseSchema(),
			    			   	  this.getStep().getId(),
				   			   	  sample,
				   			   	  this.getLossFunctionPos(),
				   			   	  this.getLossFunctionPos());
    	   	}
    	}
	    catch (java.lang.InterruptedException e)
    	{   this.doPrint(Print.MAX, "Error while constructing SVM: " + e.getMessage());  }
	    catch (java.lang.Exception e)
	    {   this.doPrint(Print.MAX, "Error with MissingValuesWithRegressionSVM: " + e.getMessage());  }

		Columnset theColumnSet;
		Vector theCols = new Vector();
		try {
		    theColumnSet = getTheInputConcept().getCurrentColumnSet();
		    // String fctSchema = theColumnSet.getSchema();
		    // if (fctSchema.length() == 0) { fctSchema = null;  }
		    BaseAttribute[] bas = this.getThePredictingAttributes();
			for (int i = 0; i < bas.length; i++)
		    {   theCols.add(bas[i].getCurrentColumn());  }
		    theCols.trimToSize();
		}
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 
	
		svm.callSVM( theColumnSet,
		             columnWithMissingValues,
		             getTheInputConcept().getId(),
		             this.getC(),
		             this.getKernelType(),
		             this.getEpsilon(),
		             theCols
		            );

	    try { this.getM4Db().commitM4Transactions();  }
    	catch (SQLException sqle)
	    {  throw new M4CompilerError("MissingValuesWithRegressionSVM: could not commit DB updates: " + sqle.getMessage());  }
    }
    
	// returns a string that is used by the wrapper to create 
	// database objects (whose name is prefixed by this string)
	private String getPrefixForDatabaseObjects() throws M4CompilerError
	{
		try {
	        String loopIdentification = "";
	        if (this.getHighestLoopNr() > 1)
	        {   loopIdentification = "L" + this.getCurrentLoopNumber();   }
	        
    	    return this.getTheInputConcept().getCurrentColumnSet().getName() + loopIdentification;
		}
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 
	}       
    
	// returns the name of the database schema in which the input table or view lives
	protected String getDatabaseSchema() throws M4CompilerError {
		try {
			return this.getTheInputConcept().getCurrentColumnSet().getSchema();
		}
		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 
	}
	
    /**
	 * Gets the c.
	 * 
	 * @return Returns a String
	 */
	public String getC() throws M4CompilerError {
		Value v = (Value) this.getSingleParameter("C", this.getCurrentLoopNumber());
		if (v == null)
		{  throw new M4CompilerError("MissingValuesWithRegressionSVM: Parameter C not found in loop " + 
			                         this.getCurrentLoopNumber());  }
		return v.getValue();
	}

	/**
	 * Gets the epsilon.
	 * 
	 * @return Returns a String
	 */
	public String getEpsilon() throws M4CompilerError {
		Value v = (Value) this.getSingleParameter("Epsilon", this.getCurrentLoopNumber());
		if (v == null)
		{  throw new M4CompilerError("MissingValuesWithRegressionSVM: Parameter Epsilon not found in loop " + 
			                         this.getCurrentLoopNumber());  }
		return v.getValue();
	}
	
	/**
	 * Gets the kernelType.
	 * 
	 * @return Returns a String
	 */
	public String getKernelType() throws M4CompilerError {
		Value v = (Value) this.getSingleParameter("KernelType", this.getCurrentLoopNumber());
		if (v == null)
		{  throw new M4CompilerError("MissingValuesWithRegressionSVM: Parameter KernelType not found in loop " + 
			                         this.getCurrentLoopNumber());  }
		return v.getValue();
	}

	/**
	 * Gets the lossFunctionNeg.
	 * 
	 * @return Returns a String
	 */
	public String getLossFunctionNeg() throws M4CompilerError {
		Value v = (Value) this.getSingleParameter("LossFunctionNeg", this.getCurrentLoopNumber());
		if (v == null)
		{  throw new M4CompilerError("MissingValuesWithRegressionSVM: Parameter LossFunctionNeg not found in loop " + 
			                         this.getCurrentLoopNumber());  }
		return v.getValue();
	}
	
	/**
	 * Gets the lossFunctionPos.
	 * 
	 * @return Returns a String
	 */
	public String getLossFunctionPos() throws M4CompilerError {
		Value v = (Value) this.getSingleParameter("LossFunctionPos", this.getCurrentLoopNumber());
		if (v == null)
		{  throw new M4CompilerError("MissingValuesWithRegressionSVM: Parameter LossFunctionPos not found in loop " + 
			                         this.getCurrentLoopNumber());  }
		return v.getValue();
	}

	/**
	 * Gets the sampleSize.
	 * 
	 * @return -1 if the parameter was not specified; otherwise the sample size as a <code>long</code>
	 */
	public long getSampleSize() throws M4CompilerError
	{
		long sample;
		Value v = (Value) this.getSingleParameter("SampleSize", this.getCurrentLoopNumber());
		
		if (v == null) 
		{  return -1;  }
		
        try
		{   sample = Long.parseLong(v.getValue());  }
		catch (NumberFormatException nfe)
	    {  throw new M4CompilerError("MissingValuesWithRegressionSVM: SampleSize must be an integer! Found: " + v.getValue());  }
	    return sample;
	}

	/**
	 * Gets ThePredictingAttributes.
	 * 
	 * @return Returns an array of BaseAttributes
	 */
	public BaseAttribute[] getThePredictingAttributes() throws M4CompilerError {
		return (BaseAttribute[]) this.getParameter("ThePredictingAttributes", this.getCurrentLoopNumber());
	}
	
	/**
	 * Gets the <b>optional</b> parameter 'TheKey'.
	 * 
	 * @return Returns a BaseAttribute or <code>null</code>
	 */
	public BaseAttribute getKey() throws M4CompilerError {
		return (BaseAttribute) this.getSingleParameter("TheKey", this.getCurrentLoopNumber());		
	}	

	/**
	 * When this method is called before <code>generateSQL()</code>,
	 * this operator will use mySVM/db, otherwise mySVM.
	 */
	public void setToDatabaseSVM()
	{   this.useDatabaseSVM = true;   }
	
	/**
	 * When this method is called before <code>generateSQL()</code>,
	 * this operator will use mySVM.
	 */
	public void setToStandardSVM()
	{   this.useDatabaseSVM = false;   }
}
/*
 * Historie
 * --------
 * 
 * $Log: MissingValuesWithRegressionSVM.java,v $
 * Revision 1.4  2006/04/11 14:10:10  euler
 * Updated license text.
 *
 * Revision 1.3  2006/04/06 16:31:10  euler
 * Prepended license remark.
 *
 * Revision 1.2  2006/03/23 11:13:44  euler
 * Improved exception handling.
 *
 * Revision 1.1  2006/01/03 09:54:21  hakenjos
 * Initial version!
 *
 */
