// This software is released into the Public Domain.  See copying.txt for details.
package org.openstreetmap.osmosis.replication.v0_6;

import java.io.File;
import java.text.MessageFormat;
import java.util.logging.Logger;

import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
import org.openstreetmap.osmosis.core.task.common.RunnableTask;
import org.openstreetmap.osmosis.core.util.FileBasedLock;
import org.openstreetmap.osmosis.core.util.PropertiesPersister;
import org.openstreetmap.osmosis.replication.common.ReplicationState;
import org.openstreetmap.osmosis.replication.common.ServerStateReader;
import org.openstreetmap.osmosis.replication.v0_6.impl.ReplicationDownloaderConfiguration;

/**
 * Compares the timestamp of a local replication directory and the timestamp on the 
 * HTTP server that is configured to provide the replication files. It calculates 
 * the number of seconds the local replication directory is behind the HTTP server
 * and prints it to stdout.
 * 
 * @author Peter Koerner
 */
public class ReplicationLagReader implements RunnableTask {
	private static final Logger LOG = Logger.getLogger(ReplicationLagReader.class.getName());
	private static final String LOCK_FILE_NAME = "download.lock";
	private static final String CONFIG_FILE = "configuration.txt";
	private static final String LOCAL_STATE_FILE = "state.txt";
	
	private boolean humanReadable;
	private File workingDirectory;
	private ServerStateReader serverStateReader;
	
	/**
	 * Creates a new instance.
	 * 
	 * @param workingDirectory
	 *            The directory containing configuration and tracking files.
	 * @param humanReadable
	 *            Print the replication lag in a Hours, Minutes and Seconds
	 *            instead of the raw number of seconds
	 */
	public ReplicationLagReader(File workingDirectory, boolean humanReadable) {
		this.workingDirectory = workingDirectory;
		this.humanReadable = humanReadable;
		
		serverStateReader = new ServerStateReader();
	}
	
	
	/**
	 * Calculate the replication lag and print it to stdout
	 */
	private void getLag() {
		ReplicationDownloaderConfiguration configuration;
		ReplicationState serverState;
		ReplicationState localState;
		PropertiesPersister localStatePersistor;
		
		// Instantiate utility objects.
		configuration = new ReplicationDownloaderConfiguration(new File(workingDirectory, CONFIG_FILE));
		
		// Obtain the server state.
		LOG.fine("Reading current server state.");
		serverState = serverStateReader.getServerState(configuration.getBaseUrl());
		
		// Build the local state persister which is used for both loading and storing local state.
		localStatePersistor = new PropertiesPersister(new File(workingDirectory, LOCAL_STATE_FILE));
		
		// If local state isn't available we need to fail because no lag can be calculated.
		if (!localStatePersistor.exists()) {
			throw new OsmosisRuntimeException("Can't read local state.");
		}
		
		// fetch the local state from the file
		localState = new ReplicationState(localStatePersistor.loadMap());
		
		// extract the time of the local and the remote state files
		long local = localState.getTimestamp().getTime();
		long server = serverState.getTimestamp().getTime();
		
		// we assume the server being before the local state while calculating the difference
		long lag = (server - local) / 1000;
		
		// check if a human readable version is requested
		if (this.humanReadable) {
			
			if (lag > 86400) {
				
				// more than a day
				Object[] args = {
					Long.valueOf(lag / 86400),
					Long.valueOf((lag % 86400) / 3600)
				};
				System.out.println(
					new MessageFormat("{0} day(s) and {1} hour(s)").format(args)
				);
				
			} else if (lag > 3600) {
				
				// morte than an hour
				Object[] args = {
					Long.valueOf(lag / 3600),
					Long.valueOf((lag % 3600) / 60)
				};
				System.out.println(
					new MessageFormat("{0} hour(s) and {1} minute(s)").format(args)
				);
				
			} else if (lag > 60) {
				
				// more than a minute
				Object[] args = {
					Long.valueOf(lag / 60),
					Long.valueOf(lag % 60)
				};
				System.out.println(
					new MessageFormat("{0} minute(s) and {1} second(s)").format(args)
				);
				
			} else {
				Object[] args = {
					Long.valueOf(lag)
				};
				
				// just some seconds
				System.out.println(
					new MessageFormat("{0} second(s)").format(args)
				);
				
			}
			
		} else {
			
			// print out the raw number of seconds
			System.out.println(lag);
			
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void run() {
		FileBasedLock fileLock;
		
		fileLock = new FileBasedLock(new File(workingDirectory, LOCK_FILE_NAME));
		
		try {
			fileLock.lock();
			
			getLag();
			
			fileLock.unlock();
			
		} finally {
			fileLock.close();
		}
	}
}
