/*
@license.text@
 */
package com.hammurapi.reasoning.tutorial;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import com.hammurapi.config.runtime.FactoryConfig;
import com.hammurapi.reasoning.ExceptionHandler;
import com.hammurapi.reasoning.ForwardReasoningSession;
import com.hammurapi.reasoning.ReasoningException;
import com.hammurapi.reasoning.impl.ForwardReasoningSessionFactoryImpl;
import com.hammurapi.reasoning.tutorial.objectmodel.Child;
import com.hammurapi.reasoning.tutorial.objectmodel.Person;
import com.hammurapi.reasoning.tutorial.objectmodel.Relative;
import com.hammurapi.reasoning.tutorial.objectmodel.Spouse;
import com.hammurapi.util.CompositeContext;
import com.hammurapi.util.DefaultContext;
import com.hammurapi.util.SimpleMutableContext;

public class Tutorial {

	/**
	 * Infers relationships.
	 * @throws ReasoningException 
	 */
	private static void putFacts(ForwardReasoningSession<Relative> session) throws ReasoningException {
		
		Person kate = new Person("Kate", 58, false);
		Person victor = new Person("Victor", 63, true);
		session.put(new Spouse(kate, victor));

		Person peter = new Person("Peter", 37, true);
		session.put(new Child(peter, kate));
		session.put(new Child(peter, victor));
		
		Person alison = new Person("Alison", 36, false);
		session.put(new Spouse(peter, alison));

		Person lucy = new Person("Lucy", 17, false);
		session.put(new Child(lucy, alison));
		
		Person nancy = new Person("Nancy", 14, false);
		session.put(new Child(nancy, peter));
		
		Person dan = new Person("Dan", 7, true);
		session.put(new Child(dan, peter));
		session.put(new Child(dan, alison));
		
		Person audrey = new Person("Audrey", 4, false);
		session.put(new Child(audrey, peter));
		session.put(new Child(audrey, alison));		
		
		Person tanya = new Person("Tanya", 31, false);
		Person max = new Person("Max", 32, true);
		session.put(new Spouse(tanya, max));
		session.put(new Child(tanya, kate));
		session.put(new Child(tanya, victor));

		Person vilma = new Person("Vilma", 14, false);
		session.put(new Child(vilma, tanya));
		
		Person george = new Person("George", 10, true);
		session.put(new Child(george, tanya));
		
		Person lisa = new Person("Lisa", 5, false);
		session.put(new Child(lisa, tanya));
		session.put(new Child(lisa, max));		
	}

	/**
	 * Main method for the tutorial.
	 * @param args The first argument is rule set URI.
	 */
	public static void main(String[] args) throws Exception {	
		long factoryStart = System.currentTimeMillis();
		System.out.println("Hammurapi rules tutorial");
		
		if (args.length!=1) {
			System.out.println("Usage: java <options> biz.hammurapi.rules.tutorial.Tutorial <rule set file>");
			System.exit(1);
		}
		
		FactoryConfig factoryConfig = new FactoryConfig();
				
		final AtomicInteger commandCounter = new AtomicInteger();
		
		ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 15, 20, TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>()) {
			@Override
			public void execute(Runnable command) {
				commandCounter.incrementAndGet();
				super.execute(command);
			}
		};
		
		SimpleMutableContext sc = new SimpleMutableContext();
		sc.register(Executor.class, executor);
		
		factoryConfig.setContext(new CompositeContext(new DefaultContext(Tutorial.class.getClassLoader(), null), sc));
		
		ForwardReasoningSessionFactoryImpl<Relative> factory = new ForwardReasoningSessionFactoryImpl<Relative>(new File(args[0]), factoryConfig); 		
		
		ForwardReasoningSession<Relative> session = factory.createSession(null);
		
		session.setExceptionHandler(new ExceptionHandler() {
			
			@Override
			public void handleException(Exception e) throws Exception {
				System.out.println("Exception: "+e);				
			}
		});
		
		System.out.println("Session created in "+(System.currentTimeMillis()-factoryStart));
		long start = System.currentTimeMillis();
		try {
			putFacts(session);
			session.executeRules();
			
			List<Relative> allFacts = new ArrayList<Relative>(session.getObjects());
			Collections.sort(allFacts);
			
			System.out.println("Inference time "+(System.currentTimeMillis()-start)+" for "+allFacts.size()+" objects.");
			System.out.println("Commands executed "+commandCounter.get());
			System.out.println("=== All facts ===");
			int factNo = 0;
//			int maxDepth = 0;
//			int maxCardinality = 0;
//			Object maxCardinalityFact = null;
			Person lastSubject = null;
			for (Relative fact: allFacts) {				
				if (lastSubject==null || !lastSubject.equals(fact.getSubject())) {
					if (lastSubject!=null) {
						System.out.println("---");
					}
					lastSubject = fact.getSubject();
				}
				System.out.println(++factNo+": "+fact);
//				int dc = 0;
//				for (Derivation<Object> d: session.getDerivations(fact)) {
//					System.out.println(++dc+": "+d);
//					maxDepth = Math.max(maxDepth, d.getDepth());
//					int c = d.getCardinality();
//					if (c>maxCardinality) {
//						maxCardinality = c;
//						maxCardinalityFact = fact;
//					}
//				}
//				System.out.println("---");
			}
			
//			System.out.println("Max depth: "+maxDepth);
//			System.out.println("Max cardinality: "+maxCardinality);
//			
//			System.out.println("~~~ Max cardinality fact: "+maxCardinalityFact);
//			int dc = 0;
//			for (Derivation<Object> d: session.getDerivations(maxCardinalityFact)) {
//				System.out.println(++dc+": "+d);
//			}
//			System.out.println("---");
			
			// Exclude already known.
			
			// Derivations.
		} finally {
			session.close();
		}
		executor.shutdown();
	}

}
