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

import java.awt.Component;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;

import javax.swing.DefaultCellEditor;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.TableCellRenderer;

import org.musoft.limo.application.Application;

import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.gui.application.MiningMartApplication;
import edu.udo.cs.miningmart.m4.M4Interface;
import edu.udo.cs.miningmart.m4.OpParam;
import edu.udo.cs.miningmart.m4.ParameterObject;
import edu.udo.cs.miningmart.m4.Step;
import edu.udo.cs.miningmart.m4.Value;
import edu.udo.cs.miningmart.m4.utils.Print;

/**
 * This is an editor for one parameter in the <code>StepParameterTable</code>.
 * @author Daniel Hakenjos
 * @version $Id: ParameterEditor.java,v 1.7 2006/04/11 14:10:13 euler Exp $
 */
public class ParameterEditor extends DefaultCellEditor implements TableCellRenderer{

	protected MiningMartApplication app;
	
	protected Step step;

	protected OpParam opParam;
	
	protected int numberofloops,loopnumber;
	
	protected Vector paramcollections;

	protected Vector oldparamcollections;

	private Vector changeslistener;
	
	protected boolean editable;

	/**
	 * Default Constructor for editing a <code>JTextField</code>.
	 */
	public ParameterEditor(){
		super(new JTextField());
		changeslistener=new Vector();
		editable=false;
	}
	
    /**
     *Constructs new <code>ParameterEditor</code> with a <code>JTextField</code>.
     */
	public ParameterEditor(MiningMartApplication app,Step step, OpParam opParam){
		super(new JTextField());
		this.opParam=opParam;
		this.app=app;
		this.step=step;
		changeslistener=new Vector();
		
		numberofloops=step.getLoopCount();
		if (numberofloops==0){
			numberofloops=1;
		}
		loopnumber=1;
		
		editable=app.getViewMode()==Application.EDITOR;

		loadParamCollections();

	}
	
	private void addCollection(Collection coll){
		if (coll==null)
			coll=new LinkedList();

		if (opParam.isInput()){
			paramcollections.add(coll);
			oldparamcollections.add(coll);
		}else{
			LinkedList list=new LinkedList();
			if ((coll!=null)&&(!coll.isEmpty())){	
				Iterator iter=coll.iterator();
				Object obj;
				while(iter.hasNext()){
					obj=iter.next();
					if (obj instanceof Value){
						list.add(((Value) obj).getValue());				
					}else if (obj instanceof ParameterObject){
						list.add(((ParameterObject) obj).getName());				
					}else{
						M4Interface.print.doPrint(Print.ERROR,"Unknown Object: "+obj.getClass().getName()+"\nCannot hold this object in the ParameterEditor!");
					}
				}
			}
			paramcollections.add(list);
			oldparamcollections.add(list);
		}
	}
	
	private void loadParamCollections(){
		paramcollections=new Vector();
		oldparamcollections=new Vector();		
		try{
			Collection coll;
			for(int i=0;i<numberofloops;i++){
				if (opParam.isCoordinated()){
					//coordinierte Parameter so auslesen
					coll=step.getParameter(opParam,0);					
					addCollection(coll);
				}else if ((opParam.isLoopable())&&(numberofloops==1)){
					//opParam ist loopable, mechanismus wird im step nicht genutzt
					coll=step.getParameter(opParam,0);					
					addCollection(coll);
					
				}else if ((opParam.isLoopable())&&(numberofloops>1)){
					//opParam ist loopable, mechanismus wird im step genutzt
					//also auslesen von 1...numberofloops
					coll=step.getParameter(opParam,i+1);					
					addCollection(coll);
					
				}else{
					coll=step.getParameter(opParam,0);					
					addCollection(coll);
				}
			}
		}catch(M4Exception error){
			M4Interface.print.doPrint(Print.ERROR,error.getMessage(),error);
		}
	}

	/**
	 * Inits this editor for the current loopnumber.	
	 * This method  is useful in subclasses to set the text or list.
	 */
	public void init(){
	}
	
	
	
	/**
	 * Gets the number of loops of the <code>Step</code>.
	 */
	public int getNumberOfLoops(){
		return numberofloops;
	}
	
	/**
	 * Gets the current loopnumber.
	 */
	public int getLoopNumber(){
		return loopnumber;
	}

	/**
	 * Sets the loopnumber.
	 */	
	public void setLoopNumber(int loopnumber){
		this.loopnumber=loopnumber;
		init();
		setEditable();
		

	}


