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

import java.awt.Color;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;

import javax.swing.JOptionPane;

import org.musoft.limo.application.Application;
import org.musoft.limo.application.Resource;
import org.musoft.limo.model.Model;
import org.musoft.limo.model.ModelAssociationEnd;
import org.musoft.limo.model.ModelAttribute;
import org.musoft.limo.model.ModelFigureElement;
import org.musoft.limo.model.ModelPrimitiveAttribute;

import edu.udo.cs.miningmart.exception.M4Exception;
import edu.udo.cs.miningmart.gui.application.MiningMartApplication;
import edu.udo.cs.miningmart.m4.Case;
import edu.udo.cs.miningmart.m4.Chain;
import edu.udo.cs.miningmart.m4.M4Data;
import edu.udo.cs.miningmart.m4.M4Interface;
import edu.udo.cs.miningmart.m4.Step;
import edu.udo.cs.miningmart.m4.utils.Print;


/**
 *A wrapper for <code>Chain</code>s.
 *A <code>MiningMartChain</code> could be a child of a <code>MiningMartCase</code> or of a <code>MiningMartChain</code>.
 * @author Daniel Hakenjos
 * @version $Id: MiningMartChain.java,v 1.4 2006/04/11 14:10:15 euler Exp $
 */
public class MiningMartChain extends MiningMartModel {
	
	private M4Interface m4Interface=M4Interface.getInstance();

	private Point point;
	
	private String casename;
	
	private Case thecase;
	private Chain chain;
	
	private MiningMartApplication app;

	private int transcounter;

	private boolean deleteInDatabase=true;

    /**
     * Creates a new <code>MiningMartChain</code> with the given parameters.
     */
    public MiningMartChain(MiningMartApplication app,Case thecase,Chain chain,String casename, Point point, Rectangle bounds, ModelFigureElement parent) {
        super(chain.getName(),bounds, parent);

		this.thecase=thecase;
		this.chain=chain;
		this.point=point;
		this.app=app;
		this.casename=casename;
		this.transcounter=0;
        addAttribute(new ModelAssociationEnd(this, "incoming", ModelAttribute.ACCESS_READWRITE, true, StepTransition.class, "end"));
        addAttribute(new ModelAssociationEnd(this, "outgoing", ModelAttribute.ACCESS_READWRITE, true, StepTransition.class, "start"));
        try{
	        ((ModelPrimitiveAttribute)getAttribute("notes")).setString(chain.getDocumentation());
        }catch(M4Exception error){
        }        
        
        ((ModelPrimitiveAttribute)getAttribute(Resource.getString("ATTR_COLOR"))).setColor(Color.WHITE);
	
    }

	/**
	 * Sets the flag that this object could be deleted in the database.
	 */    
	public void setDeleteInDatabase(boolean bool,boolean propagate){
		this.deleteInDatabase=bool;
		if (propagate){
			for(int i=0;i<this.getConnectionCount();i++){
					((StepTransition) this.getConnection(i)).setDeleteInDatabase(bool);    			
			}
			for(int i=0;i<this.getChildCount();i++){
				ModelFigureElement child=this.getChild(i);
				if (child instanceof MiningMartChain){
					((MiningMartChain) child).setDeleteInDatabase(bool,true);
				}
				if (child instanceof MiningMartStep){
					((MiningMartStep) child).setDeleteInDatabase(bool);
				}
			}
		}
	}
    
	/**
	 * Gets the flag indicating that this obkect can be deleted in the database.
	 */
	public boolean canDeleteInDatabase(){
		return this.deleteInDatabase;
	}

    /**
     * Gets the counter for the transitions.
     */
    public int getTransitionCounter(){
    	return transcounter;
    }

	/**
	 * Increments the counter for the transitions.
	 */    
    public int incrementTransitionCounter(){
    	transcounter++;
    	return transcounter;
    }
    
	/**
	 * Decrements the counter for the transitions.
	 */    
    public int decrementTransitionCounter(){
    	if (transcounter>0)
	    	transcounter--;
    	return transcounter;
    }

    /**
     *Gets the <code>MiningMartAplication</code>.
     */
	public MiningMartApplication getMMartApplication(){
		return app;
	}

