/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.update.internal.core;

import java.io.*;
import java.net.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.StringTokenizer;

import org.eclipse.core.runtime.*;
import org.eclipse.update.configuration.*;
import org.eclipse.update.configurator.*;
import org.eclipse.update.core.*;
import org.eclipse.update.internal.operations.*;


/**
 * Parses the installation log and creates installation configuration objects
 */
public class InstallLogParser {
	private IPath logPath;
	private BufferedReader buffRead;
	private InstallConfiguration currentConfiguration;
	private HashMap installConfigMap;
	private Comparator comparator;
	
	private static final String FEATURE_INSTALL = "feature-install"; //$NON-NLS-1$
	private static final String FEATURE_REMOVE = "feature-remove"; //$NON-NLS-1$
	private static final String SITE_INSTALL = "site-install"; //$NON-NLS-1$
	private static final String SITE_REMOVE = "site-remove"; //$NON-NLS-1$
	private static final String UNCONFIGURE = "feature-disable"; //$NON-NLS-1$
	private static final String CONFIGURE = "feature-enable"; //$NON-NLS-1$
	private static final String REVERT = "revert"; //$NON-NLS-1$
	private static final String RECONCILIATION = "reconciliation"; //$NON-NLS-1$
	private static final String PRESERVED = "preserve-configuration"; //$NON-NLS-1$	
	
	private static final String ACTIVITY = "!ACTIVITY"; //$NON-NLS-1$
	
	public static final String SUCCESS = "success"; //$NON-NLS-1$
	public static final String FAILURE = "failure"; //$NON-NLS-1$

	
	public InstallLogParser(){
		String loc = ConfiguratorUtils.getCurrentPlatformConfiguration().getConfigurationLocation().getFile();
		logPath = new Path(loc).removeLastSegments(1).append("install.log");  //$NON-NLS-1$
		installConfigMap = new HashMap();
		try {
			InstallConfiguration[] configs = (InstallConfiguration[])SiteManager.getLocalSite().getConfigurationHistory();
			for (int i=0;i<configs.length; i++){
				if (!configs[i].isCurrent())
					installConfigMap.put(new Long(configs[i].getCreationDate().getTime()), configs[i]);
			}
			// Need to make a copy of the current config instead
			InstallConfiguration config = getConfigCopy((InstallConfiguration)SiteManager.getLocalSite().getCurrentConfiguration());
			installConfigMap.put(new Long(config.getCreationDate().getTime()), config);
			
		} catch (CoreException e) {
			UpdateCore.log(e);
		} catch (MalformedURLException e){
			UpdateCore.log(e);
		}
		comparator = new Comparator(){
			public int compare(Object e1, Object e2) {
				Date date1 = ((InstallConfiguration)e1).getCreationDate();
				Date date2 = ((InstallConfiguration)e2).getCreationDate();
				return date1.before(date2) ? 1 : -1;
			}
		};
	}
	private InstallConfiguration getConfigCopy(InstallConfiguration origConfig) throws CoreException, MalformedURLException{
		InstallConfiguration config = new InstallConfiguration(origConfig, origConfig.getURL(), origConfig.getLabel() );
		config.setCreationDate(origConfig.getCreationDate());
		return config;
	}
	public void parseInstallationLog(){
		try {
			openLog();
			parseLog();
		} catch (CoreException e) {
			UpdateUtils.logException(e);
		} finally {
			closeLog();
		}
	}
	
	private void openLog() throws CoreException {
		try {
		    // throws FileNotFoundException, IOException
		    InputStream is = new FileInputStream(logPath.toOSString());
		    // throws UnsupportedEncodingException
		    InputStreamReader isr = new InputStreamReader(is,"UTF-8"); //$NON-NLS-1$
		    buffRead = new BufferedReader(isr);
		} catch (Exception e) {
			throwCoreException(e);
		}
	}
	
	private void throwCoreException(Throwable e) throws CoreException {
		throw new CoreException(
			new Status(
				IStatus.ERROR,
				UpdateUtils.getPluginId(),
				IStatus.ERROR,
				Messages.InstallLogParser_errors, 
				e));
	}
	
