package de.brightbyte.rdf;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import de.brightbyte.util.BeanUtils;

public abstract class AbstractPlatform<V, R extends V, A, W> implements RdfPlatform<V, R, A, W> {

	protected Map<String, RdfNamespace<V, R>> namespaces = new HashMap<String, RdfNamespace<V, R>>();
	
	public AbstractPlatform() {
		/*
		try {
			aquireNamespace(RDF.class);
		} catch (RdfException e) {
			throw new Error("failed to aquire RDF namespace", e);
		}
		*/
	}
	
	public Collection<RdfNamespace<V, R>> getNamespaces() {
		return namespaces.values();
	}

	public <N extends RdfNamespace<V, R>> N aquireNamespace(Class<N> nsclass) throws RdfException {
		return loadNamespace(nsclass, true);
	}
	
	@SuppressWarnings("unchecked")
	public <N extends RdfNamespace<V, R>> N loadNamespace(Class<N> nsclass, boolean register) throws RdfException {
		try {
			Field f = nsclass.getField("namespace");
			String uri = (String)f.get(null);
	
			N n = (N)getNamespace(uri);
			if (n!=null) return n;
			
			n = createNamespace(nsclass);
			if (register) addNamespace(n);
			
			return n;
		} catch (NoSuchFieldException e) {
			throw new RdfException("failed to aquire namespace "+nsclass, e);
		} catch (IllegalAccessException e) {
			throw new RdfException("failed to aquire namespace "+nsclass, e);
		} 
	}
	
	protected <N extends RdfNamespace<V, R>> N createNamespace(Class<N> nsclass) throws RdfException {
		try {
			N n = null;
			Constructor<N> ctor = null;
			
			try {
				ctor = BeanUtils.findConstructor(nsclass, new Object[] { this });
				if (ctor != null) n = ctor.newInstance(new Object[] { this });
			}
			catch (NoSuchMethodException ex) {
				//noop
			}
			
			if (n==null) n = nsclass.newInstance();
			
			return n;
		} catch (IllegalAccessException e) {
			throw new RdfException("failed to aquire namespace "+nsclass, e);
		} catch (InstantiationException e) {
			throw new RdfException("failed to aquire namespace "+nsclass, e);
		} catch (InvocationTargetException e) {
			throw new RdfException("failed to aquire namespace "+nsclass, e);
		}
	}
	
	public static boolean isPlainName(String s) {
		int i = 0; 
		while (i < s.length()) {
			int cp = s.codePointAt(i);
			i += Character.charCount(cp);
			
			if (cp=='_') continue;
			if (cp>='a' && cp<='z') continue;
			if (cp>='A' && cp<='Z') continue;
			if (i>1 && cp>='0' && cp<='9') continue;
			if (!Character.isLetter(cp)) return false;
		}
		return true;
	}

	public String getBaseURI() {
		RdfNamespace<V, R> ns = getNamespace(""); 
		return ns.getNamespace();
	}

	public RdfNamespace<V, R> getNamespace(String namespace) {
		return namespaces.get(namespace);
	}

	public void addNamespace(RdfNamespace<V, R> vocab) {
		this.namespaces.put(vocab.getNamespace(), vocab);
	}

	public void setBaseURI(String prefix) throws RdfException {
		addNamespace(prefix, "");
	}
	
	public GenericNamespace<V, R> addNamespace(String namespace, String prefix) throws RdfException {
		GenericNamespace<V, R> ns = new GenericNamespace<V, R>(this, namespace, prefix);
		addNamespace(ns);
		return ns;
	}

	public void setLiteralProperty(A about, R property, String value, String lang) throws RdfException {
		setProperty(about, property, newLiteral(value, lang));
	}
	
	public void setLiteralProperty(A about, R property, String value, R datatype) throws RdfException {
		setProperty(about, property, newLiteral(value, datatype));
	}
	
	public void setReferenceProperty(A about, R property, String namespace, String name) throws RdfException {
		setProperty(about, property, newResource(namespace, name));
	}
	
	
}