        /**
         *Inits this <code>MiningMartChain</code>.
         */
	public void initTheChain(){
		try{
			Collection c=this.chain.getDirectSubChains();
			Iterator iter=c.iterator();
			Chain chain;
			MiningMartChain mchain;
			Point p;
			int i=10;
			while (iter.hasNext()){
				chain=(Chain) iter.next();
				p=chain.getPoint();
				if (p==null){
					p=new Point(i,i);
					chain.setPoint(p);
				}
				mchain=new MiningMartChain(app,thecase,chain,this.chain.getTheCase().getName(),p,new Rectangle(p.x,p.y,35,35),this);
		        mchain.initTheChain();
				addMiningMartChain(this,mchain);
				i+=60;
			}
			
//			long zeit=System.currentTimeMillis();
			c=this.chain.getTopLevelSteps();
			iter=c.iterator();
			Step step;
			MiningMartStep mstep;
			i=10;
			while (iter.hasNext()){
				step=(Step) iter.next();
				p=step.getPoint();
				if (p==null){
					p=new Point(i,i);
					step.setPoint(p);
				}
				mstep=new MiningMartStep(app,step,this,p,new Rectangle(p.x,p.y,35,35));
				addMiningMartStep(this,mstep);
				i+=60;
			}
//			zeit=System.currentTimeMillis()-zeit;
//			System.out.println("zeit: "+zeit+"ms");
			
			organizeTransitions(false);
			
		}catch(M4Exception error){
			M4Interface.print.doPrint(Print.ERROR,error.getMessage(),error);
		}
	}
	
	/**
         *Adds transitions to the model if the case contains dependencies.
         */
	public void organizeTransitions(boolean check) throws M4Exception{
			int childs=this.getChildCount();
			ModelFigureElement elem1,elem2;
			MiningMartStep mstep1=null,mstep2=null;
			MiningMartChain mchain1=null, mchain2=null;
			Step step1=null,step2=null;
			Chain chain1=null,chain2=null;
			StepTransition transition;
			boolean bchain1=false,bchain2=false,bstep1=false,bstep2=false;
			
			boolean create;
			for (int a=0;a<childs;a++){
				elem1=this.getChild(a);	

				if (elem1 instanceof MiningMartChain){
					mchain1=(MiningMartChain) elem1;
					chain1=mchain1.getChain();
					bchain1=true;
					bstep1=false;
				}
				if (elem1 instanceof MiningMartStep){
					mstep1=(MiningMartStep) elem1;
					step1=mstep1.getStep();
					bchain1=false;
					bstep1=true;
				}
				
				for (int j=0;j<childs;j++){

					elem2=this.getChild(j);
						
					if (elem2 instanceof MiningMartChain){
						mchain2=(MiningMartChain) elem2;
						chain2=mchain2.getChain();
						bchain2=true;
						bstep2=false;
					}
					if (elem2 instanceof MiningMartStep){
						mstep2=(MiningMartStep) elem2;
						step2=mstep2.getStep();
						bchain2=false;
						bstep2=true;
					}
					
					create=true;
					if ((bchain1)&&(bchain2)&&(chain1.dependencyExists(chain2))){
						if (mchain1.getName().equals(mchain2.getName())){
							//dies sind zwei gleiche Elemente
							continue;
						}
						if (check){
							create=!existsTransition(mchain1,mchain2);
						}
						if (create){
							incrementTransitionCounter();
							transition=new StepTransition(app,thecase,Resource.getString("DEPENDENCY")+"("+getTransitionCounter()+")",(Model) this,mchain1,mchain2);
//							transition=new StepTransition(app,thecase,chain1.getName(),(Model) this,mchain1,mchain2);
							addStepTransition(transition);
						}
					}
					if ((bchain1)&&(bstep2)&&(chain1.dependencyExists(step2))){
						if (check){
							create=!existsTransition(mchain1,mstep2);
						}
						if (create){
							incrementTransitionCounter();
							transition=new StepTransition(app,thecase,Resource.getString("DEPENDENCY")+"("+getTransitionCounter()+")",(Model) this,mchain1,mstep2);
//							transition=new StepTransition(app,thecase,chain1.getName(),(Model) this,mchain1,mstep2);
							addStepTransition(transition);
						}
					}
					if ((bstep1)&&(bchain2)&&(step1.dependencyExists(chain2))){
						if (check){
							create=!existsTransition(mstep1,mchain2);
						}
						if (create){
							incrementTransitionCounter();
							transition=new StepTransition(app,thecase,Resource.getString("DEPENDENCY")+"("+getTransitionCounter()+")",(Model) this,mstep1,mchain2);
//							transition=new StepTransition(app,thecase,step1.getName(),(Model) this,mstep1,mchain2);
							addStepTransition(transition);
						}
					}
					if ((bstep1)&&(bstep2)&&(thecase.containsDependency(step1,step2))){
						if (mstep1.getName().equals(mstep2.getName())){
							//dies sind zwei gleiche Elemente
							continue;
						}
						if (check){
							create=!existsTransition(mstep1,mstep2);
						}
						if (create){
							incrementTransitionCounter();
							transition=new StepTransition(app,thecase,Resource.getString("DEPENDENCY")+"("+getTransitionCounter()+")",(Model) this,mstep1,mstep2);
//							transition=new StepTransition(app,thecase,step1.getName(),(Model) this,mstep1,mstep2);
							addStepTransition(transition);
						}
					}					
							
				}

			}
	}

