package com.unprompted.tildefriends;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.strictmode.Violation;
import android.util.Base64;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.JsResult;
import android.webkit.URLUtil;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
//import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.lang.Process;
import java.lang.Thread;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.TimeUnit;

public class MainActivity extends Activity {
	WebView web_view;
	String base_url;
	Process process;
	Thread thread;

	private ValueCallback<Uri[]> upload_message;
	private final static int FILECHOOSER_RESULT = 1;
	private float touch_down_y;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
			.detectLeakedClosableObjects()
			.penaltyLog()
			.build());
		super.onCreate(savedInstanceState);
		this.requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);

		web_view = (WebView)findViewById(R.id.web);
		set_status("Extracting executable...");
		Log.w("tildefriends", String.format("getFilesDir() is %s", getFilesDir().toString()));
		Log.w("tildefriends", String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString()));
		Log.w("tildefriends", String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir));

		String port_file_path = getFilesDir().toString() + "/port.txt";
		new File(port_file_path).delete();
		base_url = "http://127.0.0.1:12345/";

		MainActivity activity = this;

		thread = new Thread(new Runnable() {
			@Override
			public void run() {
				Log.w("tildefriends", "Watching for changes in: " + getFilesDir().toString());
				try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
					Paths.get(getFilesDir().toString()).register(
						watcher,
						StandardWatchEventKinds.ENTRY_CREATE,
						StandardWatchEventKinds.ENTRY_MODIFY);
					while (true) {
						WatchKey key = watcher.poll(100, TimeUnit.MILLISECONDS);
						boolean attempt_it = true;
						if (key != null)
						{
							attempt_it = false;
							for (WatchEvent event : key.pollEvents()) {
								if (event.context().toString().equals("port.txt")) {
									Log.w("tildefriends", "Observed file write: " + event.context().toString());
									attempt_it = true;
								}
							}
						}
						if (attempt_it) {
							int port = read_port(port_file_path);
							if (port >= 0) {
								base_url = "http://127.0.0.1:" + String.valueOf(port) + "/";
								activity.runOnUiThread(() -> {
									activity.hide_status();
									web_view.loadUrl(base_url);
								});
								break;
							} else {
								activity.runOnUiThread(() -> {
									activity.set_status("Waiting to connect...");
								});
							}
						}
						if (key != null && !key.reset()) {
							Log.w("tildefriends", "watcher is no longer valid");
							break;
						}
					}
				} catch (java.io.IOException e) {
					Log.w("tildefriends", "IOException: " + e.toString());
				} catch (InterruptedException e) {
					Log.w("tildefriends", "InterruptedException: " + e.toString());
				}
			}
		});
		thread.start();

		set_status("Starting server...");
		String exe = getApplicationInfo().nativeLibraryDir + "/tildefriends.so";
		ProcessBuilder builder = new ProcessBuilder(exe, "run", "-z", getPackageResourcePath().toString(), "-a", "out_http_port_file=" + port_file_path, "-p", "0");
		Log.w("tildefriends", "files = " + getFilesDir().toString());
		Log.w("tildefriends", "exe = " + exe);
		builder.directory(getFilesDir());
		builder.inheritIO();
		try {
			process = builder.start();
		} catch (java.io.IOException e) {
			Log.w("tildefriends", "IOException starting process: " + e.toString());
		}

		web_view.getSettings().setJavaScriptEnabled(true);
		web_view.getSettings().setDatabaseEnabled(true);
		web_view.getSettings().setDomStorageEnabled(true);

		web_view.setDownloadListener(new DownloadListener() {
			public void onDownloadStart(String url, String userAgent, String content_disposition, String mime_type, long content_length) {
				Log.w("tildefriends", "Let's download: " + url + " (" + content_disposition + ")");
				String file_name = URLUtil.guessFileName(url, content_disposition, mime_type);
				if (url.startsWith("data:") && url.indexOf(',') != -1) {
					String b64 = url.substring(url.indexOf(',') + 1);
					byte[] data = Base64.decode(b64, Base64.DEFAULT);
					Log.w("tildefriends", "Downloaded " + data.length + " bytes.");
					File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
					try (OutputStream stream = new FileOutputStream(new File(path, file_name))) {
						stream.write(data);
					} catch (java.io.IOException e) {
						Log.w("tildefriends", "IOException: " + e.toString());
					}
					Toast.makeText(getApplicationContext(), "Downloaded File", Toast.LENGTH_LONG).show();
				} else {
					DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
					request.setMimeType(mime_type);
					String cookies = CookieManager.getInstance().getCookie(url);
					request.addRequestHeader("cookie", cookies);
					request.addRequestHeader("User-Agent", userAgent);
					request.setDescription("Downloading file...");
					request.setTitle(file_name);
					request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
					request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(url, content_disposition, mime_type));
					DownloadManager dm = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
					dm.enqueue(request);
					Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show();
				}
			}
		});

		web_view.setWebChromeClient(new WebChromeClient() {
			public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
				new AlertDialog.Builder(view.getContext())
					.setTitle("Tilde Friends")
					.setMessage(message)
					.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
					{
						public void onClick(DialogInterface dialog, int which)
						{
							result.confirm();
						}
					})
					.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
					{
						public void onClick(DialogInterface dialog, int which)
						{
							result.cancel();
						}
					})
					.create()
					.show();
				return true;
			}

			/*
			** https://stackoverflow.com/questions/5907369/file-upload-in-webview
			** https://stackoverflow.com/questions/8586691/how-to-open-file-save-dialog-in-android
			*/

			public boolean onShowFileChooser(WebView view, ValueCallback<Uri[]> message, WebChromeClient.FileChooserParams params) {
				upload_message = message;
				Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
				intent.addCategory(Intent.CATEGORY_OPENABLE);
				intent.setType("*/*");
				MainActivity.this.startActivityForResult(Intent.createChooser(intent, "File Chooser"), MainActivity.FILECHOOSER_RESULT);
				return true;
			}

			public boolean onConsoleMessage(android.webkit.ConsoleMessage consoleMessage) {
				Log.d("tildefriends", consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId());
				return true;
			}
		});

		web_view.setWebViewClient(new WebViewClient() {
			public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
			{
				if (request.getUrl() != null && request.getUrl().toString().startsWith(base_url)) {
					return false;
				} else {
					view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, request.getUrl()));
					return true;
				}
			}
		});

		TextView refresh = (TextView)findViewById(R.id.refresh);
		refresh.setVisibility(View.GONE);
		refresh.setText("REFRESH");
	}

	@Override
	protected void onDestroy()
	{
		if (process != null) {
			Log.w("tildefriends", "Killing process.");
			process.destroyForcibly();
			Log.w("tildefriends", "Process killed.");
			process = null;
		}
		super.onDestroy();
	}

	@Override
	protected void onSaveInstanceState(Bundle outState)
	{
		super.onSaveInstanceState(outState);
		web_view.saveState(outState);
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState)
	{
		super.onRestoreInstanceState(savedInstanceState);
		web_view.restoreState(savedInstanceState);
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent intent) {
		if (requestCode == FILECHOOSER_RESULT) {
			if (upload_message == null) {
				return;
			}
			upload_message.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
			upload_message = null;
		}
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK && web_view.canGoBack()) {
			web_view.goBack();
			return true;
		} else if (keyCode == KeyEvent.KEYCODE_FORWARD && web_view.canGoForward()) {
			web_view.goForward();
			return true;
		} else if (keyCode == KeyEvent.KEYCODE_REFRESH) {
			web_view.reload();
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {
		final int k_drag_distance = 160;
		switch (event.getActionMasked()) {
		case MotionEvent.ACTION_DOWN:
			touch_down_y = event.getY();
			web_view.clearOverscrolledY();
			break;
		case MotionEvent.ACTION_MOVE:
			{
				float delta = web_view.getOverscrolledY() ? event.getY() - touch_down_y : 0.0f;
				TextView refresh = (TextView)findViewById(R.id.refresh);
				LayoutParams layout = refresh.getLayoutParams();
				layout.height =
					Math.min(Math.max((int)delta, 0), k_drag_distance) +
					(delta > k_drag_distance ? (int)Math.sqrt(delta - k_drag_distance) : 0);
				refresh.setLayoutParams(layout);
				refresh.setVisibility(layout.height > 0 ? View.VISIBLE : View.GONE);
			}
			break;
		case MotionEvent.ACTION_UP:
			{
				float delta = web_view.getOverscrolledY() ? event.getY() - touch_down_y : 0.0f;
				if (delta > getWindow().getDecorView().getHeight() / 4) {
					web_view.reload();
				}
				TextView refresh = (TextView)findViewById(R.id.refresh);
				refresh.setVisibility(View.GONE);
			}
			break;
		}
		return super.dispatchTouchEvent(event);
	}

	private int read_port(String path) {
		try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
			return Integer.parseInt(reader.readLine());
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (java.io.FileNotFoundException e) {
			e.printStackTrace();
		} catch (java.io.IOException e) {
			e.printStackTrace();
		}
		return -1;
	}

	private void set_status(String text) {
		TextView text_view = (TextView)findViewById(R.id.text);
		web_view.setVisibility(View.GONE);
		text_view.setVisibility(View.VISIBLE);
		text_view.setText(text);
	}

	private void hide_status() {
		TextView text_view = (TextView)findViewById(R.id.text);
		web_view.setVisibility(View.VISIBLE);
		text_view.setVisibility(View.GONE);
	}
}
