package de.tudarmstadt.informatik.hostage.sync.tracing;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;

import android.app.IntentService;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.preference.PreferenceManager;
import android.util.Log;
import de.tudarmstadt.informatik.hostage.logging.AttackRecord;
import de.tudarmstadt.informatik.hostage.net.MySSLSocketFactory;
import de.tudarmstadt.informatik.hostage.persistence.HostageDBOpenHelper;

/**
 * Service that synchronizes with a specified remote server.
 * 
 * @author Lars Pandikow
 */
public class TracingSyncService extends IntentService {

	public static final String REMOTE_DEVICE = "de.tudarmstadt.informatik.hostage.REMOTE_DEVICE";

	public static final String ACTION_START_SYNC = "de.tudarmstadt.informatik.hostage.ACTION_START_SYNC";
	public static final String EXTRA_RECEIVER = "de.tudarmstadt.informatik.hostage.EXTRA_HANDLER";

	public static final String UPLOAD_SIZE = "de.tudarmstadt.informatik.hostage.UPLOAD_SIZE";
	public static final String UPLOAD_PROGRESS = "de.tudarmstadt.informatik.hostage.UPLOAD_PROGRESS";

	public static final int RECORD_UPLOADED = 0x00;
	public static final int SYNC_COMPLETE = 0x01;

	private HttpClient httpClient;
	private ResultReceiver receiver;

	HostageDBOpenHelper dbh;

	SharedPreferences pref;
	Editor editor;

	public TracingSyncService() {
		super(TracingSyncService.class.getName());

	}

	@Override
	public void onCreate() {
		super.onCreate();
		pref = PreferenceManager.getDefaultSharedPreferences(this);
		editor = pref.edit();
		dbh = new HostageDBOpenHelper(this);
	}

	/**
	 * The IntentService calls this method from the default worker thread with
	 * the intent that started the service. When this method returns,
	 * IntentService stops the service, as appropriate.
	 */
	@Override
	protected void onHandleIntent(Intent intent) {
		if (intent != null) {
			final String action = intent.getAction();
			if (ACTION_START_SYNC.equals(action)) {
				receiver = intent.getParcelableExtra(EXTRA_RECEIVER);
				syncNewRecords();
				dbh.clearSyncInfos();
				if (receiver != null) {
					receiver.send(SYNC_COMPLETE, null);
				}
			}

		}
	}

	/**
	 * Uploads all new Records to a server, specified in the settings.
	 */
	private void syncNewRecords() {
		int lastUploadedAttackId = pref.getInt("LAST_UPLOADED_ATTACK_ID", -1);
		// String serverAddress = pref.getString("pref_upload",
		// "https://ssi.cased.de");
		String serverAddress = "http://87.230.23.240/hostage/push.php";
		ArrayList<AttackRecord> recordList = dbh.getRecordOfEachAttack(lastUploadedAttackId);
		int size = recordList.size();
		int offset = 1;
		for (AttackRecord record : recordList) {
			editor.putInt("LAST_UPLOADED_ATTACK_ID", lastUploadedAttackId + offset);
			editor.commit();

			boolean success = uploadSingleRecord(record, serverAddress);
			Log.i("Tracing upload", "Upload of record: " + offset + "/" + size + ((success) ? " successful." : " failed."));
			if (receiver != null) {
				Bundle data = new Bundle();
				data.putInt(UPLOAD_SIZE, size);
				data.putInt(UPLOAD_PROGRESS, offset);
				receiver.send(RECORD_UPLOADED, data);
			}
			offset++;

			// TODO pull
			// getRemoteData(record.getBssid(), record.getTimestamp());
		}
	}

	/**
	 * Uploads a single Record to a server, specified in the settings.
	 * 
	 * @param record
	 *            The Record to upload.
	 * @serverAddress Address of the target server
	 * @return True if the upload was successful, else false.
	 */
	private boolean uploadSingleRecord(AttackRecord record, String serverAddress) {
		// Create a https client. Uses MySSLSocketFactory to accept all
		// certificates
		HttpPost httppost;
		try {
			httpClient = createHttpClient();
			// Create HttpPost
			httppost = new HttpPost(serverAddress);
			// Create JSON String of Record
			// TODO StringEntity se = new
			// StringEntity(record.toString(TraCINgFormatter.getInstance()));
			StringEntity se = new StringEntity("record=" + record.toJSON()); // FIXME
			httppost.addHeader("content-type", "application/x-www-form-urlencoded");
			httppost.setEntity(se);
			// Execute HttpPost
			HttpResponse response = httpClient.execute(httppost);
			Log.i("TracingSyncService", "Status Code: " + response.getStatusLine().getStatusCode());
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * Gets the data from the server and updates the database.
	 */
	private void getRemoteData(String bssid, long timestamp) {
		HttpURLConnection connection;
		OutputStreamWriter request = null;
		URL url = null;
		String response = null;
		String parameters = "bssid=" + bssid + "timestamp=" + timestamp;
		try {
			url = new URL("http://87.230.23.240/hostage/pull.php");
			connection = (HttpURLConnection) url.openConnection();
			connection.setDoOutput(true);
			connection.setRequestProperty("content-type", "application/x-www-form-urlencoded");
			connection.setRequestMethod("POST");

			request = new OutputStreamWriter(connection.getOutputStream());
			request.write(parameters);
			request.flush();
			request.close();
			String line = "";

			InputStreamReader isr = new InputStreamReader(connection.getInputStream());
			BufferedReader reader = new BufferedReader(isr);
			StringBuilder sb = new StringBuilder();
			while ((line = reader.readLine()) != null) {
				sb.append(line);
			}
			response = sb.toString();
			isr.close();
			reader.close();
			Log.d("TEST", response);
		} catch (IOException e) {
			Log.i("NetworkTest", "Network Error: " + e);
		}
	}

	/**
	 * Creates a HttpClient with an own SSL Socket.
	 * 
	 * @return HttpsClient who accepts accepts all certificates.
	 * @see MySSLSocketFactory
	 */
	private HttpClient createHttpClient() {
		try {
			KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			trustStore.load(null, null);

			SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
			sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

			HttpParams params = new BasicHttpParams();
			HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

			SchemeRegistry registry = new SchemeRegistry();
			registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
			registry.register(new Scheme("https", sf, 443));

			ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);

			return new DefaultHttpClient(ccm, params);
		} catch (Exception e) {
			e.printStackTrace();
			return new DefaultHttpClient();
		}
	}

}