    /**
     * Checks wether a transition between the specified <code>ModelFigureElement</code>s exists.
     * @return true if there exits a transition.
     */
	public boolean existsTransition(ModelFigureElement start, ModelFigureElement end){
		//pruefe die connection
		StepTransition trans=null;
		for(int c=0;c<this.getConnectionCount();c++){
			trans=(StepTransition) this.getConnection(c);
			if ((trans.getStartName().equals(start.getName()))&&(trans.getEndName().equals(end.getName()))){
				//es besteht eine Transition
				return true;
			}
		}
		return false;
	}


    /**
     * Adds a <code>MiningMartStep</code> to this <code>MiningMartChain</code>.
     * @ return null if creating the MiningMartStep is permitted
     */
	public MiningMartStep addMiningMartStep(ModelFigureElement parent, MiningMartStep step){
		if (! ( parent instanceof MiningMartChain)){
//			System.out.println("ModelFigureElement parent must be a MiningMartChain");
			return null;
		}

        for (int i = 0; i < getListenerCount(); i++) {
            getListener(i).onCreateChild(this, step);
        }
        
        if (step.canSetParent(this) && canAddChild(step)) {
            step.setParent(this);
        }
        else {
            step.destroy();
            step = null;
        }

        return step;
	}
	
    /**
     * Creates a new <code>MiningMartStep</code> with the specified parameters.
     * @param parent The created <code>MiningMartStep</code> is child of this <code>ModelFigureElement</code>.
     * @param opname the name of the operator of the step.
     * @param position the <code>MiningMartStep</code> is placed at this point.
     * @param bounds the bounds of the figure.
     * @return null if creating the MiningMartStep is permitted
     */
    public MiningMartStep createMiningMartStep(ModelFigureElement parent, String opname, Point position, Rectangle bounds) {
		String name;
		if (opname!=null)
			name=new String(opname);
		else
			name=Resource.getString("STEP");
				
        MiningMartStep result;
		Step step;
		try{
			name=this.chain.getTheCase().getValidName(name,Step.class);
			step=this.chain.getTheCase().createStep(name);
			this.chain.addStep(step);
			step.setTheCase(this.chain.getTheCase());		
			step.setTheOperator(m4Interface.findOperator(opname));
		}catch(M4Exception error){
			M4Interface.print.doPrint(Print.ERROR,error.getMessage(),error);
			JOptionPane.showMessageDialog(app,
				error.getMessage(),
				Resource.getString("DIALOG_ERROR_TITLE"),
				JOptionPane.ERROR_MESSAGE);
			return null;
		}


        result = new MiningMartStep(app,step,parent,position,bounds);
        result.setName(name);
        
        return addMiningMartStep(parent,result);

    }
   

	/**
	 * Creates a new <code>MiningMartStep</code> with the specified parameters.
	 * @param parent The created <code>MiningMartStep</code> is child of this <code>ModelFigureElement</code>.
	 * @param opname the name of the operator of the step.
	 * @param position the <code>MiningMartStep</code> is placed at this point.
	 * @param bounds the bounds of the figure.
	 * @return null if creating the MiningMartStep is permitted
	 */
	public MiningMartStep createMiningMartStep(ModelFigureElement parent, Step step, Point position, Rectangle bounds) {
		MiningMartStep result;
		result = new MiningMartStep(app,step,parent,position,bounds);
        
		return addMiningMartStep(parent,result);

	}

   
    /**
     * Adds a <code>MiningMartChain</code> to this <code>MiningMartChain</code>.
     * @ return null if creating the MiningMartChain is permitted
     */
   public MiningMartChain addMiningMartChain(ModelFigureElement parent,MiningMartChain chain){

		if (!( parent instanceof Model)){
			return null;
    	}

        for (int i = 0; i < getListenerCount(); i++) {
            getListener(i).onCreateChild(this, chain);
        }
        
        if (chain.canSetParent(this) && canAddChild(chain)) {
            chain.setParent(this);
        }
        else {
            chain.destroy();
            chain = null;
        }
        
        return chain;

   }
    
