package de.dhbwstuttgart.parser.scope;

import java.util.ArrayList;
import java.util.List;

/**
 * Stellt den Namen einer Java Klasse dar.
 * Dieser kann auch den Packagenamen mit beinhalten:
 * de.dhbwstuttgart.typeinference.Menge
 * 
 * @author Andreas Stadelmeier
 */
public class JavaClassName {

	// FIXME It's very much possible to have imports to inner classes
	// In that case a.package.Foo.Bar, a.package is the Package and Foo.Bar the class name
	// Its impossible to decide what's the package based solely on the name of the class

	public static final JavaClassName Void = new JavaClassName("void");
	private String name;
	private PackageName packageName;

	public JavaClassName(String name) {
		if (name == null)
			throw new NullPointerException();

		String[] names = name.split("[.]");
		boolean match = true;
		if (names.length == 1) {
			// packageName = new PackageName();
			this.name = name;
		} else {
			name = names[names.length - 1];
			List<String> packageNames = new ArrayList<String>();
			for (int i = 0; i < names.length - 1; i++) {
				packageNames.add(names[i]);
			}
			packageName = new PackageName(packageNames);
			this.name = names[names.length - 1];
		}
	}

	/**
	 * Gibt von einem Klassennamen nur den Namen der Klasse zurück
	 * Beispiel:
	 * java.lang.Object wird zu: Object
	 */
	public static String stripClassName(String className) {
		return new JavaClassName(className).name;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		/*
		 * result = prime * result
		 * + ((packageName == null) ? 0 : packageName.hashCode()); //PackageName does
		 * not infect hashCode
		 */
		return result;
	}

	/**
	 * Namen sind nur gleich, wenn bei den beiden zu vergleichenden JavaClassNames
	 * auch das Package angegeben ist
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (obj instanceof String)
			return this.toString().equals(obj) || (this.name != null && this.name.equals(obj)); // Auch mit Strings als
																																													// Klassennamen kompatibel
																																													// TODO: sollte bald obsolet
																																													// sein
		if (getClass() != obj.getClass())
			return false;
		JavaClassName other = (JavaClassName) obj;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (packageName != null && other.packageName != null) {
			if (!packageName.equals(other.packageName))
				return false;// Spezialfall, nicht beide Typen mÃ¼ssen eindeutig mit Packagenamen angegeben
											// werden
		}
		return true;
	}

	@Override
	public String toString() {
		return (packageName != null ? packageName.toString() + "." : "") + name;
	}

	public String getPackageName() {
		return (packageName != null ? packageName.toString() : "");
	}

	public String getClassName() {
		return name;
	}
}

class PackageName {

	List<String> names = new ArrayList<>();

	public PackageName(List<String> packageNames) {
		names = packageNames;
	}

	public PackageName() {
		// Do nothing
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((names == null) ? 0 : names.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		PackageName other = (PackageName) obj;
		if (names == null) {
			if (other.names != null)
				return false;
		} else if (!names.equals(other.names))
			return false;
		return true;
	}

	@Override
	public String toString() {
		String ret = "";
		if (names == null)
			return "";
		for (String n : names)
			ret += n + ".";
		if (ret != null && ret.length() > 0 && ret.charAt(ret.length() - 1) == '.') {
			ret = ret.substring(0, ret.length() - 1);
		}
		return ret;
	}
}
