package de.ugoe.cs.autoquest.androidmonitor;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;

import org.xmlpull.v1.XmlSerializer;

import android.util.Log;
import android.util.Xml;
import android.view.View;
import android.view.ViewGroup;

/**
 * 
 * @author Florian Unger
 * @version 1.0
 */
public class AndroidmonitorLogFile {

	// TODO rename getDeviceInformation() and getAppInformation() to set ... use
	// writeToFile for both

	private String name;
	private File file;

	public AndroidmonitorLogFile(String appName, File dir) {
		this.name = "androidLogFile_" + appName + System.currentTimeMillis() + ".log";

		try {
			// prove if file exists
			this.file = new File(dir, this.name);
			/*
			 * if file does not exists write device and app information to a new
			 * file. Otherwise use existing file and add activity information to
			 * file.
			 */
			if (true) { // !this.file.exists()
				/*
				 * create log file. Using method openFileOutput() does not work
				 * for this project due to the reason that this method would try
				 * to create the file in the directory of the non-existing
				 * directory de.ugoe.cs.androidmonitor. This directory does not
				 * exist due to the reason that this project is a library and
				 * the file has to be stored in the directory of the running
				 * application. Furthermore it would not be possible to write in
				 * another app directory as the own one.
				 */

				// TODO split document head information from
				// getDeviceInformation.

				String string = "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><sessions>" 
						+ getDeviceInformation() + getAppInformation();
				try {
					FileOutputStream outputStream = new FileOutputStream(
							this.file);
					outputStream.write(string.getBytes());
					outputStream.close();
				} catch (Exception e) {
					Log.e("this.file", "outputstream: " + e.getMessage());
				}

			} else {
				// TODO add activity information
			}
		} catch (Exception e) {
			e.printStackTrace();
			Log.e("file", "file: " + e.getMessage());
		}

	}

	/**
	 * <p>
	 * Get file name which is in use.
	 * </p>
	 * 
	 * @return filename
	 * 
	 */
	public String getName() {
		return this.name;
	}

	// should be set
	private String getAppInformation() {
		// TODO create app information in the same manner as coded in
		// getDeviceInformation
		return "";
	}