	/**
	 * Sets the number of the loops.
	 */
	public void setNumberOfLoops(int numberofloops){
		if (numberofloops==0){
			numberofloops=1;
		}
		this.numberofloops=numberofloops;

		if (paramcollections.size()<numberofloops){
			while (paramcollections.size()!=numberofloops){
				paramcollections.add(new LinkedList());
			}
		}else
		if (paramcollections.size()>numberofloops){
			while (paramcollections.size()!=numberofloops){
				paramcollections.remove(paramcollections.size()-1);
			}
		}

	}

	/**
	 * Gets the <code>Collection</code> of the Parameter for the specified loopnumber.
	 */
	public Collection getParameterCollection(int loopnumber){
		boolean loopable=true;
		try{
			loopable=opParam.isLoopable();
		}catch(M4Exception error){
			M4Interface.print.doPrint(Print.ERROR,error.getMessage(),error);
			loopable=true;
		}
		if (loopable){		
			return (Collection) paramcollections.get(loopnumber-1);
		}else{
			return (Collection) paramcollections.get(0);
		}
	}
	
	/**
	 * Gets the <code>Collection</code> for the current loopnumber.
	 */
	public Collection getParameterCollection(){
		return getParameterCollection(loopnumber);
	}
	
	/**
	 * Sets the <code>Collection</code> of the Parameter for the specified loppnumber.
	 */
	public void setParameterCollection(Collection collection, int loopnumber){
		boolean loopable=true;
		try{
			loopable=opParam.isLoopable();
		}catch(M4Exception error){
			M4Interface.print.doPrint(Print.ERROR,error.getMessage(),error);
			loopable=true;
		}

		if (!opParam.isInput()){
			//nur strings sollten in der Collection sein!
			Iterator iter=collection.iterator();
			boolean onlystrings=true;
			while (iter.hasNext()&&onlystrings){
				if (!(iter.next() instanceof String)){
					onlystrings=false;
				}
			}

			//die Collection sollte nur Strings enthalten
			if (!onlystrings){
				LinkedList list=new LinkedList();						
				iter=collection.iterator();
				Object obj;
				while (iter.hasNext()){
					obj=iter.next();
					if (obj instanceof String){
						//Sehr gut, dass es ein String ist
						//dies verwundert hier ein wenig
						//weil es so nicht vorkommen sollte
						//aber wir koennen es ja trotzdem hinzufuegen
						list.add(obj);
					}else if (obj instanceof Value){
						list.add(((Value) obj).getValue());
					}else if (obj instanceof ParameterObject){
						list.add(((ParameterObject) obj).getName());
					}else{
						M4Interface.print.doPrint(Print.ERROR,"Unknown Object: "+obj.getClass().getName()+"\nCannot hold this object in the ParameterEditor!");
					}
				}
				collection=list;
			}
		}
		

		if (loopable){
			paramcollections.set(loopnumber-1, collection);
		}else{
			for(int i=0;i<paramcollections.size();i++){
				paramcollections.set(i, collection);
			}
		}
		
	}
	
	/**
	 * Sets the <code>Collection</code> of the Parameter for the current loopnumber.
	 */
	public void setParameterCollection(Collection collection){
		setParameterCollection(collection,loopnumber);
	}

	/**
	 * Gets a <code>Collection</code> with <code>Collection</code>s for each loopnumber.
	 */
	public Collection getAllParameterCollections(){
		return paramcollections;
	}

	/**
     *Gets the <code>editorComponent</code>.
	 * @see javax.swing.table.TableCellRenderer#getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)
	 */
	public Component getTableCellRendererComponent(
		JTable table,
		Object value,
		boolean isSelected,
		boolean hasFocus,
		int row,
		int column) {
		return editorComponent;
	}
	
