/*
 * This class is autogenerated if it does not exists. It may be modified.
 * Copyright: (c) Reinders Posters
 * Modified:  $Date: 2007/08/08 15:10:25 $
 */

package nl.reinders.bm;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.GregorianCalendar;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.Table;

import org.tbee.util.jpa.EntityManagerChangeCount;

/**
 * A JPA entity class for standversion.
 * @version    $Revision: 1.20 $
 * @author     $Author: toeukpap $
 */
@Entity
@Table(name="standversion")
public class Standversion extends nl.reinders.bm.generated.Standversion
implements java.io.Serializable
{
	static final long serialVersionUID = 0;
	static final String SOURCECODE_VERSION = "$Revision: 1.20 $";
	static org.apache.log4j.Logger log4j = org.apache.log4j.Logger.getLogger(Standversion.class.getName());
	
	// =========================================================================================
	// CONSTRUCTORS

	/**
	 * 
	 */
	public Standversion()
	{
		construct();
	}
	
	/**
	 * Create a new related to the specified stand
	 * @param stand
	 */
	public Standversion(Stand stand)
	{
		construct();
		setStand(stand);
	}
	
	/**
	 * 
	 *
	 */
	private void construct()
	{
		// defaults
		GregorianCalendar lNow = new GregorianCalendar();
		setReplaceDate( new GregorianCalendar( lNow.get(GregorianCalendar.YEAR) + 1, lNow.get(GregorianCalendar.MONTH), lNow.get(GregorianCalendar.DATE)));
		setPendingChanges(false);
	}

	
	/**
	 * Check for duplicates
	 */
	public void setVersion(Integer value)
	{
		// check for code duplicates
		if (value != null && getStand() != null)
		{
			for (Standversion lOtherStandversion : getStand().getStandversionsWhereIAmStand())
			{
				if ( !lOtherStandversion.equals(this) && value.equals(lOtherStandversion.getVersion()) )
				{
					throw new IllegalArgumentException("Versie " + value + " komt reeds voor");
				}
			}
		}
		
		// accept value
		super.setVersion(value);
	}

	/** 
	 * Check for duplicate version
	 */
	public void setStand(nl.reinders.bm.Stand value)
	{
		// check for version duplicates
		if (value != null && getVersion() != null)
		{
			for (Standversion lOtherStandversion : value.getStandversionsWhereIAmStand())
			{
				if ( !lOtherStandversion.equals(this) && getVersion().equals(lOtherStandversion.getVersion()) )
				{
					throw new IllegalArgumentException("Versie " + getVersion() + " komt reeds voor in standaard " + value);
				}
			}
		}

		// allow setup
		super.setStand(value);
	}
	
	
	// =========================================================================================
	// PROPERTIES

	// Inifill
	public void setInifill(Boolean value) { super.setInifill( value.booleanValue() ? 1 : 0); }
	public Boolean hasInifill() { return (super.getInifill() != 0); }
	final static public Class INIFILL_PROPERTY_CLASS = Boolean.class;

	
	/**
	 * Find the standassign with the specified code 
	 * @param code
	 * @return
	 */
	public Standassign getStandassign(int code)
	{
		for (Standassign lStandassign : getStandassignsWhereIAmStandversion())
		{
			if ( lStandassign.getCode().intValue() == code ) return lStandassign;
		}
		return null;
	}
	
	/** determine the highest code in all the standassigns of this standversion or 0 */
	public int getHighestCodeOfAllMyStandassigns()
	{
    	int lHighestCode = 0;
		for (Standassign lStandassign : getStandassignsWhereIAmStandversion())
		{
			if (lStandassign.getCode() != null && lStandassign.getCode() > lHighestCode) lHighestCode = lStandassign.getCode(); 
		}
		return lHighestCode;
	}

	// =========================================================================================
	// DAO

	/**
	 * @param standversionnr
	*/
	static public nl.reinders.bm.Standversion findByStandidVersion(String standid, Integer version)
	{
		EntityManager lEntityManager = org.tbee.util.jpa.EntityManagerFinder.findEntityManager();
		Query lQuery = lEntityManager.createQuery("select t from Standversion t where t.iVersion=:version and t.iStand.iStandid=:standid");
		lQuery.setParameter("version", version);
		lQuery.setParameter("standid", standid);
		java.util.List<nl.reinders.bm.Standversion> lList = lQuery.getResultList();
	    if (lList.size() == 0) return null;
	    return lList.get(0);
	}
	
	/**
	 * find all entities ordered by
	 */
	static public java.util.List<nl.reinders.bm.Standversion> findAllOrderedByStandIdAndVersion()
	{
		EntityManager lEntityManager = org.tbee.util.jpa.EntityManagerFinder.findEntityManager();
		if (log4j.isDebugEnabled()) log4j.debug("Using EM #" + Integer.toHexString( lEntityManager.hashCode() ) );
		Query lQuery = lEntityManager.createQuery("select t from Standversion t join fetch t.iStand order by t.iStand.iStandid, t.iVersion");
		java.util.List<nl.reinders.bm.Standversion> lList = lQuery.getResultList();
	    return lList;
	}
	
	/**
	 * @param standversionnr
	*/
	public nl.reinders.bm.Standversion findPreviousVersion()
	{
		// precond
		if (getVersion() == null || getStand() == null) return null;
		
		// db
		EntityManager lEntityManager = org.tbee.util.jpa.EntityManagerFinder.findEntityManager();
		
		// determine previous version
		Query lVersionQuery = lEntityManager.createQuery("SELECT MAX(o.iVersion) FROM Standversion o WHERE o.iVersion < :version");
		lVersionQuery.setParameter("version", getVersion());
		Integer lPreviousVersion = (Integer)lVersionQuery.getSingleResult();
		if (lPreviousVersion == null) return null;
		
		// find it
		return findByStandidVersion(getStand().getStandid(), lPreviousVersion);
	}
	
	// =========================================================================================
	// Display

	public String createStringForDisplay()
	{
    	StringBuilder lStringBuilder = new StringBuilder();
    	if (getStand() != null) lStringBuilder.append(getStand().getStandid());
    	lStringBuilder.append("[");
    	lStringBuilder.append(getVersion());
    	lStringBuilder.append("]");    	
    	return lStringBuilder.toString();
		
	}
	
	// =========================================================================================
	// Import and merge
	
    /**
     * Import
     * 
     * @param importFromStandversion
     */
    public void importStandVersion(final Standversion importFromStandversion, ProgressListener progressListener)
    {
    	// get the assigns to copy; we use a query so there are less selects required
		EntityManager lEntityManager = org.tbee.util.jpa.EntityManagerFinder.findEntityManager();
		if (log4j.isDebugEnabled()) log4j.debug("Using EM #" + Integer.toHexString( lEntityManager.hashCode() ) );
		Query lQuery = lEntityManager.createQuery("select t from Standassign t join fetch t.iArticle join fetch t.iBatchtype where t.iStandversion = :standversion");
		lQuery.setParameter("standversion", importFromStandversion);
		java.util.List<Standassign> lList = lQuery.getResultList();
    	
        // copy the assigns
    	int lProgressMin = 0;
    	int lProgressCnt = 0;
    	int lProgressMax = lList.size() - 1;
     	if (progressListener != null) progressListener.init(lProgressMin, lProgressMax);
        for (Standassign lImportFromStandassign : new ArrayList<Standassign>( lList ) ) // The "new ArrayList" forces the whole collection to be read, not lazy loaded. Otherwise this will result in a concurrent modification exception.
        {
     		// progress
         	lProgressCnt++;
         	if (progressListener != null) progressListener.progress(lProgressCnt);
         	
        	// clone
        	Standassign lNewStandassign = lImportFromStandassign.cloneShallow();
        	lNewStandassign.setChangeIndicator(false); // there is no change between this version and the imported one
        	
        	// but this assign belong to new standversion
        	lNewStandassign.setStandversion(this);
        }
    	if (progressListener != null) progressListener.done();        
    }
    /** without the ProgressListener */
    public void importStandVersion(final Standversion importFromStandversion) { importStandVersion(importFromStandversion, null); }

    /**
     * Suppose there is a A8 that is derrived from A5, with the following Standversions and Standassigns, and A8v29 is being created:
     * 		A5v66	A5v67		A8v28(A5v66)	--> A8v29
     * 	1:	15312 > 2460		15343			--> 15343
     * 	2:	14949 >	3328		14949			--> 3328
     * 	3:	15108 >	15108		15108			--> 15108
     * 
     * A8v28 is derrived from A5v66. 
     * A5v66 has 15312 on code 1, in v67 this is changed to 2460. However A8v28 has 15343 on code 1. This is different from A5v66, so the change is NOT copied to A5v29.  
     * A5v66 has 14949 on code 1, in v67 this is changed to 33280. However A8v28 also has 14949 on code 1, so the change is copied to A5v29.
     * Basically what happens is: if in the A8v28 "imported from" the articlenr is identical to A5v66 "its derrived from", then a change is copied from A5v67 "derrive from" to A8v29 "newly created".    
     *   
     * In order to make the algorithm more readable, the example Standversions from above have been added to the variable names.
     * 
     * @param importedFromStandversion
     * @param derriveFromStandVersion
     * @param createdStandVersion
     * @return list of changed codes in a string
     * @throws IllegalStateException upon an error
     */
    public String mergeStandVersion(Standversion importedFromStandversion_A8v28, Standversion derriveFromStandVersion_A5v67, ProgressListener progressListener) // this is createdStandVersion_A8v29
    {
    	// init
    	String lMerged = "";
    	Standversion lImportedFromDerrivedFrom_A5v66 = derriveFromStandVersion_A5v67.findPreviousVersion();
    	if (lImportedFromDerrivedFrom_A5v66 == null)
    	{
    		String lError = "Standversie " + derriveFromStandVersion_A5v67.createStringForDisplay() + " heeft geen vorige versie.";
    		throw new IllegalStateException(lError);
    	}
    	
    	// we can only merge from two standversion of the same stand
    	if (!lImportedFromDerrivedFrom_A5v66.getStand().equals( derriveFromStandVersion_A5v67.getStand() ))
    	{
    		String lError = "Merge van standversie (" + derriveFromStandVersion_A5v67.createStringForDisplay() + ") is niet van dezelfde standaard als zijn vorige versie (" + lImportedFromDerrivedFrom_A5v66.createStringForDisplay() + ").";
    		throw new IllegalStateException(lError);
    	}
    	
    	// we can only merge outside our own stand
    	if ( getStand().equals( derriveFromStandVersion_A5v67.getStand() ))
    	{
    		String lError = "Merge standversie(" + derriveFromStandVersion_A5v67.createStringForDisplay() + ") is van dezelfde standaard als de nieuwe (" + createStringForDisplay() + ").";
    		throw new IllegalStateException(lError);
    	}
    	
        // scan the assignments
    	int lProgressMin = 0;
    	int lProgressCnt = 0;
    	int lProgressMax = this.getStandassignsWhereIAmStandversion().size() - 1;
     	if (progressListener != null) progressListener.init(lProgressMin, lProgressMax);
    	for (Standassign lNewlyCreatedStandassign : this.getStandassignsWhereIAmStandversion()) 
    	{
    		// progress
        	lProgressCnt++;
        	if (progressListener != null) progressListener.progress(lProgressCnt);
        	
        	// this is the code we're processing
        	int lCode = lNewlyCreatedStandassign.getCode();

        	// get the stand assign in the newly created Standversion
        	if (derriveFromStandVersion_A5v67.getStandassign(lCode) == null) // not found
        	{
        		String lError = "Code " + lCode + " komt niet voor in de te mergen standaard.";
        		throw new IllegalStateException(lError);
        	}

        	// get the stand assign in the newly created Standversion
        	if (lImportedFromDerrivedFrom_A5v66.getStandassign(lCode) == null) // not found
        	{
        		String lError = "Code " + lCode + " komt niet voor in de eerder gemergde standaard en dus kan niet gecontroleerd worden of er een wijziging is geweest.";
        		throw new IllegalStateException(lError);
        	}

        	// If the "imported from" article is identical to its "derrived from", then a change may be copied.
        	// If not, then not, so skip this code.
        	if (importedFromStandversion_A8v28.getStandassign(lCode).getArticle().getArticlenr() != lImportedFromDerrivedFrom_A5v66.getStandassign(lCode).getArticle().getArticlenr()) continue;
        		
        	// The old articles are the same, so a change may be copied. 
        	// If there is no change, then this code can be skipped.
        	if (derriveFromStandVersion_A5v67.getStandassign(lCode).getArticle().getArticlenr() == lImportedFromDerrivedFrom_A5v66.getStandassign(lCode).getArticle().getArticlenr()) continue;

        	// The old article is the same, but there was a change: copy it!
        	try
        	{
	        	lNewlyCreatedStandassign.setArticle( derriveFromStandVersion_A5v67.getStandassign(lCode).getArticle() );
	        	lNewlyCreatedStandassign.setChangeIndicator(true);
	            lMerged += lNewlyCreatedStandassign.getCode() + ": " + importedFromStandversion_A8v28.getStandassign(lCode).getArticle().getArticlenr() + " -> " + lNewlyCreatedStandassign.getArticle().getArticlenr() + "\n";
        	}
        	catch (IllegalArgumentException e)
        	{
        		lMerged += e.getLocalizedMessage() + "\n";
        	}
    	}
    	if (progressListener != null) progressListener.done();
    	
        // remember where we were derrived from
    	this.setDerrivedfromstandversion( derriveFromStandVersion_A5v67 );
        
	    // done
	    return lMerged;
    }
    /** without the ProgressListener */
    public String mergeStandVersion(Standversion importedFromStandversion_A8v28, Standversion derriveFromStandVersion_A5v67) { return mergeStandVersion(importedFromStandversion_A8v28, derriveFromStandVersion_A5v67, null); }


	// =========================================================================================
	// Storage
	
    /**
     * Create storages for this standversion (if not exists) and assign the batchtype and articles
     * 
     */
    public void createStorage(ProgressListener progressListener)
    {
    	// scan through all our standassigns
		java.util.List<Standassign> lList = getStandassignsWhereIAmStandversion();
		
// at this time the toplink library does not support cascade update and insert, so we need to hand code that		
if (EntityManagerChangeCount.hasPendingChanges()) throw new IllegalStateException("Entity manager has pending changes");		

		// copy the assigns
    	int lProgressMin = 0;
    	int lProgressCnt = 0;
    	int lProgressMax = lList.size() - 1;
     	if (progressListener != null) progressListener.init(lProgressMin, lProgressMax);
        for (Standassign lStandassign : lList)
        {
     		// progress
         	lProgressCnt++;
         	if (progressListener != null) progressListener.progress(lProgressCnt);
         	
         	// create storage
         	lStandassign.createStorage();
        }
    	if (progressListener != null) progressListener.done();
    }
    /** without the ProgressListener */
    public void createStorage() { createStorage(null); }

	// =========================================================================================
	// Comparator
	
	final static public Comparator<Standversion> getVersionComparator() { return cVersionComparator; }
	final static private Comparator<Standversion> cVersionComparator = new Comparator<Standversion>()
	{
		public int compare(Standversion o1, Standversion o2)
		{
			return o1.getVersion().compareTo(o2.getVersion());
		}
	};
}
