package transform;
import model.Association;
import model.Diagram;
import model.Set;
import model.Field;
import model.Method;
import model.Link;

@:expose
class DiagramToUML {
	public static function transform(diagram : Diagram) {
		initNavigation(diagram);
		initConstructors(diagram);
		initAccessors(diagram);
	}

	static function initNavigation(diagram : Diagram) {
		for (assoc in diagram.associations) {
			if (assoc.isClass()) {
				for (link in assoc.links) {
					assoc.navFields.push(new Field(constructFieldName(link, "1"), link.set.className, link.visibility));
				}
			} else if (assoc.links.length == 2) {
				if (!assoc.links[0].hasArrow) {
					var c1 : Set = diagram.getObj(assoc.links[0].set.name);
					var field1Type : String = (assoc.links[0].max == "1" ? assoc.links[1].set.className : Field.arrayOf(assoc.links[1].set.className));
					c1.navFields.push(new Field(constructFieldName(assoc.links[1], assoc.links[0].max), field1Type, assoc.links[1].visibility));
				}
				if (!assoc.links[1].hasArrow) {
					var c2 : Set = diagram.getObj(assoc.links[1].set.name);
					var field2Type : String = (assoc.links[1].max == "1" ? assoc.links[0].set.className : Field.arrayOf(assoc.links[0].set.className));
					c2.navFields.push(new Field(constructFieldName(assoc.links[0], assoc.links[1].max), field2Type, assoc.links[0].visibility));
				}
			}
		}
	}

	public static function constructFieldName(link : Link, maxCard : String) : String {
		var fName : String = link.classRole;
		if (link.role == "") {
			fName = maxCard == "N" ? link.set.getPluralClassName() : link.set.className;
		}
		fName = fName.charAt(0).toLowerCase() + fName.substr(1);
		return fName;
	}

	static function initConstructors(diagram : Diagram) {
		for (set in diagram) {
			if (set.constructors.length == 0 && (!Std.isOfType(set, Association) || cast(set, Association).isClass())) {
				var constructor : Method = new Method(set.className, "", set.isAbstract ? Field.ACCESS_PROTECTED : Field.ACCESS_PUBLIC);
				for (field in retrieveConstructorFields(set)) {
					constructor.params.push(new Field(field.className, field.classType));
				}
				set.constructors.push(constructor);
			}
		}
	}

	static function retrieveConstructorFields(set : Set) : Array<Field> { //recursive
		var fields : Array<Field>;
		if (set.superset != null) {
			fields = retrieveConstructorFields(set.superset);
		} else {
			fields = new Array();
		}
		for (fieldSet in [set.fields, set.navFields]) {
			for (field in fieldSet) {
				if (!field.isArray()) {
					fields.push(field);
				}
			}
		}
		return fields;
	}

	static function initAccessors(diagram : Diagram) {
		for (set in diagram) {
			if (!Std.isOfType(set, Association) || cast(set, Association).isClass()) {
				Lambda.iter(set.fields, field -> setAccessors(set, field));
				Lambda.iter(set.navFields, field -> setAccessors(set, field));
			}
		}
	}

	static function setAccessors(set: Set, field : Field) {
		if (field.visibility == Field.ACCESS_UNSET) {
			field.visibility = Field.ACCESS_PROTECTED;
			var getters : Method = new Method("get" + field.className.charAt(0).toUpperCase() + field.className.substr(1),
				field.classType, Field.ACCESS_PUBLIC);
			getters.isStatic == field.isStatic;
			set.accessors.push(getters);
			if (field.classType.substr(0, 5) == "Array") {
				var itemType : String = field.classType.substr(6, field.classType.length - 7);
				var adders : Method = new Method("add" + itemType, set.className, Field.ACCESS_PUBLIC);
				adders.params.push(new Field("item", itemType));
				adders.isStatic == field.isStatic;
				set.accessors.push(adders);
				var removers : Method = new Method("remove" + itemType, set.className, Field.ACCESS_PUBLIC);
				removers.params.push(new Field("item", itemType));
				removers.isStatic == field.isStatic;
				set.accessors.push(removers);
			} else {
				var setters : Method = new Method("set" + field.className.charAt(0).toUpperCase() + field.className.substr(1),
					field.classType, Field.ACCESS_PUBLIC);
				setters.params.push(new Field(field.className, field.classType));
				setters.isStatic == field.isStatic;
				set.accessors.push(setters);
			}
		}
	}
}
