/*
* This file is part of the Remote Sensor Actuator Interface (ReSAI).
*
* Copyright(c) Andreas Kipp, Frederic Siepmann
* http://opensource.cit-ec.de/projects/resai
*
* This file may be licensed under the terms of of the
* GNU Lesser General Public License Version 3 (the ``LGPL''),
* or (at your option) any later version.
*
* Software distributed under the License is distributed
* on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
* express or implied. See the LGPL for the specific language
* governing rights and limitations.
*
* You should have received a copy of the LGPL along with this
* program. If not, go to http://www.gnu.org/licenses/lgpl.html
* or write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The development of this software was supported by the
* Excellence Cluster EXC 277 Cognitive Interaction Technology.
* The Excellence Cluster EXC 277 is a grant of the Deutsche
* Forschungsgemeinschaft (DFG) in the context of the German
* Excellence Initiative.
*
*/

/**
 * Evaluation tab to measure exchange rates.
 *
 * @author akipp
 */

package de.unibi.airobots.resaidroid.tabactivities;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.HashMap;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.widget.TextView;
import de.unibi.airobots.resaidroid.R;
import de.unibi.airobots.resaidroid.helper.TimeConverter;
import de.unibi.airobots.resaidroid.tabactivities.template.TabTemplate;

public class TabEvaComponent extends TabTemplate {

	private long servertime = 0;
	private long clientTime = 0;
	// private long timeDiff = 0;

	private long sendTime = 0;
	private long receiveTime = 0;
	private long workTime = 0;
	private long receiveLatency = 0;
	private long workLatency = 0;
	private long sumLatency = 0;
	private double meanlatency = 0.0;
	private long messageNo = 0;

	private TextView txtServerTime;
	private TextView txtClientTime;
	private TextView txtMeanLatency;
	private TextView txtWorkText;
	private TextView txtWorkTime;
	private TextView txtSendTime;
	private TextView txtReciveTime;
	private TextView txtMessageNo;

	private String filename = "";

	final private String STAT_FILE = "/proc/stat";

	private long mUser;
	private long mSystem;
	private long mTotal;

	private long lastResaiRuntime = 0;
	private String cpuText = "";

	private double resaiCpu = 0.0;
	private double cpu = 0.0;

	private int batteryLevel = 0;
	private int battOld = 0;
	private int charsCounted = 0;

