package com.example.PhidgetDictionaryExample;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.content.Context;

import com.phidget22.*;

import java.util.ArrayList;

public class DictionaryExample extends Activity {

	Manager manager;
	Dictionary dictionary;

	ArrayList<Phidget> channelList=new ArrayList<Phidget>();
	ArrayAdapter<Phidget> channelAdapter;

	ArrayList<String> keys=new ArrayList<String>();
	ArrayAdapter<String> keysAdapter;

	Toast errToast;

	/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        try
        {
        	//We're using a Manager here to list available dictionaries
			//so they can be selected from a list. If you already know the
			//dictionary you want to connect to, you don't need to use the Manager
        	manager = new Manager();
			dictionary = new Dictionary();
			//Allow direct USB connection of Phidgets
        	if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_HOST))
				com.phidget22.usb.Manager.Initialize(this);

			//Enable server discovery to list remote Phidgets
			this.getSystemService(Context.NSD_SERVICE);
			Net.enableServerDiscovery(ServerType.DEVICE_REMOTE);

			addManagerEvents();

			addDictionaryEvents();

			//Set up UI----------------------------------------

			//set up an array adapter to display all connected Phidget channels
			channelAdapter = new ArrayAdapter<Phidget>(this, android.R.layout.simple_list_item_2, android.R.id.text1, channelList) {
				@Override
				public View getView(int position, View convertView, ViewGroup parent) {
					View view = super.getView(position, convertView, parent);
					TextView text1 = (TextView) view.findViewById(android.R.id.text1);
					TextView text2 = (TextView) view.findViewById(android.R.id.text2);

					try {
						text1.setText(channelList.get(position).getChannelName());
						text2.setText(
								"Label: " + channelList.get(position).getDeviceLabel() +
								"\nSerial: " + String.valueOf(channelList.get(position).getDeviceSerialNumber()) +
								"\nServer: " + channelList.get(position).getServerName() +
								"\nHost: " + channelList.get(position).getServerHostname());
					} catch (PhidgetException pe) {
						pe.printStackTrace();
					}

					return view;
				}
			};

			//set up an array adapter to display all keys in the dictionary
			keysAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_2, android.R.id.text1, keys) {
				@Override
				public View getView(int position, View convertView, ViewGroup parent) {
					View view = super.getView(position, convertView, parent);
					TextView text1 = (TextView) view.findViewById(android.R.id.text1);
					TextView text2 = (TextView) view.findViewById(android.R.id.text2);

					try {
						text1.setText(keys.get(position));
						text2.setText(dictionary.get(keys.get(position)));
					} catch (PhidgetException pe) {
						pe.printStackTrace();
					}
					return view;
				}
			};

			((ListView)findViewById(R.id.deviceList)).setAdapter(channelAdapter);
			((ListView)findViewById(R.id.keyList)).setAdapter(keysAdapter);

			ListView lv = ((ListView)findViewById(R.id.deviceList));

			lv.setOnItemClickListener(new AdapterView.OnItemClickListener()  {
				@Override
				public void onItemClick(AdapterView<?> parent, View view, int position,
										long id) {
					try {
						//Address the dictionary we want to connect to, and open it
						Dictionary refDict = ((Dictionary)channelList.get(position));
						dictionary.setDeviceLabel(refDict.getDeviceLabel());
						dictionary.setDeviceSerialNumber(refDict.getDeviceSerialNumber());
						dictionary.setServerName(refDict.getServerName());

						dictionary.open(1000);

						updateKeys();

						((ListView)findViewById(R.id.deviceList)).setVisibility(View.GONE);
						((LinearLayout)findViewById(R.id.dictionaryLayout)).setVisibility(View.VISIBLE);
						((TextView)findViewById(R.id.dictionaryName)).setText(dictionary.getDeviceLabel());
					} catch (PhidgetException pe) {
						pe.printStackTrace();
					}
				}
			});

			//Set up the button click handlers
			Button addButton = (Button)findViewById(R.id.addButton);
			Button editButton = (Button)findViewById(R.id.editButton);
			Button removeButton = (Button)findViewById(R.id.removeButton);
			addButton.setOnClickListener(onClickListener);
			editButton.setOnClickListener(onClickListener);
			removeButton.setOnClickListener(onClickListener);

			manager.open();
        } catch (PhidgetException pe) {
	        pe.printStackTrace();
		}

    }

	public enum DictionaryActions {
		Add, Edit, Remove
	}

	private View.OnClickListener onClickListener = new View.OnClickListener() {
		@Override
		public void onClick(final View v) {
			switch(v.getId()){
				case R.id.addButton:
					showDialog(DictionaryActions.Add);
					break;
				case R.id.editButton:
					showDialog(DictionaryActions.Edit);
					break;
				case R.id.removeButton:
					showDialog(DictionaryActions.Remove);
					break;
			}
		}
	};

    private void updateKeys() {
    	try {
			String start = "";
			int length;

			keys.clear();
			do {
				String[] newKeys = dictionary.scan(start);
				for(int i = 0; i < newKeys.length; i++) {
					keys.add(newKeys[i]);
				}
				if(newKeys.length > 0)
					start = newKeys[newKeys.length - 1];
				length = newKeys.length;
			} while(length > 0);

			keysAdapter.notifyDataSetChanged();

		} catch (PhidgetException pe) {
			pe.printStackTrace();
		}
	}

	//Shows the dialog to get information to edit the dicitonary
    private void showDialog(final DictionaryActions function) {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);

		if(function == DictionaryActions.Add)
			builder.setTitle("Add");
		else if(function == DictionaryActions.Edit)
			builder.setTitle("Edit");
		else if(function == DictionaryActions.Remove)
			builder.setTitle("Remove");

		Context context = getApplicationContext();
		LinearLayout layout = new LinearLayout(context);
		layout.setOrientation(LinearLayout.VERTICAL);

		final EditText keyBox = new EditText(context);
		keyBox.setHint("Key");
		layout.addView(keyBox);

		//Remove doesn't require a value to remove the key
		final EditText valueBox = new EditText(context);
		if(function != DictionaryActions.Remove) {
			valueBox.setHint("Value");
			layout.addView(valueBox);
		}
		builder.setView(layout);

		// Set up the dialog buttons
		builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				String newKey = keyBox.getText().toString();
				String newValue = valueBox.getText().toString();
				try {
					if(function == DictionaryActions.Add)
						dictionary.add(newKey, newValue);
					else if(function == DictionaryActions.Edit)
						dictionary.update(newKey, newValue);
					else if(function == DictionaryActions.Remove)
						dictionary.remove(newKey);

					updateKeys();
				} catch (PhidgetException pe) {
					pe.printStackTrace();
				}
			}
		});

		builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				dialog.cancel();
			}
		});

		builder.show();
	}

	@Override
	public void onBackPressed()
	{
		if(((LinearLayout)findViewById(R.id.dictionaryLayout)).getVisibility() == View.VISIBLE) {
			((ListView)findViewById(R.id.deviceList)).setVisibility(View.VISIBLE);
			((LinearLayout)findViewById(R.id.dictionaryLayout)).setVisibility(View.GONE);
			((TextView)findViewById(R.id.dictionaryName)).setText("");
			keys.clear();
			try {
				dictionary.close();
			} catch (PhidgetException pe) {
				pe.printStackTrace();
			}
		} else
			super.onBackPressed();
	}

	//Adds event handlers for the Dictionary
	private void addDictionaryEvents() {

		dictionary.addAddListener(new DictionaryAddListener() {
			public void onAdd(final DictionaryAddEvent addEvent) {
				AddEventHandler handler = new AddEventHandler(addEvent);
				synchronized(handler)
				{
					runOnUiThread(handler);
					try {
						handler.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});

		dictionary.addUpdateListener(new DictionaryUpdateListener() {
			public void onUpdate(final DictionaryUpdateEvent addEvent) {
				UpdateEventHandler handler = new UpdateEventHandler(addEvent);
				synchronized(handler)
				{
					runOnUiThread(handler);
					try {
						handler.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});

		dictionary.addRemoveListener(new DictionaryRemoveListener() {
			public void onRemove(final DictionaryRemoveEvent addEvent) {
				RemoveEventHandler handler = new RemoveEventHandler(addEvent);
				synchronized(handler)
				{
					runOnUiThread(handler);
					try {
						handler.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
	}

	//Declare Dictionary event handlers to catch changes in the key-value pairs

	class AddEventHandler implements Runnable {
		DictionaryAddEvent ev;

		public AddEventHandler(DictionaryAddEvent ev) {
			this.ev = ev;
		}

		public void run() {
			String key = ev.getKey();
			String value = ev.getValue();
			keys.add(0, key);
			keysAdapter.notifyDataSetChanged();

			//notify that we're done
			synchronized (this) {
				this.notify();
			}
		}
	}

	class UpdateEventHandler implements Runnable {
		DictionaryUpdateEvent ev;

		public UpdateEventHandler(DictionaryUpdateEvent ev) {
			this.ev = ev;
		}

		public void run() {
			String key = ev.getKey();
			String value = ev.getValue();
			keys.remove(key);
			keys.add(0, key);
			keysAdapter.notifyDataSetChanged();

			//notify that we're done
			synchronized (this) {
				this.notify();
			}
		}
	}

	class RemoveEventHandler implements Runnable {
		DictionaryRemoveEvent ev;

		public RemoveEventHandler(DictionaryRemoveEvent ev) {
			this.ev = ev;
		}

		public void run() {
			String key = ev.getKey();
			keys.remove(key);
			keysAdapter.notifyDataSetChanged();

			//notify that we're done
			synchronized (this) {
				this.notify();
			}
		}
	}

	//Adds event handlers for the Manager
	public void addManagerEvents() {
		//Set up Manager events to detect available dictionaries
		manager.addAttachListener(new ManagerAttachListener() {
			public void onAttach(final ManagerAttachEvent attachEvent) {
				AttachEventHandler handler = new AttachEventHandler(attachEvent);
				synchronized(handler)
				{
					runOnUiThread(handler);
					try {
						handler.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});

		manager.addDetachListener(new ManagerDetachListener() {
			public void onDetach(final ManagerDetachEvent detachEvent) {
				DetachEventHandler handler = new DetachEventHandler(detachEvent);
				synchronized(handler)
				{
					runOnUiThread(handler);
					try {
						handler.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
	}

	//Declare Manager event handlers to list what dictionaries are available
	class AttachEventHandler implements Runnable {
		ManagerAttachEvent ev;

		public AttachEventHandler(ManagerAttachEvent ev) {
			this.ev = ev;
		}

		public void run() {
			Phidget ch = ev.getChannel();

			try {
				//Only display dictionary channels
				if(ch.getChannelClass() == ChannelClass.DICTIONARY) {
					channelList.add(0, ch);
					channelAdapter.notifyDataSetChanged();

					//A dictionary has been found, hide the loading symbol
					((ProgressBar)findViewById(R.id.loadingSymbol)).setVisibility(View.GONE);
				}

				//notify that we're done
				synchronized (this) {
					this.notify();
				}
			} catch (PhidgetException pe) {
				pe.printStackTrace();
			}
		}
	}

	class DetachEventHandler implements Runnable {
		ManagerDetachEvent ev;

		public DetachEventHandler(ManagerDetachEvent ev) {
			this.ev = ev;
		}

		public void run() {
			Phidget ch = ev.getChannel();

			try {
				if(ch.getChannelClass() == ChannelClass.DICTIONARY) {
					channelList.remove(ch);
					channelAdapter.notifyDataSetChanged();
				}

				//notify that we're done
				synchronized(this)
				{
					this.notify();
				}
			} catch (PhidgetException pe) {
				pe.printStackTrace();
			}
		}
	}

	class ErrorEventHandler implements Runnable {
		Phidget ch;
		ErrorEvent errorEvent;

		public ErrorEventHandler(Phidget ch, ErrorEvent errorEvent) {
			this.ch = ch;
			this.errorEvent = errorEvent;
		}

		public void run() {
			if (errToast == null)
				errToast = Toast.makeText(getApplicationContext(), errorEvent.getDescription(), Toast.LENGTH_SHORT);

			//replace the previous toast message if a new error occurs
			errToast.setText(errorEvent.getDescription());
			errToast.show();
		}
	}

    @Override
    protected void onDestroy() {
    	super.onDestroy();
    	try {
			manager.close();
			dictionary.close();
		} catch (PhidgetException e) {
			e.printStackTrace();
		}

		//Disable USB connection to Phidgets
		if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_HOST))
            com.phidget22.usb.Manager.Uninitialize();
    }

}