	private void parseLog() throws CoreException {
		// 		.install-log template
		//		!CONFIGURATION <configuration-date>
		//		!ACTIVITY <date> <target> <action> <status>

		try {
			String type, status, action;
			StringTokenizer htmlCode;

			while (buffRead.ready()) {

				htmlCode = new StringTokenizer(buffRead.readLine());
				while (!(htmlCode.hasMoreElements())) {
					if (!buffRead.ready())
						return;
					htmlCode = new StringTokenizer(buffRead.readLine());
				}

				type = htmlCode.nextToken().trim();

				if (type.equals(ACTIVITY)) {
					String time = htmlCode.nextToken();
					String date;
					StringBuffer target = new StringBuffer();
					date = htmlCode.nextToken(".");  //$NON-NLS-1$
					htmlCode.nextToken(" ");  //$NON-NLS-1$
					while (htmlCode.countTokens() > 2){
						target.append(" "); //$NON-NLS-1$
						target.append(htmlCode.nextToken());
					}
					
					action = htmlCode.nextToken();
					status = htmlCode.nextToken();
					createActivity(action, time, date, status, target.toString(), currentConfiguration);
				}  else {
					String time = htmlCode.nextToken();
					StringBuffer date;
					date = new StringBuffer();
					while (htmlCode.countTokens() > 0){
						if (date.length() != 0)
							date.append(" "); //$NON-NLS-1$
						date.append(htmlCode.nextToken());
					}
					currentConfiguration = (InstallConfiguration)installConfigMap.get(new Long(time));
				}
			}
		} catch (Exception e) {
			throwCoreException(e);
		}
	}
	
	private void closeLog() {
		try {
			if (buffRead != null)
				buffRead.close();
		} catch (IOException e) {
		} finally {
			buffRead = null;
		}
	}
	private IActivity createActivity(String action, String time, String date, String status, String target, InstallConfiguration config){
		ConfigurationActivity a = new ConfigurationActivity();

		int code = 0;
		if (FEATURE_INSTALL.equals(action))
			code = IActivity.ACTION_FEATURE_INSTALL;
		else if (FEATURE_REMOVE.equals(action))
			code = IActivity.ACTION_FEATURE_REMOVE;
		else if (SITE_INSTALL.equals(action))
			code = IActivity.ACTION_SITE_INSTALL;
		else if (SITE_REMOVE.equals(action))
			code = IActivity.ACTION_SITE_REMOVE;
		else if (UNCONFIGURE.equals(action))
			code = IActivity.ACTION_UNCONFIGURE;
		else if (CONFIGURE.equals(action))
			code = IActivity.ACTION_CONFIGURE;
		else if (REVERT.equals(action))
			code = IActivity.ACTION_REVERT;
		else if (RECONCILIATION.equals(action))
			code = IActivity.ACTION_RECONCILIATION;
		else if (PRESERVED.equals(action))
			code = IActivity.ACTION_ADD_PRESERVED;
		
		a.setAction(code);
		try {
			long activityTime = Long.parseLong(time);
			a.setDate(new Date(activityTime));
		} catch (NumberFormatException e) {
			//PAL foundation
			//a.setDate(new Date(date));
			try {
				a.setDate(new SimpleDateFormat().parse(date));
			} catch (ParseException e1) {
				//ignore
			}
		}
		a.setStatus(SUCCESS.equals(status) ? IActivity.STATUS_OK : IActivity.STATUS_NOK);
		a.setLabel(target);
		a.setInstallConfigurationModel(config);
		
		if (config != null && !configContainsActivity(config, a)){
			config.addActivity(a);
		}
		
		return a;
	}
	
	private boolean configContainsActivity(InstallConfiguration c, IActivity a){
		IActivity[] activities = c.getActivities();
		for (int i = 0 ; i<activities.length; i++){
			if (a.equals(activities[i]))
				return true;
		}
		return false;
	}

	public InstallConfiguration[] getConfigurations(){
		Collection configSet = installConfigMap.values();
		InstallConfiguration[] configs = (InstallConfiguration[]) configSet.toArray(new InstallConfiguration[configSet.size()]);
		Arrays.sort(configs, comparator);
		return configs;
	}
}