	final private DecimalFormat mPercentFmt = new DecimalFormat("#0.0");

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.evacomponent);

		txtServerTime = (TextView) findViewById(R.id.serverTime);
		txtClientTime = (TextView) findViewById(R.id.clientTime);
		txtMeanLatency = (TextView) findViewById(R.id.meanLatency);
		txtWorkText = (TextView) findViewById(R.id.worktext);
		txtWorkTime = (TextView) findViewById(R.id.workTime);
		txtReciveTime = (TextView) findViewById(R.id.receiveTime);
		txtSendTime = (TextView) findViewById(R.id.sendTime);
		txtMessageNo = (TextView) findViewById(R.id.msgNo);

		lastResaiRuntime = getResaiRuntime();
		registerReceiver(this.mBatInfoReceiver, new IntentFilter(
				Intent.ACTION_BATTERY_CHANGED));
	}

	@Override
	public void processProperties() {

		messageHandler.post(new Runnable() {
			public void run() {
				try {

					synchronized (receivedProperties) {

						if (receivedProperties.containsKey(cm.getComponent(TAG)
								+ "_HANDSHAKE")) {
							servertime = Long.parseLong(receivedProperties.get(
									cm.getComponent(TAG) + "_HANDSHAKE")
									.toString());

							clientTime = System.currentTimeMillis();

							txtServerTime.setText(TimeConverter
									.toDateTime(servertime));
							txtClientTime.setText(TimeConverter
									.toDateTime(clientTime));

							// timeDiff = Math.abs(clientTime - servertime);
							// txtTimeDiff.setText(String.valueOf(timeDiff));

							// CREATE DATA FILE
							
							sumLatency = 0;
							meanlatency = 0.0;
							messageNo = 0;

							filename = receivedProperties.get(cm
									.getComponent(TAG) + "_FILENAME");

							createLogHead();
						}

						if (receivedProperties.containsKey(cm.getComponent(TAG)
								+ "_TIME")) {
							receiveTime = System.currentTimeMillis();
							sendTime = Long.parseLong(receivedProperties.get(
									cm.getComponent(TAG) + "_TIME").toString());

							receiveLatency = Math.abs(Math.abs(sendTime
									- receiveTime));
							

							txtMessageNo.setText(receivedProperties.get(
									cm.getComponent(TAG) + "_MSGNO").toString());							
							
							messageNo = Long.parseLong(receivedProperties.get(
									cm.getComponent(TAG) + "_MSGNO").toString());
							
							sumLatency +=receiveLatency;
							
							meanlatency = sumLatency / messageNo;
							
							txtMeanLatency.setText(String.valueOf(meanlatency));
							
							// TODO - timeDiff);
							txtSendTime.setText(TimeConverter
									.toDateTime(sendTime));
							txtReciveTime.setText(TimeConverter
									.toDateTime(receiveTime)
									+ "("
									+ receiveLatency + ")");


							// HERE WE CREATE WORK
							charsCounted = doWork(receivedProperties);

							workTime = System.currentTimeMillis();
							workLatency = Math.abs(Math
									.abs(workTime - sendTime)); // TODO -
																// timeDiff);
							txtWorkTime.setText(TimeConverter
									.toDateTime(workTime)
									+ "("
									+ workLatency
									+ ")");

							readCpuTime();
							txtWorkText.setText("ReSAI: " + getRuntime()
									+ " / CPU: " + cpuText + "\n");
							txtWorkText.append("Battery:  " + batteryLevel
									+ "%\n");
							txtWorkText.append("Chars worked: " + charsCounted);

							writeLogLine();
						}
					}
				} catch (NullPointerException e) {
					Log.i(TAG, "NULL POINTER...ELEMENT NOT FOUND.");
				}

			}

		});
	}

	/**
	 * This method iterates over all properties and adds their value length to
	 * the return value.
	 * 
	 * @param receivedProperties
	 */
	private int doWork(HashMap<String, String> receivedProperties) {
		int characters = 0;
		for (String prop : receivedProperties.keySet()) {
			for (int i = 0; i < receivedProperties.get(prop).length(); i++) {
				characters++;
			}
		}
		return characters;
	}

	private void createLogHead() {

		FileOutputStream fos;
		try {
			File root = Environment.getExternalStorageDirectory();
			File file = new File(root.getAbsoluteFile(), filename);
			if (file.exists()) {
				file.delete();
			}
			fos = new FileOutputStream(file, true);

			String text = // "timeDiff;" +
					"charsCounted;sendTime;receiveTime;receiveLat;workTime;worklat;resaiCpu;cpu;batteryLevel;battUsage\n";

			fos.write(text.getBytes());
			fos.close();

			// txtWorkText.setText(getRuntime());

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void writeLogLine() {

		FileOutputStream fos;
		try {
			File root = Environment.getExternalStorageDirectory();
			File file = new File(root.getAbsoluteFile(), filename);
			fos = new FileOutputStream(file, true);

			String text = // timeDiff + ";" +
			charsCounted + ";" + sendTime + ";" + receiveTime + ";"
					+ receiveLatency + ";" + workTime + ";" + workLatency + ";"
					+ resaiCpu + ";" + cpu + ";" + batteryLevel + ";"
					+ (battOld - batteryLevel) + "\n";

			fos.write(text.getBytes());
			fos.close();

			// txtWorkText.setText(getRuntime());

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private String getRuntime() {
		long thisResaiRuntime = getResaiRuntime();
		long deltaResaiRuntime = Math.abs(lastResaiRuntime - thisResaiRuntime);

		String text = String.valueOf((deltaResaiRuntime / 10.0) + "% ");

		resaiCpu = (deltaResaiRuntime / 10.0);

		lastResaiRuntime = thisResaiRuntime;

		return text;
	}

	private long getResaiRuntime() {
		File proc_dir = new File("/proc/");

		String files[] = proc_dir.list();
		for (int i = 0; i < files.length; ++i) {
			if (files[i].matches("[0-9]+") == true) {
				String stat = readData("/proc/" + files[i] + "/stat");
				if (stat == null)
					continue;

				String[] segs = stat.split("[ ]+");
				long runtime = Long.parseLong(segs[13])
						+ Long.parseLong(segs[14]);

				String cmdline = segs[1];

				if (cmdline.toLowerCase().contains("resai")) {
					return runtime;
				}
			}
		}
		return 0;
	}

	private String readData(String filename) {
		FileReader fstream;
		try {
			fstream = new FileReader(filename);
		} catch (FileNotFoundException e) {
			Log.i("NetMeter", "File access error " + filename);
			return null;
		}

		BufferedReader in = new BufferedReader(fstream, 500);
		try {
			return in.readLine();
		} catch (IOException e) {
			Log.i("NetMeter", "read error on " + filename);
			return null;
		}
	}

	private void readCpuTime() {

		FileReader fstream;
		try {
			fstream = new FileReader(STAT_FILE);

			BufferedReader in = new BufferedReader(fstream, 500);
			String line;
			while ((line = in.readLine()) != null) {
				if (line.startsWith("cpu")) {
					updateStats(line.trim().split("[ ]+"));
					break;
				}
			}
		} catch (FileNotFoundException e) {
			Log.e(TAG, "Could not read " + STAT_FILE);

		} catch (IOException e) {
			Log.e(TAG, e.toString());
		}
	}

	private void updateStats(String[] segs) {

		// user = user + nice
		long user = Long.parseLong(segs[1]) + Long.parseLong(segs[2]);
		// system = system + intr + soft_irq
		long system = Long.parseLong(segs[3]) + Long.parseLong(segs[6])
				+ Long.parseLong(segs[7]);
		// total = user + system + idle + io_wait
		long total = user + system + Long.parseLong(segs[4])
				+ Long.parseLong(segs[5]);

		if (mTotal != 0 || total >= mTotal) {
			long duser = user - mUser;
			long dsystem = system - mSystem;
			long dtotal = total - mTotal;
			cpu = (double) (duser + dsystem) * 100.0 / dtotal;

			cpuText = (mPercentFmt.format((double) (duser + dsystem) * 100.0
					/ dtotal)
					+ "% ("
					+ mPercentFmt.format((double) (duser) * 100.0 / dtotal)
					+ "/"
					+ mPercentFmt.format((double) (dsystem) * 100.0 / dtotal) + ")");

		}
		mUser = user;
		mSystem = system;
		mTotal = total;
	}

	private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context arg0, Intent intent) {
			// TODO Auto-generated method stub
			batteryLevel = intent.getIntExtra("level", 0);
			if (battOld == 0) {
				battOld = batteryLevel;
			}

		}
	};

}