/**
 * File:    ViewResourceManager.java
 * Created: 26.09.2006
 *
 *
/*******************************************************************************
 * Copyright (c) 2005-2011
 * University of Augsburg, Germany <www.ds-lab.org>
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Wolf Fischer, Programming distributed Systems Lab, University of Augsburg 
 *    	- initial API and implementation
 *******************************************************************************/

package org.eclipse.jwt.we.misc.views;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.jwt.we.misc.logging.Logger;
import org.eclipse.jwt.we.misc.logging.internal.Level;
import org.eclipse.jwt.we.misc.views.internal.AnalyseEcore;


/**
 * This class manages the read-out of the ecore metamodel for Edit/Creation purposes of a
 * view.
 * 
 * @version $Id: ViewEcoreManager.java,v 1.4 2010-05-10 09:31:27 chsaad Exp $
 * @author Wolf Fischer, Programming distributed Systems Lab, University of Augsburg,
 *         Germany, www.ds-lab.org
 * @since 0.6.0
 */
public class ViewEcoreManager
{

	/**
	 * The Logger.
	 */
	private Logger logger = Logger.getLogger(ViewEcoreManager.class);

	/**
	 * Singleton instance.
	 */
	private static ViewEcoreManager myinstance = null;

	/**
	 * Standard file extension of view files.
	 */
	public static final String VIEWEXTENSION = "view"; //$NON-NLS-1$

	/**
	 * Saves the path to the ecore file
	 */
	private static String pathtoecorefile = ""; //$NON-NLS-1$

	/**
	 * Holds a list of all {@link EClass} items that have been found in the meta model.
	 */
	private List<ViewItemWrapper> items = new ArrayList<ViewItemWrapper>();

	/**
	 * Associates every {@link EClass} item with its name. This is important for a fast
	 * look up later on.
	 */
	private Hashtable<String, List<ViewItemWrapper>> attributes = new Hashtable<String, List<ViewItemWrapper>>();


	/**
	 * Constructor.
	 * 
	 */
	private ViewEcoreManager()
	{
	}


	/**
	 * Standard Singleton access
	 * 
	 * @return
	 */
	public static ViewEcoreManager getInstance()
	{
		if (myinstance == null)
			myinstance = new ViewEcoreManager();
		return myinstance;
	}


	/**
	 * Sets the name of the currently loaded view.
	 * 
	 * @param name
	 */
	public void setName(String name)
	{
		Views.getInstance().getSelectedView().setInternalID(name);
	}


	/**
	 * Returns the name of the currently loaded view.
	 * 
	 * @return
	 */
	public String getName()
	{
		return Views.getInstance().getSelectedView().getInternalName();
	}


	/**
	 * Sets the path to an ecore file. Necessary for creating the views informations
	 * 
	 * @param uri
	 */
	public void setPath(String uri)
	{
		pathtoecorefile = uri;
	}


	/**
	 * Sets the "Display" Value of a ViewItemWrapper.
	 * 
	 * @param viw
	 * @param value
	 */
	public void setDisplay(ViewItemWrapper viw, boolean value)
	{
		Views.getInstance().setDisplay(viw, value);
	}


	/**
	 * Makes a complete reset of the internal data structures by loading the ecore file,
	 * retrieving the package list from the {@link AnalyseEcore} and analysing every
	 * single package object.
	 * 
	 */
	public void reset()
	{
		Views.getInstance().reset();
		items.clear();
		attributes.clear();
		try
		{
			AnalyseEcore ec = new AnalyseEcore(pathtoecorefile);
			List packages = ec.getPackagesList();
			for (int i = 0; i < packages.size(); i++)
			{
				analysePackage((EObject) packages.get(i), null);
			}
			Collections.sort(items);
		}
		catch (Exception ex)
		{
			logger.log(Level.SEVERE, ex.toString());
		}
	}