	/**
	 * <p>
	 * Query device information.
	 * </p>
	 * 
	 * @return XML device information in XML format
	 */
	// should be set
	private String getDeviceInformation() {
		String deviceInformation = "";
		XmlSerializer serializer = Xml.newSerializer();
		StringWriter writer = new StringWriter();
		try {
			serializer.setOutput(writer);
			serializer.startTag("", "device");
			serializer.startTag("", "param");
			serializer.attribute("", "value", ""
					+ android.os.Build.VERSION.SDK_INT);
			serializer.attribute("", "name", "sdk_version");
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "value", android.os.Build.DEVICE);
			serializer.attribute("", "name", "device");
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "value", android.os.Build.MANUFACTURER);
			serializer.attribute("", "name", "manufacturer");
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "value", android.os.Build.MODEL);
			serializer.attribute("", "name", "model");
			serializer.endTag("", "param");

			// TODO get resolution ...

			serializer.endTag("", "device");
			serializer.endDocument();

			deviceInformation = writer.toString();

		} catch (IOException e) {
			Log.e("xml", e.getMessage());
		}

		return deviceInformation;
	}

	/**
	 * <p>
	 * Adds some information of an component of an activity (view) to the file.
	 * </p>
	 * 
	 * @param view
	 *            view to be logged
	 * @param parentHash
	 *            hash of the parent view
	 * @param activityName
	 *            name of the activity that is analyzed
	 */
	public void addComponent(View view, int parentHash, String activityName) {
		XmlSerializer serializer = Xml.newSerializer();
		StringWriter writer = new StringWriter();
		// create HEX string

		try {
			serializer.setOutput(writer);
			serializer.startTag("", "component");
			// TODO find a way in that the hash code is unique over time and
			// target
			/*
			 * (non-Javadoc) view.getId() seems to be unique over time and
			 * targets but there is a problem. In some cases there is no ID
			 * (value: -1).
			 */
			serializer.attribute("", "hash", "" + view.hashCode());

			serializer.startTag("", "param");
			serializer.attribute("", "name", "id");
			serializer.attribute("", "value", "" + view.getId());
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "name", "path");
			serializer.attribute("", "value", activityName + "/"
					+ getViewPath(view) + view.getClass().getSimpleName());
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "name", "class");
			serializer.attribute("", "value", view.getClass().getName());
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "name", "parent");
			// Problem in using view.getParent().hashCode():
			// http://developer.android.com/reference/android/view/View.html#getParent()
			// tells: "... parent is a ViewParent and not necessarily a View."
			// ViewParent does not have a method hashCode(). Solution is done
			// add parentHash as parameter to method addComponent() and
			// Androidmonitor-> addLogListenerToView.
			serializer.attribute("", "value", "" + parentHash);
			serializer.endTag("", "param");

			// TODO add title e.g. android:text="Button"

			// TODO add ancestors @see:
			// de.ugoe.cs.autoquest.jfcmonitor.JFCComponent#getClassHierarchy()
			
			/* This solution leads to java.lang.OutOfMemory!
			serializer.startTag("", "ancestors");
			
			Class<? extends Object> classobject = view.getClass();
			while(classobject != null){
				serializer.startTag("", "ancestor");
				serializer.attribute("", "name", classobject.getName());
				serializer.endTag("", "ancestor");
			}
			serializer.endTag("", "ancestors");
			*/

			serializer.endTag("", "component");
			serializer.endDocument();

			writeToFile(writer.toString());

		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.e("file", "outputstream: " + e.getMessage());
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.e("file", "outputstream: " + e.getMessage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.e("file", "outputstream: " + e.getMessage());
		}

	}

	/**
	 * <p>
	 * Add an event to the log file
	 * </p>
	 * 
	 * @param view
	 *            the calling view of the listener
	 * @param type
	 *            the type of listener e.g. onClick ...
	 */
	public void addEvent(View view, String type) {

		String x = "" + view.getX();
		String y = "" + view.getY();

		XmlSerializer serializer = Xml.newSerializer();
		StringWriter writer = new StringWriter();

		try {
			serializer.setOutput(writer);

			serializer.startTag("", "event");
			serializer.attribute("", "id", type);

			serializer.startTag("", "param");
			serializer.attribute("", "value", x);
			serializer.attribute("", "name", "X");
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "value", y);
			serializer.attribute("", "name", "Y");
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "value", "" + view.hashCode());
			serializer.attribute("", "name", "source");
			serializer.endTag("", "param");

			serializer.startTag("", "param");
			serializer.attribute("", "value", "" + System.currentTimeMillis());
			serializer.attribute("", "name", "timestamp");
			serializer.endTag("", "param");

			serializer.endTag("", "event");
			serializer.endDocument();

			writeToFile(writer.toString());
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.e("file", "outputstream: " + e.getMessage());
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.e("file", "outputstream: " + e.getMessage());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.e("file", "outputstream: " + e.getMessage());
		}
	}

	/**
	 * <p>
	 * Writes given information to the file. e.g. previous produced XML
	 * statements.
	 * </p>
	 * 
	 * @param data
	 *            content to add to the file
	 */
	private void writeToFile(String data) {

		FileOutputStream outputStream;
		try {
			outputStream = new FileOutputStream(file, true);
			outputStream.write(data.getBytes());
			outputStream.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			Log.e("file", "outputstream: " + e.getMessage());
		} catch (IOException e) {
			e.printStackTrace();
			Log.e("file", "outputstream: " + e.getMessage());
		}

	}

	/**
	 * <p>
	 * Generates the path of an view element.
	 * </p>
	 * 
	 * @param view
	 * @return path path to the element
	 */
	private String getViewPath(View view) {
		return getViewPath(view, null);
	}

	/**
	 * <p>
	 * Generates the path of an view element.
	 * </p>
	 * 
	 * @param view
	 * @param path
	 * @return path path to the element
	 */
	private String getViewPath(View view, String path) {
		if (path == null) {
			path = "";
		} else {
			path = view.getClass().getSimpleName() + "/" + path;
		}
		if (view.getParent() != null && (view.getParent() instanceof ViewGroup)) {
			return getViewPath((View) view.getParent(), path);
		} else {
			return path;
		}
	}

}