	/**
	 * Saves the <code>Collection</code>s in the <code>Step</code>.
	 * Only input-parameters.
	 * @see edu.udo.cs.miningmart.m4.Step#setParameter(OpParam,Collection,int)
	 */
	public void setCollectionsInStep() throws M4Exception {
		if (!opParam.isInput()) {
			return;
		}

		Collection coll;
		for (int loop = 1; loop <= numberofloops; loop++) {
			coll = getParameterCollection(loop);
			if (coll == null) {
				coll = new LinkedList();
			}

			//Der Sonderfall: step is loopable, 
			//aber nur die coordinierten Parameter werden in loops angegeben.
			//dann sollte die For-Schleife sowieso nur einaml durchlaufen werden,
			//da es trotzdem nur eine loop sprich keine loop fuer den step gibt.

			if (opParam.isCoordinated()) {

				//zunaechst die alten Werte loeschen
				
				Collection coordcoll = step.getParameter(opParam, 0);
				int coordloops;
				if (coordcoll == null) {
					coordloops = 0;
				} else {
					coordloops = coordcoll.size();
				}
				
				//step.removeParameter(opParam.getName());
				
				step.setParameter(opParam, new LinkedList(), 0);
				for (int i = coordloops; i >= 1; i--) {
					step.setParameter(opParam, new LinkedList(), i);
				}
				
				Iterator iter = coll.iterator();
				int coord = 1;
				LinkedList list;
				while (iter.hasNext()) {
					list = new LinkedList();
					list.add(iter.next());
					step.setParameter(opParam, list, coord);
					coord++;
				}
			} else {

				//Dies sind ganz normale Faelle:
				// Ist der Step loopable, so werden die Parameter-Collection 
				//mit der jeweiligen loop angegeben.
				//Sollten sie nicht loopable sein, so werden die Parameter-Collection 
				//mit loopnumber 0 uebergeben.

				if ((!opParam.isLoopable()) && (loop == 1)) {
					step.setParameter(opParam, coll, 0);
				} else if ((!opParam.isLoopable()) && (loop > 1)){
					//hier ist nichts zu tun, da der OpParam ja nicht loopable ist!
				} else {

					//sollte der Benutzer loops eingegeben haben,
					//so wird auch die loopnumber uebergeben
					if (numberofloops > 1) {
						// die tatsache dass es mehrere loops gibt koennte neu sein.
						// Also sicherheitshalber alte eintraege loeschen:
						step.setParameter(opParam, new Vector(), 0);
						step.setParameter(opParam, coll, loop);
					} else {
						/*System.out.println();
						System.out.println();
						System.out.println();
						System.out.println("Aufruf setzen opParam: "+opParam.getName()+"\t loop: 0");
						System.out.println("coll" + coll.toString());*/
						step.setParameter(opParam, coll, 0);
					}
				}

			}
		}
	}
	
	/**
	 * Checks wether the output exists or not.
	 * If it is an input parameter then it returns false.
	 * Otherwise if all <code>Collection</code>s of the old Parameters are null or empty then the ouput doesn't exists.
	 */
	public boolean existsOutput(){
		return false;
	}

	/**
	 * Creates,updates and/or renames the output of the parameter.
	 */	
	public void makeOutput(boolean inputchanged, boolean outputchanged, boolean loopcountlower, boolean loopcounthigher) throws M4Exception{
		if (opParam.isInput()){
			return;
		}
		
		if (loopcountlower && loopcounthigher){
			throw new M4Exception("The loopcount could not be lower and higher!");	
		}
		
		boolean outputexists=existsOutput();
		
		Collection outcoll=new LinkedList();
		if (opParam.isLoopable()){
			outcoll.addAll(paramcollections);
		}else{
			if (opParam.isCoordinated()){
					Collection coll=(Collection) paramcollections.get(0);
					Iterator iter=coll.iterator();
					LinkedList list;
					while(iter.hasNext()){
						list=new LinkedList();
						list.add(iter.next());
						((LinkedList) outcoll).add(list);
					}
			}else{
				Iterator iter=paramcollections.iterator();
				while(iter.hasNext()){
					Collection inner = (Collection) iter.next();
					Iterator innerIt = inner.iterator();
					while (innerIt.hasNext()) {
						Object o = innerIt.next();
						if ( ! outcoll.contains(o)) {
							if (o instanceof String) {
								outcoll.add(o);
							}
							else {
								outcoll.add(((ParameterObject) o).getName());
								/*System.out.println("ParameterEditor.makeOutput: found Parameter object in paramcollections for OpParam " + opParam.getName());
								 System.out.println("ParameterObject: "+o.getClass().getName());
								 System.out.println("ParameterEditor: "+getClass().getName());*/
							}
						}
					}
				}
			}
		}
		
		if (loopcountlower){

			if (!outputexists){
				//OutputObject exisitiert noch nicht.
				// sollte nicht vorkommen....
				step.createOutput(opParam,outcoll);
			}
			else{
				//OutputObject existiert
				step.updateOutput(opParam);
					
				if (outputchanged){
					step.renameOutput(opParam,outcoll);
				}
			}
			outputchanged=false;
			
		}else if (loopcounthigher){

			if (!outputexists){
				//OutputObject exisitiert noch nicht.
				step.createOutput(opParam,outcoll);
			}
			else{
				//OutputObject existiert
				//mit setParameter alle loeschen, dann createOutput aufrufen
				
				LinkedList emptyList=new LinkedList();
				
				for (int loop=1;loop<=numberofloops;loop++){
					if ((!opParam.isLoopable())&&(loop==1)){
						step.setParameter(opParam,emptyList,0);
					}
					else{
						step.setParameter(opParam,emptyList,loop);
					}
				}
				
				step.createOutput(opParam,outcoll);
				
			}
			outputchanged=false;
			
		}else{

			//System.out.println("OpParam: "+opParam.getName());
			//System.out.println("outputexists: "+outputexists);
			if (!outputexists){
				// DIESES OutputObject exisitiert noch nicht, ein anderes
				// koennte schon existieren:
				if (outputchanged) {
					// altes OutputObject existiert ->
					// mit setParameter loeschen, dann createOutput aufrufen					
					LinkedList emptyList=new LinkedList();					
					for (int loop=1;loop<=numberofloops;loop++){
						if ((!opParam.isLoopable())&&(loop==1)){
							step.setParameter(opParam,emptyList,0);
						}
						else{
							step.setParameter(opParam,emptyList,loop);
						}
					}					
					step.createOutput(opParam,outcoll);
				}
				else {
					step.createOutput(opParam,outcoll);
				}
			}
			else{
				//OutputObject existiert
				if (inputchanged){
					//System.out.println("updateOutput()");
					step.updateOutput(opParam);
				}					
				if (outputchanged){
					//System.out.println("renameOutput()");
					step.renameOutput(opParam,outcoll);
				}
			}
			outputchanged=false;
			
		}
		
		//hole dir die neuen OutputCollectionen
		loadParamCollections();
		init();

	}
	
