package dowser.model;

import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Vector;

/**
 *
 * @author Chetan Pawgi
 */
public class DomainModel {

	private Map<String, Class> classes;
	private List<Relation> relations;
	private List<String> classes_list;

	{
		classes = new Hashtable<String, Class>();
		relations = new Vector<Relation>();
		classes_list = new Vector<String>();
	}

	public void addClass(Class cClass) {
		addClass(cClass, classes_list.size());
	}

	public void removeClass(String className) {
		if (!classes.containsKey(className)) {
			throw new IllegalArgumentException("Class " + className + " doesn't exist in the model !");
		}
		Class cClass = classes.get(className);
		classes.remove(className);
		classes_list.remove(className);
		for (Relation r : cClass.getRelations()) {
			relations.remove(r);
		}
	}

	public void addClass(Class cClass, int index) {
		String cName = cClass.getName();
		if (cName.equals("")) {
			throw new IllegalArgumentException("Class name can't be null for " + cClass);
		}
		if (index > classes_list.size()) {
			throw new IllegalArgumentException("Invalid index specified for class insertion");
		}
		if (!classes.containsKey(cName)) {
			classes.put(cName, cClass);
			classes_list.add(index, cName);
		}
	}

	public Class getClass(String className) {
		return classes.get(className);
	}

	public int getIndexOfClass(Class c) {
		return classes_list.indexOf(c);
	}

	public Class getClass(int index) {
		return classes.get(classes_list.get(index));
	}

	public Collection<Class> getClasses() {
		return Collections.unmodifiableCollection(classes.values());
	}

	public int getClassCount() {
		return classes.size();
	}

	public boolean containsClass(String className) {
		return classes.containsKey(className);
	}

	public void addRelation(Relation cRelation) {
		String cRelationName = cRelation.getName();
		if (cRelationName.equals("")) {
			throw new IllegalArgumentException("Relation name can't be null for " + cRelation);
		}
		relations.add(cRelation);
	}

	public Relation getRelation(int index) {
		return relations.get(index);
	}

	public int getIndexOfRelation(Relation r) {
		return relations.indexOf(r);
	}

	public Collection<Relation> getRelations() {
		return Collections.unmodifiableCollection(relations);
	}

	public int getRelationCount() {
		return relations.size();
	}

	@Override
	public String toString() {
		StringBuilder bld = new StringBuilder();
		bld.append("Classes : \n");
		for (Class cClass : classes.values()) {
			bld.append(cClass.toString() + "\n");
		}
		bld.append("Relations : \n");
		for (Relation relation : relations) {
			bld.append(relation.toString() + "\n");
		}
		return bld.toString();
	}

	public void renameEntity(DomainEntity entity, String newName) {
		if (entity instanceof Class) {
			if (classes_list.contains(newName)) {
				throw new IllegalArgumentException("Class " + entity.getName() + " can't be renamed to " + newName + ".\n A similar named class already exists !");
			}
			String oldName = entity.getName();
			int prevInd = classes_list.indexOf(oldName);
			classes_list.remove(oldName);
			classes.remove(oldName);
			entity.setName(newName);
			addClass((Class) entity, prevInd);
			return;
		}
		if (entity instanceof Attribute) {
			Attribute attrib = (Attribute) entity;
			Class cClass = attrib.getParentClass();
			cClass.renameAttribute(attrib, newName);
			return;
		}
		if (entity instanceof Operation) {
			Operation oprn = (Operation) entity;
			Class cClass = oprn.getParentClass();
			cClass.renameOperation(oprn, newName);
			return;
		}
		if (entity instanceof Relation) {
			entity.setName(newName);
			return;
		}
	}

	public boolean containsRelation(Relation relation) {
		return relations.contains(relation);
	}

	public static DomainModel getMergedModel(DomainModel m1, DomainModel m2) {
		DomainModel result = new DomainModel();
		for (Class c : m1.getClasses()) {
			result.addClass(c);
		}
		for (Class c : m2.getClasses()) {
			if (!result.containsClass(c.getName())) {
				result.addClass(c);
			} else {
				Class prev = result.getClass(c.getName());
				for (Attribute a : c.getAttributes()) {
					prev.addAttribute(a.getName());
				}
				for (Operation o : c.getOperations()) {
					prev.addOperation(o.getName());
				}
				for (Class sub : c.getSubClasses()) {
					prev.addSubClass(sub);
				}
				for (Class sup : c.getSuperClasses()) {
					prev.addSuperClass(sup);
				}
			}
		}
		for (Relation r : m1.getRelations()) {
			result.addRelation(r);
		}
		for (Relation r : m2.getRelations()) {
			if (!result.containsRelation(r)) {
				result.addRelation(r);
			}
		}
		return result;
	}
}
