package terse.a1;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import terse.vm.Pro;
import terse.vm.Pro.Num;
import terse.vm.Terp;
import terse.vm.Pro.Dict;
import terse.vm.Pro.Str;
import terse.vm.Pro.Vec;

import android.app.Activity;
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

public abstract class TerseActivity extends Activity {

	public static Terp terp;
	public static String terp_error;

	protected String saveMe;
	String path;
	String queryStr;
	boolean gotLongClick = false;

	static Pattern LINK_P = Pattern
			.compile("[|]link[|](/[-A-Za-z_0-9.:]*)[|]([^|]+)[|](.*)");

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		if (savedInstanceState != null
				&& savedInstanceState.containsKey("TerseActivity")) {
			saveMe = savedInstanceState.getString("TerseActivity");
		} else {
			saveMe = null;
		}

		if (this.terp == null) {
			try {
				this.terp = new AndroidTerp(true, "aaa", "aaa.txt");
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				terp_error = e.toString();
			}
		}
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		outState.putString("TerseActivity", saveMe);
	}

	public class AndroidTerp extends Terp {

		protected AndroidTerp(boolean loadPrelude, String imageName,
				String initFilename) throws IOException {
			super(loadPrelude, imageName, initFilename);
		}

		@Override
		public String say(String s, Object... objects) {
			String msg = fmt(s, objects);
			Log.i("Terse", msg);
			return msg;
		}

		@Override
		public void loadPrelude() throws IOException {
			loadReader(preludeReader());
		}

		@Override
		public FileInputStream openFileRead(String filename)
				throws FileNotFoundException {
			return openFileInput(filename);
		}

		@Override
		public FileOutputStream openFileWrite(String filename)
				throws FileNotFoundException {
			return openFileOutput(filename, Context.MODE_WORLD_READABLE
					| Context.MODE_WORLD_WRITEABLE);
		}

		@Override
		public FileOutputStream openFileAppend(String filename)
				throws FileNotFoundException {
			return openFileOutput(filename, Context.MODE_WORLD_READABLE
					| Context.MODE_WORLD_WRITEABLE | Context.MODE_APPEND);
		}
	}

	public InputStreamReader preludeReader() {
		InputStream is = getResources().openRawResource(R.raw.prelude);
		return new InputStreamReader(is);
	}

	public void viewPath(String path, String queryStr, Bundle extras,
			Bundle savedInstanceState) {
		this.path = path;
		this.queryStr = queryStr;

		LayoutParams widgetParams = new LayoutParams(
				ViewGroup.LayoutParams.FILL_PARENT,
				ViewGroup.LayoutParams.FILL_PARENT, 1.0f);

		// Url query.
		HashMap<String, String> query = new HashMap<String, String>();
		queryStr = queryStr == null ? "" : queryStr; // Don't be null.

		String[] parts = queryStr.split("&");
		for (String part : parts) {
			String[] kv = part.split("=", 2);
			if (kv.length == 2)
				try {
					query.put(kv[0], URLDecoder.decode(kv[1], "UTF-8"));
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return; // TODO
				}
		}
		if (extras != null) {
			for (String key : extras.keySet()) {
				query.put(key, extras.getString(key));
			}
		}

		String explain;
		if (terp_error != null) {
			explain = "terp_error = " + terp_error;
		} else {
			try {
				terp.say("Sending to terp: %s", path);
				final Dict d = terp.handleUrl(path, query);
				terp.say("Result from terp: %s", d);
				explain = "DEFAULT EXPLANATION:\n\n" + d.toString();

				Str TYPE = d.cls.terp.newStr("type");
				Str VALUE = d.cls.terp.newStr("value");
				Str TITLE = d.cls.terp.newStr("title");
				Str type = (Str) d.dict.get(TYPE);
				Pro value = d.dict.get(VALUE);
				Pro title = d.dict.get(TITLE);

				if (type.str.equals("list") && value instanceof Vec) {
					final ArrayList<Pro> v = ((Vec) value).vec;
					ArrayList<String> labels = new ArrayList<String>();
					for (int i = 0; i < v.size(); i++) {
						Pro item = v.get(i);
						String label = item instanceof Str ? ((Str) item).str
								: item.toString();
						if (item instanceof Vec && ((Vec) item).vec.size() == 2) {
							label = ((Vec) item).vec.get(0).toString();
							Matcher m = LINK_P.matcher(label);
							if (m.lookingAt()) {
								label = m.group(2) + " " + m.group(3);
							}
							label += "    ["
									+ ((Vec) item).vec.get(1).toString()
											.length() + "]";
						}
						labels.add(label);
					}

					ListView listv = new ListView(this);
					listv.setAdapter(new ArrayAdapter<String>(this,
							R.layout.list_item, labels));
					listv.setLayoutParams(widgetParams);
					listv.setTextFilterEnabled(true);

					listv.setOnItemClickListener(new OnItemClickListener() {
						public void onItemClick(AdapterView<?> parent,
								View view, int position, long id) {
							// When clicked, show a toast with the TextView text
							Toast.makeText(getApplicationContext(),
									((TextView) view).getText(),
									Toast.LENGTH_SHORT).show();
							if (v.get(position) instanceof Vec) {
								Vec pair = (Vec) v.get(position);
								if (pair.vec.size() == 2) {
									if (pair.vec.get(0) instanceof Str) {
										String[] words = ((Str) pair.vec.get(0)).str
												.split("\\|");
										Log.i("TT-WORDS",
												terp.arrayToString(words));
										if (words[1].equals("link")) {
											Uri uri = new Uri.Builder()
													.scheme("terse")
													.path(words[2]).build();
											Intent intent = new Intent(
													"android.intent.action.MAIN",
													uri);
											intent.setClass(
													getApplicationContext(),
													MainActivity.class);

											startActivity(intent);
										}
									}
								}
							}
						}
					});
					setContentView(listv);
					return;
				} else if (type.str.equals("edit") && value instanceof Str) {
					final EditText ed = new EditText(this);

					ed.setText(saveMe == null ? value.toString() : saveMe);

					ed.setInputType(InputType.TYPE_CLASS_TEXT
							| InputType.TYPE_TEXT_FLAG_MULTI_LINE
							| InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE
							| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
					ed.setLayoutParams(widgetParams);
					// ed.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22);
					ed.setTextAppearance(this, R.style.teletype);
					ed.setGravity(Gravity.TOP);
					ed.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
					ed.setVerticalFadingEdgeEnabled(true);
					ed.setVerticalScrollBarEnabled(true);
					ed.setOnKeyListener(new OnKeyListener() {
						public boolean onKey(View v, int keyCode, KeyEvent event) {
							// If the event is a key-down event on the "enter"
							// button
							// if ((event.getAction() == KeyEvent.ACTION_DOWN)
							// &&
							// (keyCode == KeyEvent.KEYCODE_ENTER)) {
							// // Perform action on key press
							// Toast.makeText(TerseActivity.this, ed.getText(),
							// Toast.LENGTH_SHORT).show();
							// return true;
							// }
							return false;
						}
					});

					Button btn = new Button(this);
					btn.setText("Save");
					btn.setOnClickListener(new OnClickListener() {
						public void onClick(View v) {
							// Perform action on clicks
							String text = ed.getText().toString();
							Toast.makeText(TerseActivity.this, text,
									Toast.LENGTH_SHORT).show();
							String action = stringAt(d, "action");
							String query = "";

							String f1 = stringAt(d, "field1"); 
							String v1 = stringAt(d, "value1");
							String f2 = stringAt(d, "field2"); 
							String v2 = stringAt(d, "value2");
							f1 = (f1==null) ? "f1null" : f1;
							v1 = (v1==null) ? "v1null" : v1;
							f2 = (f2==null) ? "f2null" : f2;
							v2 = (v2==null) ? "v2null" : v2;

							startTerseActivity(action, query, 
//									"field1", stringAt(d, "field1"), 
//									"value1", stringAt(d, "value1"), 
//									"field2", stringAt(d, "field2"), 
//									"value2", stringAt(d, "value2"),
									stringAt(d, "field1"), stringAt(d, "value1"),
									stringAt(d, "field2"), stringAt(d, "value2"),
									"text", text);
						}
					});

					LinearLayout linear = new LinearLayout(this);
					linear.setOrientation(LinearLayout.VERTICAL);
					linear.addView(btn);
					linear.addView(ed);
					setContentView(linear);
					return;

				} else if (type.str.equals("text") && value instanceof Str) {
					explain = "<<< " + title + " >>>\n\n" + value.toString();
					// Fall thru for explainv.setText(explain).
				} else if (type.str.equals("draw") && value instanceof Vec) {
					Vec v = ((Vec) value);
					DrawView dv = new DrawView(this, v.vec);
					dv.setLayoutParams(widgetParams);
					setContentView(dv);
					return;
				} else {
					explain = "Unknown page type: " + type.str
							+ "\n\n##############\n\n";
					explain += value.toString();
					// Fall thru for explainv.setText(explain).
				}

			} catch (Exception ex) {
				ex.printStackTrace();
				StringBuilder sb = new StringBuilder("CAUGHT Exception: " + ex
						+ "\n\n");
				StackTraceElement[] st = ex.getStackTrace();
				for (int i = 0; i < st.length; i++) {
					sb.append("  * ");
					sb.append(st[i]);
					sb.append("\n");
				}
				explain = sb.toString();
			}
		}

		TextView explainv = new TextView(this);
		explainv.setText(explain);
		explainv.setBackgroundColor(Color.BLACK);
		explainv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
		explainv.setTextColor(Color.YELLOW);
		// NOT IN 2.1: explainv.setTextIsSelectable(true);

		ScrollView scrollv = new ScrollView(this);
		scrollv.addView(explainv);
		setContentView(scrollv);
	}

	@Override
	public void setContentView(View view) {
		// view.setOnCreateContextMenuListener(this);
		super.setContentView(view);
	}

	class DrawView extends View {
		ArrayList<Pro> list;

		public DrawView(Context context, ArrayList<Pro> list) {
			super(context);
			this.list = list;
		}

		@Override
		protected void onDraw(Canvas canvas) {
			Paint green = new Paint();
			green.setColor(Color.GREEN);
			green.setStrokeWidth(2);

			Paint blue = new Paint();
			blue.setColor(Color.BLUE);
			blue.setStrokeWidth(1);

			Paint yellow = new Paint();
			yellow.setColor(Color.YELLOW);
			yellow.setStrokeWidth(1);
			yellow.setTextSize(12);

			for (int i = 0; i < list.size(); i++) {
				Pro p = list.get(i);
				String drawtype = stringAt(p, 0);
				if (drawtype.equals("line")) {
					float x1 = floatAt(p, 1);
					float y1 = floatAt(p, 2);
					float x2 = floatAt(p, 3);
					float y2 = floatAt(p, 4);
					canvas.drawLine(x1, y1, x2, y2, green);
				} else if (drawtype.equals("rect")) {
					float x1 = floatAt(p, 1);
					float y1 = floatAt(p, 2);
					float x2 = floatAt(p, 3) + x1;
					float y2 = floatAt(p, 4) + y1;
					canvas.drawRect(x1, y1, x2, y2, blue);
				} else if (drawtype.equals("text")) {
					float x1 = floatAt(p, 1);
					float y1 = floatAt(p, 2);
					String text = stringAt(p, 3);
					canvas.drawText(text, x1, y1, yellow);
				}
			}

			setOnTouchListener(new OnTouchListener() {
				@Override
				public boolean onTouch(View v, MotionEvent event) {
					int action = event.getAction();
					// NOT IN 2.1: int flags = event.getFlags();
					float x = event.getX();
					float y = event.getY();
					terp.say("TOUCH: %s %s %s", action, x, y);
					if (action == MotionEvent.ACTION_UP) {
						terp.say("ACTION_UP: %s %s %s", action, x, y);
						startTerseActivity(path, queryStr, "action", "up",
								"ex", Float.toString(x), "ey",
								Float.toString(y));
						return true;
					}
					return false;
				}
			});

			setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
					terp.say("CLICK");
				}
			});

			setOnLongClickListener(new OnLongClickListener() {
				@Override
				public boolean onLongClick(View v) {
					terp.say("LONG CLICK");
					gotLongClick = true;
					return false;
				}
			});
		}
	}

	void startTerseActivity(String actPath, String actQuery, String... extrasKV) {

		Uri uri = new Uri.Builder().scheme("terse").path(actPath)
				.encodedQuery(actQuery).build();
		Intent intent = new Intent("android.intent.action.MAIN", uri);
		intent.setClass(getApplicationContext(), MainActivity.class);
		for (int i = 0; i < extrasKV.length; i += 2) {
			intent.putExtra(extrasKV[i], extrasKV[i + 1]);
		}
		startActivity(intent);
	}

	static String stringAt(Pro p, String i) {
		if (!(p instanceof Dict))
			return null; // "?p?";
		Dict d = (Dict) p;
		Pro x = d.dict.get(d.terp().newStr(i));
		if (!(x instanceof Str))
			return null; // "?x?";
		return ((Str) x).str;
	}

	static String stringAt(Pro p, int i) {
		if (!(p instanceof Vec))
			return null; // "?p?";
		Vec v = (Vec) p;
		int sz = v.vec.size();
		if (sz <= i)
			return null; // "?sz?";
		Pro x = v.vec.get(i);
		if (!(x instanceof Str))
			return null; // "?x?";
		return ((Str) x).str;
	}

	static float floatAt(Pro p, int i) {
		if (!(p instanceof Vec))
			return -1;
		Vec v = (Vec) p;
		int sz = v.vec.size();
		if (sz <= i)
			return -1;
		Pro x = v.vec.get(i);
		if (!(x instanceof Num))
			return -1;
		return (float) ((Num) x).num;
	}

	// From the Note Pad example.
	/**
	 * Called when the user clicks the device's Menu button the first time for
	 * this Activity. Android passes in a Menu object that is populated with
	 * items.
	 * 
	 * Sets up a menu that provides the Insert option plus a list of alternative
	 * actions for this Activity. Other applications that want to handle notes
	 * can "register" themselves in Android by providing an intent filter that
	 * includes the category ALTERNATIVE and the mimeTYpe
	 * NotePad.Notes.CONTENT_TYPE. If they do this, the code in
	 * onCreateOptionsMenu() will add the Activity that contains the intent
	 * filter to its list of options. In effect, the menu will offer the user
	 * other applications that can handle notes.
	 * 
	 * @param menu
	 *            A Menu object, to which menu items should be added.
	 * @return True, always. The menu should be displayed.
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// // Inflate menu from XML resource
		// MenuInflater inflater = getMenuInflater();
		// inflater.inflate(R.menu.list_options_menu, menu);
		//
		// // Generate any additional actions that can be performed on the
		// // overall list. In a normal install, there are no additional
		// // actions found here, but this allows other applications to extend
		// // our menu with their own actions.
		// Intent intent = new Intent(null, getIntent().getData());
		// intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
		// menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, 0,
		// new ComponentName(this, NotesList.class), null, intent, 0, null);

		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		return super.onPrepareOptionsMenu(menu);
	}

}