	/**
	 * Adds the <code>ChangeListener</code>.
	 */	
	public void addChangeListener(ChangeListener listener){
		changeslistener.add(listener);
	}
	
	/**
	 * Removes the <code>ChangeListener</code>.
	 */
	public void removeChangeListener(ChangeListener listener){
		changeslistener.remove(listener);
	}
	
	/**
	 * Removes all <code>ChangeListener</code>.
	 */
	public void removeAllChangeListener(){
		changeslistener.removeAllElements();
	}
	
	/**
	 * Gets the number of <code>ChangeListener</code>.
	 */
	public int getChangeListenerCount(){
		return changeslistener.size();
	}
	
	/**
	 * Gets the <code>ChangeListener</code> by its index.
	 */
	public ChangeListener getChangeListener(int index){
		return (ChangeListener) changeslistener.get(index);
	}
	
	/**
	 * Informs all <code>ChangeListener</code> that changed occurred.
	 */
	public void fireStateChanged(){
		for(int i=0;i<changeslistener.size();i++){
			getChangeListener(i).stateChanged(new ChangeEvent(this));
		}
	}

	/**
	 * Gets the <code>OpParam</code>.
	 */	
	public OpParam getTheOpParam(){
		return opParam;
	}

	/**
	 * The values before editing will be displayed.
	 * They are also set to the step.
	 * @see edu.udo.cs.miningmart.m4.Step#setParameter(OpParam,Collection,int)
	 */	
	public void cancelEditing() throws M4Exception{
		paramcollections=new Vector();

		for (int i=0;i<oldparamcollections.size();i++){
			paramcollections.add(oldparamcollections.get(i));
		}
		init();
		setCollectionsInStep();
	}
	

	/**
	 * Sets the editable-flag.
	 */
	public void setEditable(){

		if (app.getViewMode()==Application.EDITOR){

			if (loopnumber>1){
				try{
					if (opParam.isLoopable()){
						editable=true;
					}else{
						editable=false;
					}
				}catch(M4Exception error){
					M4Interface.print.doPrint(Print.ERROR,error.getMessage(),error);
				}
			}else{
				editable=true;
			}
			
		}else{
			editable=false;
		}
		
	}

}
/*
$Log: ParameterEditor.java,v $
Revision 1.7  2006/04/11 14:10:13  euler
Updated license text.

Revision 1.6  2006/04/06 16:31:12  euler
Prepended license remark.

Revision 1.5  2006/03/16 14:53:38  euler
*** empty log message ***

Revision 1.4  2006/03/06 08:37:34  euler
*** empty log message ***

Revision 1.3  2006/03/02 16:49:59  euler
Many bugfixes

Revision 1.2  2006/02/06 13:31:17  euler
Bugfix

Revision 1.1  2006/01/03 09:54:14  hakenjos
Initial version!

*/
