package com.console.dao;

import java.util.List;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import com.console.jpa.AttrObjectType;
import com.console.jpa.AttrObjectTypePK;
import com.console.jpa.Attribute;
import com.console.jpa.Obj;
import com.console.jpa.ObjectType;
import com.console.jpa.Parameter;
import com.console.jpa.ParameterPK;

/**
 * Session Bean implementation class Facade
 */
@Stateless
@SuppressWarnings("unchecked")
public class Facade implements FacadeLocal {

	/**
	 * Default constructor.
	 */
	public Facade() {
	}

	@PersistenceContext(unitName = "ModelJPA")
	EntityManager em;

	@Override
	public Object save(Object obj) {
		if (obj == null)
			throw new NullPointerException("Persistance object can't be null");
		if (!(obj instanceof Attribute || obj instanceof ObjectType 
				|| obj instanceof AttrObjectType || obj instanceof Parameter || obj instanceof Obj))
			throw new IllegalArgumentException(
					"Persitance object can be instance only of folowing "
							+ "classes: Attribute, ObjectType, AttrObjectType, Parameter or Obj");
		em.persist(obj);
		return obj;
	}

	@Override
	public Object merge(Object obj) {
		if (obj == null)
			throw new NullPointerException("Persistance object can't be null");
		if (!(obj instanceof Attribute || obj instanceof ObjectType 
				|| obj instanceof AttrObjectType || obj instanceof Parameter || obj instanceof Obj))
			throw new IllegalArgumentException(
					"Persitance object can be instance only of folowing "
							+ "classes: Attribute, ObjectType, AttrObjectType, Parameter or Obj");
		em.merge(obj);
		return obj;
	}

	@Override
	public ObjectType getObjectType(long id) {
		return em.find(ObjectType.class, id);
	}
	
	@Override
	public Obj getObj(long id) {
		return em.find(Obj.class, id);
	}

	@Override
	public void remove(Object obj) {
		if (obj == null)
			throw new NullPointerException("Persistance object can't be null");
		if (!(obj instanceof Attribute || obj instanceof ObjectType 
				|| obj instanceof AttrObjectType || obj instanceof Parameter || obj instanceof Obj))
			throw new IllegalArgumentException(
					"Persitance object can be instance only of folowing "
							+ "classes: Attribute, ObjectType, AttrObjectType");
		em.remove(em.merge(obj));
	}

	@Override
	public AttrObjectType getAttrObjectType(AttrObjectTypePK pk) {
		return em.find(AttrObjectType.class, pk);
	}
	
	@Override
	public Parameter getParameter(ParameterPK pk) {
		return em.find(Parameter.class, pk);
	}

	@Override
	public List<ObjectType> getObjectTypes(Long parentId) {
		if (parentId == null)
			return em.createNativeQuery(
					"SELECT * FROM object_types WHERE parent_id is null",
					ObjectType.class).getResultList();
		List<ObjectType> children = em
				.createNativeQuery(
						"SELECT * FROM object_types WHERE parent_id = ?",
						ObjectType.class).setParameter(1, parentId)
				.getResultList();
		return children;
	}
	
	@Override
	public List<Obj> getObjects(Long parentId) {
		if (parentId == null)
			return em.createNativeQuery(
					"SELECT * FROM objects WHERE parent_id is null",
					Obj.class).getResultList();
		List<Obj> children = em
				.createNativeQuery(
						"SELECT * FROM objects WHERE parent_id = ?",
						Obj.class).setParameter(1, parentId)
				.getResultList();
		return children;
	}

	@Override
	public List<Attribute> getOwnAtributes(long objectTypeId) {
		return em
				.createNativeQuery(
						"select attr.* from attributes attr, attr_object_types aot "
								+ "where aot.object_type_id = ? and aot.attr_id = attr.attr_id",
						Attribute.class)
				.setParameter(1, Long.valueOf(objectTypeId)).getResultList();
	}
	
	@Override
	public List<Attribute> getInheritedAttributes(long objectTypeId) {
		return em
				.createNativeQuery(
						"select distinct a.* from "
								+ "    attributes a, "
								+ "    attr_object_types aot "
								+ "where "
								+ "    a.attr_id = aot.attr_id "
								+ "    and a.show_order != 0 "
								+ "    and aot.object_type_id in ( "
								+ "        select object_type_id from object_types "
								+ "        start with object_type_id = ("
								+ "            select parent_id from object_types where object_type_id = ?) "
								+ "        connect by object_type_id = prior  parent_id ) ",
						Attribute.class).setParameter(1, objectTypeId)
				.getResultList();
	}

	public List<ObjectType> searchObjectType(String name, String searchType){
		String toSearch = name;
		if ("start".equals(searchType)){
			toSearch = toSearch + "%";
		} else if ("end".equals(searchType)){
			toSearch = "%" + toSearch;
		}	else if ("conteins".equals(searchType)){
			toSearch = "%" + toSearch+"%";
		}
		return em.createNativeQuery(
						"select * from object_types where name like ?", ObjectType.class).setParameter(1, toSearch).getResultList();
	}

	@Override
	public List<ObjectType> getObjectTypeHierarchy(long current) {
		return em
				.createNativeQuery(
						"select * from object_types "
								+ "start with object_type_id = ? "
								+ "connect by object_type_id = prior  parent_id "
								+ "order by  level desc ", ObjectType.class)
				.setParameter(1, current).getResultList();
	}
	
	@Override
	public List<ObjectType> getAllObjectTypes() {
		return em.createNativeQuery(
			"select * from object_types"
				, ObjectType.class)
				.getResultList();
	}
	
	@Override
	public List<Obj> getObjectHierarchy(long current) {
		return em
				.createNativeQuery(
						"select * from objects "
								+ "start with object_id = ? "
								+ "connect by object_id = prior  parent_id "
								+ "order by  level desc ", Obj.class)
				.setParameter(1, current).getResultList();
	}

	@Override
	public List<Attribute> getAttributesAvailableToBind(long objectTypeId) {
		return em
				.createNativeQuery(
						"with ot as (select ? id from dual) " +
						"select attrs.* from attributes attrs " +
						"where attrs.attr_id not in ( " +
						"        select attr.attr_id from attributes attr, attr_object_types aot  " +
						"        where aot.object_type_id = (select id from ot)  and aot.attr_id = attr.attr_id) " +
						"	 and attrs.attr_id not in ( " +
						"		 select a.attr_id from attributes a, attr_object_types aot  " +
						"        where a.attr_id = aot.attr_id  and a.show_order != 0  and aot.object_type_id in (  " +
						"            select object_type_id from object_types  " +
						"            start with object_type_id = (select parent_id from object_types where object_type_id = (select id from ot)) " + 
						"            connect by object_type_id = prior  parent_id ))",
						Attribute.class).setParameter(1, objectTypeId)
				.getResultList();
	}

	@Override
	public Attribute getAttribute(long attrId) {
		return em.find(Attribute.class, attrId);
	}
	
	@Override
	public List<Parameter> getParameters(Long objectId){
		return em.createNativeQuery(
				"select * from parameters where object_id = ?",
				Parameter.class).setParameter(1, objectId)
				.getResultList();
	}

}
