package com.alvazan.play;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import play.Play;
import play.PlayPlugin;
import play.data.binding.ParamNode;
import play.data.binding.RootParamNode;

import com.alvazan.orm.api.base.Bootstrap;
import com.alvazan.orm.api.base.MetaLayer;
import com.alvazan.orm.api.base.NoSqlEntityManager;
import com.alvazan.orm.api.base.NoSqlEntityManagerFactory;
import com.alvazan.orm.api.base.anno.NoSqlEmbeddable;
import com.alvazan.orm.api.base.anno.NoSqlEntity;
import com.alvazan.orm.api.exc.RowNotFoundException;
import com.alvazan.play.logging.CassandraAppender;

public class NoSqlPlugin extends PlayPlugin {

	private static final Logger log = LoggerFactory.getLogger(NoSqlPlugin.class);

	private static final Map<String, Object> PROPS = new HashMap<String, Object>();

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
    public Object bind(RootParamNode rootParamNode, String name, Class clazz, java.lang.reflect.Type type, Annotation[] annotations) {
        NoSqlEntityManager em = NoSql.em();
        MetaLayer metaLayer = em.getMeta();
        if(!metaLayer.isManagedEntity(clazz))
        	return null;
        
        ParamNode paramNode = rootParamNode.getChild(name, true);

        String keyFieldName = metaLayer.getKeyFieldName(clazz);
        ParamNode id = paramNode.getChild(keyFieldName);

        String idStr = NoSqlModel.retrieveValue(id);
        if(idStr == null)
        	return NoSqlModel.create(rootParamNode, name, clazz, annotations);

        Object theId = metaLayer.convertIdFromString(clazz, idStr);
        
        //Read the entity in so that this entity is used instead...
    	Object o = em.find(clazz, theId);
    	if(o == null)
    		throw new RowNotFoundException("Row with rowkey="+theId+" was not found, but your page posted this id to lookup the row of class type="+clazz.getSimpleName());
    	return NoSqlModel.edit(rootParamNode, name, o, annotations);
    }

	@Override
    public Object bindBean(RootParamNode rootParamNode, String name, Object bean) {
    	NoSqlEntityManager mgr = NoSql.em();
    	MetaLayer meta = mgr.getMeta();
    	if(meta.isManagedEntity(bean.getClass())) {
            return NoSqlModel.edit(rootParamNode, name, bean, null);
        }
        return null;
    }
    
	@SuppressWarnings("rawtypes")
	@Override
	public void onApplicationStart() {
        List<Class> classes = Play.classloader.getAnnotatedClasses(NoSqlEntity.class);
        List<Class> classEmbeddables = Play.classloader.getAnnotatedClasses(NoSqlEmbeddable.class);
        classes.addAll(classEmbeddables);
        if (classes.isEmpty())
            return;
        else if (NoSql.getEntityManagerFactory() != null) {
        	NoSqlEntityManagerFactory factory = NoSql.getEntityManagerFactory();
        	factory.rescan(classes, Play.classloader);
        	return;
        }

        Class<?> clazz = null;
        try {
			clazz = Class.forName("nosql.NoSqlConfigImpl");
			if(NoSqlConfig.class.isAssignableFrom(clazz)) {
				NoSqlConfig config = (NoSqlConfig) clazz.newInstance();
				config.configure(PROPS);
			} else {
				log.warn("We found a class="+clazz+" to use to configure playorm but it did not implement NoSqlConfig interface so we can't use it");
			}
			
		} catch (ClassNotFoundException e) {
			log.info("nosql.NoSqlConfigImpl not found so not instantiating and calling configure to do custom configuration of playorm");
			//ignore then
		} catch (InstantiationException e) {
			log.warn("Exception, could not instantiate="+clazz, e);
		} catch (IllegalAccessException e) {
			log.warn("Exception, could not instantiate="+clazz, e);
		}

		PROPS.put(Bootstrap.LIST_OF_EXTRA_CLASSES_TO_SCAN_KEY, classes);
		PROPS.put(Bootstrap.AUTO_CREATE_KEY, "create");

		for (java.util.Map.Entry<Object, Object> entry : Play.configuration.entrySet()) {
			PROPS.put((String) entry.getKey(), entry.getValue());
		}
        
        if (log.isInfoEnabled())
			log.info("Initializing PlayORM...");

        NoSqlEntityManagerFactory factory = Bootstrap.create(PROPS, Play.classloader);
        NoSql.setEntityManagerFactory(factory);
        CassandraAppender.setFactory(factory);
	}

	@Override
	public void onApplicationStop() {
		if (log.isInfoEnabled())
			log.info("stopping PlayOrm");
	}
	
    @Override
    public void beforeInvocation() {
        if (!NoSql.isEnabled())
            return;

        NoSqlEntityManager manager = NoSql.getEntityManagerFactory().createEntityManager();
        NoSql.createContext(manager);
    }
    
    @Override
    public void afterInvocation() {
    	NoSql.clearContext();
    }

    @Override
    public void onInvocationException(Throwable e) {
    	NoSql.clearContext();
    }

    @Override
    public void invocationFinally() {
    	NoSql.clearContext();
    }

	public static Map<String, Object> getProperties() {
		return PROPS;
	}
}

@@ -3025,18 +3025,24 @@
   try %7B%0A
-%09%09
+        
 %09clazz =
@@ -3046,21 +3046,34 @@
 z = 
-Class.forName
+Play.classloader.loadClass
 (%22no

fix the plugin to read in nosqlconfig when running in qa and prod