    /**
     * Creates a new <code>MiningMartChain</code>.
     * @param parent the MiningMartChain is child of this <code>ModelFigureElement</code>.
     * @param name the name of the <code>MiningMartChain</code>
     * @param point the <code>MiningMartChain</code> is placed at this point.
     * @param bounds the bounds of the figure.
     * @ return null if creating the MiningMartChain is permitted
     */
    public MiningMartChain createMiningMartChain(ModelFigureElement parent,String name, Point point, Rectangle bounds) {
        MiningMartChain result;
		Chain subchain;
		try{
			name=this.chain.getTheCase().getValidName(name,Chain.class);
			subchain=this.chain.createSubChain(name, new LinkedList());
		}catch(M4Exception error){
			M4Interface.print.doPrint(Print.ERROR,error.getMessage(),error);
			JOptionPane.showMessageDialog(app,
				error.getMessage(),
				Resource.getString("DIALOG_ERROR_TITLE"),
				JOptionPane.ERROR_MESSAGE);
			return null;
		}
        
        result = new MiningMartChain(app,thecase,subchain,casename,point,bounds,parent);
        result.initTheChain();

        return addMiningMartChain(parent,result);

    }
    
    /**
     * Creates a new <code>MiningMartChain</code>.
     * @param parent the MiningMartChain is child of this <code>ModelFigureElement</code>.
     * @param chain the created <code>MiningMartChain</code> wrapps this <code>Chain</code>.
     * @param point the <code>MiningMartChain</code> is placed at this point.
     * @param bounds the bounds of the <code>MiningMartChain</code>.
     * @ return null if creating the MiningMartChain is permitted
     */
    public MiningMartChain createMiningMartChain(ModelFigureElement parent,Chain chain, Point point, Rectangle bounds) {
        MiningMartChain result;

        result = new MiningMartChain(app,thecase,chain,casename,point,bounds,parent);
        result.initTheChain();

        return addMiningMartChain(parent,result);

    }
    
    /**
     * Adds the specified <code>StepTransition</code> to this <code>MiningMartChain</code>.
     */
    public StepTransition addStepTransition(StepTransition transition){

        for (int i = 0; i < getListenerCount(); i++) {
            getListener(i).onCreateConnection(this, transition);
        }

		return transition;
    }

    
    /**
     * Creates a new <code>StepTransition</code>.
     * @param the name of the transition
     * @param start the <code>StepTransition</code> starts at this <code>ModelFigureElement</code>.
     * @param end the <code>StepTransition</code> ends at this <code>ModelFigureElement</code>.
     * @param existsTransition if the transition exists then no new <code>TransitionFigure</code> is added.
     */
    public StepTransition createTransition(String name, ModelFigureElement start, ModelFigureElement end, boolean existsTransition) {
        if (name == null) { 
        	incrementTransitionCounter();
            name = Resource.getString("DEPENDENCY")+"("+getTransitionCounter()+")";
        }
 
        StepTransition result = new StepTransition(app,thecase,name,(Model) this,start,end);
        
	    boolean adding =result.addTransitionToCase(); 

		if ((adding)&&(!existsTransition)){
	        //Verbinden der Elemente im Konstruktor von StepTransition

	        result.setName(name);
    	    result.setStart(start);
        	result.setEnd(end);
                
	        return addStepTransition(result);
		}
		return null;
    }




	public boolean canSetParent(ModelFigureElement parent) {
        
        return true;
	}
	
	public Point getOrigin(){
		return point;
	}
	
	public String getType(){
		return Resource.getString("CHAIN");
	}
	
    /**
     *Gets the name of the <code>MiningMartChain</code>.
     *@return returns the name of the <code>Chain</Case>
     *@see edu.udo.cs.miningmart.m4.Chain#getName()
     */
	public String getName(){
		if (chain!=null)
			return this.chain.getName();
		return "";
	}
	
        /**
         *Sets the name of the <code>MiningMartChain</code>.
         *It means it sets the name of the <code>Chain</code>.
         *@see edu.udo.cs.miningmart.m4.Chain#setName(String)
         */
	public void setName(String name){
		if (chain!=null)
			this.chain.setName(name);
		super.setName(name);
	}

