package lib.morkim.mfw.ui;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Observable;
import java.util.Observer;

import lib.morkim.mfw.app.MorkimApp;
import lib.morkim.mfw.domain.Entity;
import lib.morkim.mfw.domain.Model;
import lib.morkim.mfw.task.ScheduledTask;
import lib.morkim.mfw.task.UiTaskObserver;

/**
 * {@link Viewable} controller. Handles Viewable communication with application data model.
 * Mainly runs background tasks on data model and sends notifications on data model changes needed
 * to update the Viewable.
 * This class stays alive even if the Viewable is destroyed due to rotation. Will be destroyed when
 * its Viewable is completely destroyed.
 * @param <V> The {@link Presenter} associated with the Viewable
 * @param <M> The {@link Model} for this application
 * @param <A> The {@link MorkimApp} application that extends Android {@link android.app.Application}
 */
public abstract class Controller<A extends MorkimApp<M, ?>, M extends Model, V extends UpdateListener> {

	private A morkimApp;
	protected Viewable<A, M, V, ?, ?> viewable;

	private V updateListener;
	private V emptyUpdateListener;
	private boolean isViewUpdatable;

	private boolean initializationTaskExecuted;

	public Controller(A morkimApp) {

		emptyUpdateListener = createEmptyViewableUpdate();
		this.morkimApp = morkimApp;
	}

	protected void onExtractExtraData(Bundle bundledData) {}

	public void attachViewable(Viewable<A, M, V, ?, ?> viewable) {
		this.viewable = viewable;

		updateListener = viewable.getUpdateListener();

		Bundle bundledData = viewable.getBundledData();
		if (bundledData != null)
			onExtractExtraData(bundledData);

		if (!initializationTaskExecuted) {
			executeInitializationTask();
			initializationTaskExecuted = true;
		}
	}

	protected abstract A createContext();

	protected void executeInitializationTask() {}

	protected A getAppContext() {
		return morkimApp;
	}

	protected M getModel() {
		return morkimApp.getModel();
	}

	protected void finish() {}
	
	protected void keepScreenOn(boolean keepOn) {
		viewable.keepScreenOn(keepOn);
	}
	
	public void onDialogPositive(String tag) {
		
	}
	
	public void onDialogNegative(String tag) {
		
	}

    Viewable getViewable() {
        return viewable;
    }

    protected Context getContext() {
        return viewable.getContext();
    }

	private Observer modelObserver = new Observer() {
		@Override
		public void update(final Observable observable, final Object data) {

			if (observable instanceof Entity)
				((Activity) viewable).runOnUiThread(new Runnable() {
					@Override
					public void run() {
						onModelUpdated((Entity) observable, data);
					}
				});
		}
	};

	protected <E extends Entity> void onModelUpdated(E observable, Object data) {}

	protected void watchEntity(Observable observable) {
		observable.addObserver(modelObserver);
	}

	protected <E extends Entity> void watchEntity(Observable observable, UiEntityObserver<E> observer) {
		observable.addObserver(observer);
	}

	protected void unwatchModel(Observable observable) {
		observable.deleteObserver(modelObserver);
	}

	protected <T extends ScheduledTask> void registerToTask(Class<T> task, UiTaskObserver<T> observer) {
		morkimApp.getTaskScheduler().register(task, observer);
	}

	protected <T extends ScheduledTask> void unregisterFromTask(Class<T> task, UiTaskObserver<T> observer) {
		morkimApp.getTaskScheduler().unregister(task, observer);
	}

	public void destroy() {

	}

	public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {

//		for (String permission : permissions)
//			viewable.getScreen().onPermissionRequestHandled(permission);
	}

	protected static boolean verifyPermission(int[] grantResults) {

		if (grantResults.length < 1)
			return false;

		for (int result : grantResults)
			if (result != PackageManager.PERMISSION_GRANTED)
				return false;

		return true;
	}

	public void bindViews() {

		synchronized (this) {
			isViewUpdatable = true;
			viewable.onBindViews();
		}

		onInitViews();
	}

	public void unbindViews() {

		synchronized (this) {
			isViewUpdatable = false;
		}
	}

	protected void onUnBindViews() {}

	protected abstract View getViewById(int id);

	protected void onInitViews() {

	}

	private V createEmptyViewableUpdate() {

		V instance = null;

		try {
			Class<V> cls = (Class<V>) Class.forName(getUpdateListenerClass().getName());

			InvocationHandler handler = new InvocationHandler() {
				@Override
				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
					System.out.println(method.getName());
					return null;
				}
			};

			instance = (V) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[] { cls }, handler);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		return instance;
	}

	protected abstract Class<V> getUpdateListenerClass();

	protected V getUpdateListener() {
		synchronized (this) {
			return (isViewUpdatable) ? updateListener : emptyUpdateListener;
		}
	}

	public abstract Activity getActivity();
}

@@ -318,35 +318,8 @@
 ble;
-%0Aimport java.util.Observer;
 %0A%0Aim
@@ -2537,523 +2537,83 @@
 %0A%09pr
-ivate Observer modelObserver = new Observer() %7B%0A%09%09@Override%0A%09%09public void update(final Observable observable, final Object data) %7B%0A%0A%09%09%09if (observable instanceof Entity)%0A%09%09%09%09((Activity) viewable).runOnUiThread(new Runnable() %7B%0A%09%09%09%09%09@Override%0A%09%09%09%09%09public void run() %7B%0A%09%09%09%09%09%09onModelUpdated((Entity) observable, data);%0A%09%09%09%09%09%7D%0A%09%09%09%09%7D);%0A%09%09%7D%0A%09%7D;%0A%0A%09protected %3CE extends Entity%3E void onModelUpdated(E observable, Object data) %7B%7D%0A%0A%09protected void watchEntity(Observable observable) %7B%0A%09%09observable.addObserver(modelObserver);%0A%09
+otected %3CE extends Entity%3E void onModelUpdated(E observable, Object data) %7B
 %7D%0A%0A%09
@@ -2759,24 +2759,43 @@
 %0A%0A%09protected
+ %3CE extends Entity%3E
  void unwatc
@@ -2795,21 +2795,22 @@
  unwatch
-Model
+Entity
 (Observa
@@ -2819,24 +2819,54 @@
 e observable
+, UiEntityObserver%3CE%3E observer
 ) %7B%0A%09%09observ
@@ -2889,14 +2889,9 @@
 ver(
-modelO
+o
 bser

refactor unwatch