	/**
	 * Analyses a given package and adds its items to the lists. This methods makes use of
	 * the fact, that till today EMF generates an ordered Literals interface (so first
	 * comes the EClass and then its attributes and references)
	 * 
	 * @param obj
	 *            The EObject to parse.
	 * @param viw
	 *            On first call, this is null. Later on there will be the instance of its
	 *            parent
	 */
	private void analysePackage(EObject obj, ViewItemWrapper viw)
	{
		EList contents = obj.eContents();
		ViewItemWrapper lastview = null;
		if ((obj instanceof EPackage) || (obj instanceof EClass))
		{
			/**
			 * Walks through the contents of this eobject and either (if the child is a
			 * package again) recursively calls analysePackage or, if this is an EClass,
			 * adds it to the items list.
			 */
			for (int i = 0; i < contents.size(); i++)
			{
				EObject newobj = null;
				try
				{
					newobj = (EObject) contents.get(i);
				}
				catch (Exception ex)
				{
				}
				if (newobj instanceof EPackage)
				{
					analysePackage(newobj, viw);
				}
				else if (newobj instanceof EClass)
				{
					lastview = new ViewItemWrapper(newobj, true, true);
					Views.getInstance().addViewItemWrapper(lastview, null);
					items.add(lastview);
					EList elementcontents = newobj.eContents();
					for (int i2 = 0; i2 < elementcontents.size(); i2++)
					{
						analysePackage((EObject) elementcontents.get(i2), lastview);
					}
				}
			}
		}
		else if (obj instanceof EAttribute)
		{
			makeLookup(viw, obj);
		}
		else if (obj instanceof EReference)
		{
			makeLookup(viw, obj);
		}
	}


	/**
	 * Makes a lookup in the lokal attributes hashtable and adds a new element
	 * 
	 * @param parent
	 *            The parent element of the attribute. The EObject will be added as a
	 *            child to its parent.
	 * @param child
	 *            The child to be added
	 */
	private void makeLookup(ViewItemWrapper parentview, EObject obj)
	{
		Object val = attributes.get(parentview.toString());
		if (val == null)
		{
			val = new ArrayList<ViewItemWrapper>();
			attributes.put(parentview.toString(), (ArrayList<ViewItemWrapper>) val);
		}
		ViewItemWrapper viw = new ViewItemWrapper(obj, true, true);
		((ArrayList<ViewItemWrapper>) val).add(viw);
		Views.getInstance().addViewItemWrapper(viw, parentview);
	}


	/**
	 * Returns the list with items
	 * 
	 * @return
	 */
	public List<ViewItemWrapper> getItemsList()
	{
		return items;
	}


	/**
	 * Returns a list of attributes corresponding to a given object.
	 * 
	 * @param obj
	 * @return
	 */
	public List<ViewItemWrapper> getAttributesList(ViewItemWrapper obj)
	{
		Object val = attributes.get(obj.toString());
		if (val == null)
			return null;
		return (List<ViewItemWrapper>) val;
	}


	/**
	 * Saves a view to a file.
	 * 
	 * @param f
	 */
	public void savetoFile(File f)
	{
		Views.getInstance().saveCurrentView(f);
	}


	/**
	 * Lets the Views.java load a view file and then converts the data to its own readable
	 * structure.
	 * 
	 * @param f
	 */
	public void openFile(File f)
	{
		reset();
		Views.getInstance().loadViewFromFile(f);
		Hashtable<String, ViewItemWrapper> currentview = Views.getInstance()
				.getSelectedView().getViewData();
		adjustList(items, currentview);
		Enumeration<List<ViewItemWrapper>> elements = attributes.elements();
		while (elements.hasMoreElements())
		{
			adjustList(elements.nextElement(), currentview);
		}
	}


	/**
	 * Replaces elements of the initial list with loaded elements.
	 * 
	 * @param elements
	 * @param elementlist
	 */
	private void adjustList(List<ViewItemWrapper> elements,
			Hashtable<String, ViewItemWrapper> elementlist)
	{
		for (int i = 0; i < elements.size(); i++)
		{
			ViewItemWrapper viw = elements.get(i);
			Object obj = elementlist.get(viw.toString());
			if (obj != null)
			{
				elements.set(i, (ViewItemWrapper) obj);
			}
			else
			{
				elementlist.put(viw.toString(), viw);
			}
		}
	}

}
