package com.matthewtamlin.spyglass.library.core;

import android.content.res.TypedArray;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.View;

import com.matthewtamlin.spyglass.library.default_adapters.DefaultAdapter;
import com.matthewtamlin.spyglass.library.handler_adapters.HandlerAdapter;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import static com.matthewtamlin.java_utilities.checkers.NullChecker.checkNotNull;
import static com.matthewtamlin.spyglass.library.core.AdapterUtil.getDefaultAdapter;
import static com.matthewtamlin.spyglass.library.core.AdapterUtil.getHandlerAdapter;
import static com.matthewtamlin.spyglass.library.core.AnnotationUtil.getDefaultAnnotation;
import static com.matthewtamlin.spyglass.library.core.AnnotationUtil.getHandlerAnnotation;

public class Spyglass {
	private View view;

	private TypedArray attrSource;

	private Spyglass(final Builder builder) {
		this.view = builder.view;

		this.attrSource = view.getContext().obtainStyledAttributes(
				builder.attributeSet,
				builder.styleableRes,
				builder.defStyleAttr,
				builder.defStyleRes);
	}

	public void applyAttributesTo(final View view) {
		checkNotNull(view, "Argument 'view' cannot be null.");
		checkMainThread();

		for (final Field f : view.getClass().getDeclaredFields()) {
			processField(f);
		}

		for (final Method m : view.getClass().getDeclaredMethods()) {
			processMethod(m);
		}
	}

	private void checkMainThread() {
		if (Looper.myLooper() != Looper.getMainLooper()) {
			throw new IllegalThreadException("Spyglasses must only be touched by the UI thread.");
		}
	}

	private void processField(final Field field) {
		field.setAccessible(true);
		
		final HandlerAdapter<?, Annotation> handlerAdapter = getHandlerAdapter(handlerAnnotation);
		final DefaultAdapter<?, Annotation> defaultAdapter = getDefaultAdapter(defaultAnnotation);

		if (getHandlerAnnotation(field) != null) {
			if (handlerAdapter.attributeValueIsAvailable(attrSource, handlerAnnotation)) {
				final Object value = handlerAdapter.getAttributeValue(
						attrSource,
						handlerAnnotation);

				// Assign value to field

			} else {
				if (getDefaultAnnotation(field) != null) {
					defaultAdapter.getDefault(
							defaultAnnotation,
							view.getContext());

				} else if (handlerAdapter.attributeIsMandatory(handlerAnnotation)) {
					// throw exception
				}
			}
		}
	}

	private void processMethod(final Method method) {
		method.setAccessible(true);

		final Annotation handlerAnnotation = getHandlerAnnotation(method);
		final Annotation defaultAnnotation = getDefaultAnnotation(method);

		final HandlerAdapter<?, Annotation> handlerAdapter = getHandlerAdapter(handlerAnnotation);
		final DefaultAdapter<?, Annotation> defaultAdapter = getDefaultAdapter(defaultAnnotation);

		if (handlerAnnotation != null && handlerAdapter != null) {
			if (handlerAdapter.attributeValueIsAvailable(attrSource, handlerAnnotation)) {
				final Object value = handlerAdapter.getAttributeValue(
						attrSource,
						handlerAnnotation);

				// Get extra arguments using @Use annotations

				// Call method with value

			} else {
				if (defaultAnnotation != null && defaultAdapter != null) {
					defaultAdapter.getDefault(
							defaultAnnotation,
							view.getContext());

					// Get extra arguments using @Use annotations

					// Call method with value

				} else if (handlerAdapter.attributeIsMandatory(handlerAnnotation)) {
					// throw exception
				}
			}
		}
	}

	public static Builder builder(final View view) {
		return new Builder(view);
	}

	public static class Builder {
		private View view;

		private int styleableRes[];

		private AttributeSet attributeSet;

		private int defStyleAttr;

		private int defStyleRes;

		private Builder(final View view) {
			this.view = checkNotNull(view, "Argument 'view' cannot be null.");
		}

		public void withStyleableResource(final int[] styleableRes) {
			this.styleableRes = styleableRes;
		}

		public void withAttributeSet(final AttributeSet attributeSet) {
			this.attributeSet = attributeSet;
		}

		public void withDefStyleAttr(final int defStyleAttr) {
			this.defStyleAttr = defStyleAttr;
		}

		public void withDefStyleRes(final int defStyleRes) {
			this.defStyleRes = defStyleRes;
		}

		public Spyglass build() {
			checkNotNull(styleableRes, new InvalidBuilderStateException("Unable to build " +
					"Spyglass without a styleable resource. Call method withStyleableRes(int[]) " +
					"before calling build()"));

			return new Spyglass(this);
		}
	}
}
@@ -1756,18 +1756,16 @@
 e);%0A
-%09%09
 %0A%09%09final
  Han
@@ -1760,34 +1760,16 @@
 %0A%09%09final
- HandlerAdapter%3C?,
  Annotat
@@ -1763,33 +1763,32 @@
 final Annotation
-%3E
  handlerAdapter 
@@ -1772,38 +1772,41 @@
 otation handlerA
-dapter
+nnotation
  = getHandlerAda
@@ -1795,38 +1795,56 @@
 on = getHandlerA
-dapter
+nnotation(field);%0A%0A%09%09if 
 (handlerAnnotati
@@ -1841,35 +1841,45 @@
 erAnnotation
-);%0A
+ != null) %7B%0A%09
 %09%09final 
 DefaultAdapt
@@ -1858,39 +1858,39 @@
 ull) %7B%0A%09%09%09final 
-Default
+Handler
 Adapter%3C?, Annot
@@ -1900,108 +1900,51 @@
 on%3E 
-defaultAdapter = getDefaultAdapter(defaultAnnotation);%0A%0A%09%09if (getHandlerAnnotation(field) != null) %7B
+handlerAdapter = getHandlerAdapter(field);%0A
 %0A%09%09%09
@@ -2176,50 +2176,196 @@
 %09%09%09%09
-if (getDefaultAnnotation(field) != null) %7B
+final Annotation defaultAnnotation = getDefaultAnnotation(field);%0A%0A%09%09%09%09if (defaultAnnotation != null) %7B%0A%09%09%09%09%09final DefaultAdapter%3C?, Annotation%3E defaultAdapter = getDefaultAdapter(field);%0A
 %0A%09%09%09

Un-inlined if conditions