/*
 * 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.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;

/**
 * Abstract superclass for the two operators
 * <code>SupportVectorMachineForClassification</code> and
 * <code>SupportVectorMachineForRegression</code>
 * 
 * @author Timm Euler
 * @version $Id: SVMforDataMining.java,v 1.4 2006/04/11 14:10:11 euler Exp $
 */
public abstract class SVMforDataMining extends FeatureConstruction
{
    private SVM_Wrapper mysvm = null;
    private boolean useDatabaseSVM = false;  // set the default here (is overridden
    										 // by parameter 'UseDB_SVM')

	/**
	 * Abstract method to be implemented by subclasses.
	 * Serves to call the adequate SVM wrapper constructors.
	 * 
	 * @param useDB_SVM True iff the SVM implementation inside
	 *        the database is to be used.
	 */
	protected abstract SVM_Wrapper constructWrapper(boolean useDB_SVM) throws M4CompilerError;
	
    /** 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
     */
    public String generateSQL(Column columnToFill) throws M4CompilerError
    {
	    //Call external learning operator
	    this.callStatement(columnToFill);

	    //create statement calling the generated SQL-Function
	    String fctCall = mysvm.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 that can be used to predict values
     * for the output column.
     */
    private void callStatement(Column columnToFill) throws M4CompilerError
    {
        this.useDatabaseSVM = this.getUseDatabaseSVM();
        
        mysvm = this.constructWrapper(this.useDatabaseSVM);
	    
	    try {
	        Columnset theColumnSet = getTheInputConcept().getCurrentColumnSet();
	        String fctSchema = theColumnSet.getSchema();
	        if (fctSchema.length() == 0)
	        {  fctSchema = null;  }
	        
	        Vector theCols = new Vector();
		    for (int i = 0; i < this.getThePredictingAttributes().length; i++)
	        {   theCols.add(this.getThePredictingAttributes()[i].getCurrentColumn());  }
	        theCols.trimToSize();
	
		    mysvm.callSVM(	theColumnSet,
			                columnToFill,
	                        getTheInputConcept().getId(),
	                        this.getC(),
	                        this.getKernelType(),
	                        this.getEpsilon(),
	                        theCols);
	    }
   		catch (M4Exception m4e)
   		{   throw new M4CompilerError("M4 interface error in " + this.getName() + ": " + m4e.getMessage());  } 

        try { this.getM4Db().commitM4Transactions();  }
        catch (SQLException sqle)
        {  throw new M4CompilerError("SVM-Wrapper: 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)
	protected 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 the SVM parameter C as a String
	 */
	public String getC() throws M4CompilerError {
		return ((Value) this.getSingleParameter("C", this.getCurrentLoopNumber())).getValue();
	}

	/**
	 * Gets the parameter 'TheKey'.
	 * 
	 * @return Returns a BaseAttribute
	 */
	public BaseAttribute getKey() throws M4CompilerError {
		return (BaseAttribute) this.getSingleParameter("TheKey", this.getCurrentLoopNumber());
	}
	
	/**
	 * Gets the epsilon.
	 * 
	 * @return the SVM parameter epsilon as a String
	 */
	public String getEpsilon() throws M4CompilerError {
		return ((Value) this.getSingleParameter("Epsilon", this.getCurrentLoopNumber())).getValue();
	}

	/**
	 * Gets the kernelType.
	 * 
	 * @return Returns the kernel type parameter as a string
	 */
	public String getKernelType() throws M4CompilerError {
		return ((Value) this.getSingleParameter("KernelType", this.getCurrentLoopNumber())).getValue();
	}

	/**
	 * Gets the lossFunctionNeg.
	 * 
	 * @return Returns a String
	 */
	public String getLossFunctionNeg() throws M4CompilerError {
		return ((Value) this.getSingleParameter("LossFunctionNeg", this.getCurrentLoopNumber())).getValue();
	}

	/**
	 * Gets the lossFunctionPos.
	 * 
	 * @return Returns a String
	 */
	public String getLossFunctionPos() throws M4CompilerError {
		return ((Value) this.getSingleParameter("LossFunctionPos", this.getCurrentLoopNumber())).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("SupportVectorMachineForRegression: SampleSize must be an integer! Found: " + v.getValue());  }
	    return sample;
	}

	/**
	 * Gets the thePredictingAttributes.
	 * 
	 * @return Returns a BaseAttribute
	 */
	public BaseAttribute[] getThePredictingAttributes() throws M4CompilerError {
		return (BaseAttribute[]) this.getParameter("ThePredictingAttributes", this.getCurrentLoopNumber());
	}

	public boolean getUseDatabaseSVM() throws M4CompilerError {   
		Value v = (Value) this.getSingleParameter("UseDB_SVM", this.getCurrentLoopNumber()); 
		if (v == null)
		{  return this.useDatabaseSVM;  }  // default
		String s = v.getValue();
		if (s.equalsIgnoreCase("true"))
		{   return true;   }
		else
		{   return false;  }
	}
}
/*
 * Historie
 * --------
 *
 * $Log: SVMforDataMining.java,v $
 * Revision 1.4  2006/04/11 14:10:11  euler
 * Updated license text.
 *
 * Revision 1.3  2006/04/06 16:31:11  euler
 * Prepended license remark.
 *
 * Revision 1.2  2006/03/23 11:13:45  euler
 * Improved exception handling.
 *
 * Revision 1.1  2006/01/03 09:54:22  hakenjos
 * Initial version!
 *
 */
