/**
 * Copyright (C) 2016 Dourachok Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package com.ivan.dourachok.engine.runtime.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import com.ivan.dourachok.Engine;
import com.ivan.dourachok.annotation.TimeStamp;
import com.ivan.dourachok.annotation.Ttl;
import com.ivan.dourachok.engine.runtime.exception.DourachokRuntimeException;

/**
 * util class for getting timestamp of an object, storing/getting it, etc.
 * @author Christophe Dame
 */
public class FactUtils {
	
	private static Map<Class<?>, Method> timestampClassMap = new HashMap<>();
	
	public static long computeFactTimeStamp(Object fact, Engine engine) throws DourachokRuntimeException {
		Method tmstmpMethod = getTimeStampMethod(fact.getClass());
		long timeStamp = 0;
		if (tmstmpMethod==null) {
			timeStamp = engine.getCurrentTime();
		} else {
			try {
				timeStamp = (Long) tmstmpMethod.invoke(fact);
			} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
				throw new DourachokRuntimeException("@TimeStamp method in "+fact.getClass()+" should return a long and have no parameters ",e);
			}
		}
		return timeStamp;
	}
	
	public static long computeTtl(Ttl ttlAnotation) {
		long result = ttlAnotation.days()*86400000;
		result += ttlAnotation.hours()*3600000;
		result += ttlAnotation.minutes()*60000;
		result += ttlAnotation.seconds()*1000;
		result += ttlAnotation.milliseconds();
		return result;
	}
	
	private static Method getTimeStampMethod(Class<?> type) {
		Method tmstmpMethod = timestampClassMap.get(type);
		if (tmstmpMethod==null) {
			Class<?> klass = type;
			while (klass != Object.class) {
				Method[] allMethods = type.getDeclaredMethods();
		        for (final Method method : allMethods) {
		            if (method.isAnnotationPresent(TimeStamp.class)) {
		            	tmstmpMethod = method;
		            	timestampClassMap.put(type, tmstmpMethod);
		            	return tmstmpMethod;
		            }
		        }
		        klass = klass.getSuperclass();
		    }
		}
		return tmstmpMethod;
	}

}