	/**
         * If the description of this <code>MiningMartChain</code> changed then set the documentation of the underlying <code>Chain</code>.
	 * @see org.musoft.limo.model.ModelElement#primitiveAttributeChanged(ModelPrimitiveAttribute)
	 */
	public void primitiveAttributeChanged(ModelPrimitiveAttribute mpa) {
		super.primitiveAttributeChanged(mpa);
		if ((mpa.getName().equals(Resource.getString("ATTR_NAME")))&&(chain!=null)){
			setName(mpa.getString());
		}
	}
	
        /**
         *Gets the <code>Case</code>.
         */
	public Case getTheCase(){
		return this.thecase;
	}


        /**
         *Gets the <code>Chain</code>.
         */
	public Chain getChain(){
		return this.chain;
	}
	
	M4Data getAttachedM4Object() {
		return this.getChain();
	}

	public boolean canDestroy() {
		if (app.getViewMode()==Application.VIEWER){
			return false;
		}
		return true;
	}


	/**
     *Destroys the <code>MiningMartChain</code> and deletes the underlying <code>Chain</code>.
	 * @see org.musoft.limo.model.ModelElement#destroy()
	 */
	public void destroy() {

		if (app.getViewMode()==Application.VIEWER){
			//Im viewer-mode darf nicht zerstoert werden
			return;
		}

		StepTransition trans;
		Model parentmodel=(Model) getParent();
        for (int i=parentmodel.getConnectionCount()-1;i>=0;i--){
        	trans=(StepTransition) parentmodel.getConnection(i);
        	if ((trans.getStart().getName().equals(getName()))||(trans.getEnd().getName().equals(getName()))){
	        	trans.destroy();
        	}
        }


		super.destroy();
        for (int i = 0; i < getListenerCount(); i++) {
            getListener(i).onDestroy(this);
        }

		if (this.canDeleteInDatabase()){
			try{
				String chainname=this.chain.getName();
				this.chain.deleteSoon();
				M4Interface.print.doPrint(Print.COMPILER_CASE_CONTROL,Resource.getString("DELETED_CHAIN")+" "+chainname);
			}catch(M4Exception error){
				M4Interface.print.doPrint(Print.ERROR,error.getMessage(),error);
			}
		}
	}
	
     /**
      * Dissolves the specified <code>MiningMartChain</code>.
      */
	public void dissolveMiningMartChain(MiningMartChain mchain) throws M4Exception{

		//I need all subelements
		Collection subelements=new LinkedList();
		for (int i=0;i<mchain.getChildCount();i++){
			ModelFigureElement child=mchain.getChild(i);
			if (child instanceof MiningMartChain){
				subelements.add(((MiningMartChain) child).getChain());
			}
			if (child instanceof MiningMartStep){
				subelements.add(((MiningMartStep) child).getStep());
			}
		}

		//now resolve and destroy the mchain
		app.getMiningMartCase().setDeleteInDatabase(false,true);

		this.chain.resolveSubChain(mchain.getChain());
		mchain.destroy();
		for (int i = 0; i < getListenerCount(); i++) {
			getListener(i).onRemoveChild(this,mchain);
		}

		app.getMiningMartCase().setDeleteInDatabase(true,true);

		//add the subelements
		Iterator iter=subelements.iterator();
		Object next;
		Chain subchain;
		Step substep;
		MiningMartChain submchain;
		MiningMartStep submstep;
		
		while(iter.hasNext()){
			next=iter.next();
			if (next instanceof Chain){
				subchain=(Chain) next;			

				submchain=this.createMiningMartChain(this,subchain,subchain.getPoint(),new Rectangle(35,35));	
				for (int i=0;i<getListenerCount();i++){
					this.getListener(i).onAddChild(submchain,this);			
				}
			}
			if (next instanceof Step){
				substep=(Step) next;

				submstep=this.createMiningMartStep(this,substep,substep.getPoint(),new Rectangle(35,35));				
				for (int i=0;i<getListenerCount();i++){
					this.getListener(i).onAddChild(submstep,this);			
				}
			}
		}
		
		this.organizeTransitions(true);
	}

}
/*
$Log: MiningMartChain.java,v $
Revision 1.4  2006/04/11 14:10:15  euler
Updated license text.

Revision 1.3  2006/04/06 16:31:14  euler
Prepended license remark.

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

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

*/
