/* Copyright (c) 2008 -- CommonsWare, LLC

	 Licensed under the Apache License, Version 2.0 (the "License");
	 you may not use this file except in compliance with the License.
	 You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

	 Unless required by applicable law or agreed to in writing, software
	 distributed under the License is distributed on an "AS IS" BASIS,
	 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	 See the License for the specific language governing permissions and
	 limitations under the License.
*/
	 
package com.commonsware.tourit;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentReceiver;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.drawable.PaintDrawable;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationProvider;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Html;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewInflate;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.ViewFlipper;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

public class TourViewActivity extends ListActivity implements AdapterView.OnItemSelectedListener {
	public static final String CURRENT_WAYPOINT="current";
	private static final int MAP_ID = Menu.FIRST+2;
	private static final int EDIT_ID = Menu.FIRST+4;
	private static final int DELETE_ID = Menu.FIRST+5;
	private static final int CONFIG_ID = Menu.FIRST+6;
	private static final int HELP_ID = Menu.FIRST+100;
	private static final int CLOSE_ID = Menu.FIRST+8;
	private static final DecimalFormat distanceFormat=new DecimalFormat();
	private static final String ANIM="anim";
	private static final String ALERT="alert";
	protected static final String PROXIMITY_ALERT = new String("com.commonsware.tourit.intent.action.PROXIMITY_ALERT");
	protected final IntentFilter proximitylocationIntentFilter = new IntentFilter(PROXIMITY_ALERT);

	private Tour tour;
	private ViewFlipper detailsPanel;
	private View previousRow=null;
	private boolean first=true;
	private boolean isFlipping=false;
	private TextView waypoint_desc;
	private TextView waypoint_location;
	private TextView waypoint_elevation;
	private TextView waypoint_source;
	private TextView direction_net_ele;
	private TextView direction_distance;
	private ImageButton play_media;
	private TextView media_count;
	private Cursor c;
	private CheckBox alert;
	private CheckBox animate;
	private LocationProvider provider=null;
	private LocationManager myLocationManager=null;
	private List<Intent> proximityIntents=new ArrayList<Intent>();
	private ProximityIntentReceiver receiver=new ProximityIntentReceiver();
	private AtomicLong lastAlertSeen=new AtomicLong(-1L);
	private boolean alertSound=true;
	private boolean alertVibrate=true;
	private boolean alertInsistent=true;
	private Handler handler=new Handler() {
		@Override
		public void handleMessage(Message msg) {
			long tmp=lastAlertSeen.get();
			
			if (tmp>-1L && System.currentTimeMillis()-tmp>2000) {
				NotificationManager nm=(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
				
				nm.cancel(R.string.go_button);
				lastAlertSeen.set(-1L);
			}
		}
	};
	
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		setContentView(R.layout.view);
		
		myLocationManager=(LocationManager)getSystemService(Context.LOCATION_SERVICE);
		
		waypoint_desc=(TextView)findViewById(R.id.waypoint_desc);
		waypoint_location=(TextView)findViewById(R.id.waypoint_location);
		waypoint_elevation=(TextView)findViewById(R.id.waypoint_elevation);
		waypoint_source=(TextView)findViewById(R.id.waypoint_source);
		direction_net_ele=(TextView)findViewById(R.id.direction_net_ele);
		direction_distance=(TextView)findViewById(R.id.direction_distance);
		
		getListView().setDivider(new PaintDrawable(0xFF000000));
		getListView().setDividerHeight(1);
		getListView().setOnItemSelectedListener(this);
		
		detailsPanel=(ViewFlipper)findViewById(R.id.details);
		detailsPanel.setFlipInterval(4000);
		detailsPanel.setInAnimation(AnimationUtils.loadAnimation(this,
									R.anim.push_left_in));
		detailsPanel.setOutAnimation(AnimationUtils.loadAnimation(this,
									R.anim.push_left_out));
		stopAnimation();
		
		distanceFormat.setMinimumFractionDigits(1);
		distanceFormat.setMaximumFractionDigits(1);
		
		alert=(CheckBox)findViewById(R.id.alert);
		alert.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
			public void onCheckedChanged(CompoundButton buttonView,
											boolean isChecked) {
				if (isChecked) {
					enableAlerts();
				}
				else {
					disableAlerts();
				}
			}
		});
		
		animate=(CheckBox)findViewById(R.id.anim);
		animate.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
			public void onCheckedChanged(CompoundButton buttonView,
											boolean isChecked) {
				if (isChecked) {
					startAnimation();
				}
				else {
					stopAnimation();
				}
			}
		});
		
		if (icicle!=null) {
			alert.setChecked(icicle.getBoolean(ALERT));
			animate.setChecked(icicle.getBoolean(ANIM));
		}
	}
	
	@Override
	public void onResume() {
		super.onResume();
		
		try {
			if (c==null) {
				c=managedQuery(getIntent().getData(), Tour.PROJECTION, null, null, null);
			}
			else {
				c.requery();
			}
			
			c.first();
			tour=new Tour(getIntent().getData(), c);
			
			int sel=getSelectedItemPosition();
			
			setListAdapter(new RouteAdapter(tour, this));
			setSelection(sel);
			setTitle("TourIt! - "+tour.getTitle());
		}
		catch (Throwable t) {
			android.util.Log.e("TourIt", "Exception creating tour", t);
		}
		
		SharedPreferences prefs=getSharedPreferences(ConfigActivity.PREFS, 0);

		alertSound=prefs.getBoolean(ConfigActivity.ALERT_SOUND, true);
		alertVibrate=prefs.getBoolean(ConfigActivity.ALERT_VIBRATE, true);
		alertInsistent=prefs.getBoolean(ConfigActivity.ALERT_INSISTENT, true);
		
		String providerName=prefs.getString(ConfigActivity.LOCATION_PROVIDER, null);
		
		if (providerName!=null) {
			for (LocationProvider p : myLocationManager.getProviders()) {
				if (p.getName().equals(providerName)) {
					provider=p;
					break;
				}
			}
		}
		
		if (provider==null) {
			Criteria crit=new Criteria();
			
			crit.setCostAllowed(true);
			crit.setSpeedRequired(false);
			crit.setBearingRequired(false);
			crit.setAltitudeRequired(false);
			
			provider=myLocationManager.getBestProvider(crit);
		}
	}
	
	@Override
	public void onFreeze(Bundle icicle) {
		icicle.putBoolean(ANIM, animate.isChecked());
		icicle.putBoolean(ALERT, alert.isChecked());
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		menu.setQwertyMode(true);
		
		menu.add(0, MAP_ID, "View Map", R.drawable.map)
				.setAlphabeticShortcut('m');
		menu.add(0, EDIT_ID, "Update", R.drawable.edit)
				.setAlphabeticShortcut('u');
		menu.add(0, DELETE_ID, "Delete", R.drawable.delete)
				.setAlphabeticShortcut('e');
		menu.add(0, CONFIG_ID, "Config", R.drawable.configure);
		menu.add(0, HELP_ID, "Help", R.drawable.help)
				.setAlphabeticShortcut('h');
		menu.add(0, CLOSE_ID, "Close", R.drawable.eject)
				.setAlphabeticShortcut('c');

		return(super.onCreateOptionsMenu(menu));
	}

	@Override
	public boolean onOptionsItemSelected(Menu.Item item) {
		Intent i;
		
		switch (item.getId()) {
			case MAP_ID:
				i=new Intent(this, TourMapActivity.class);
				i.setData(tour.getUrl());
				i.putExtra(CURRENT_WAYPOINT, getSelectedItemPosition());
				startActivity(i);
				return(true);
				
			case EDIT_ID:
				startActivity(new Intent(Intent.EDIT_ACTION, tour.getUrl()));
				return(true);
				
			case CONFIG_ID:
				startActivity(new Intent(this, ConfigActivity.class));
				return(true);
				
			case DELETE_ID:
				delete();
				return(true);
				
			case HELP_ID:
				i=new Intent(this, HelpActivity.class);
				i.putExtra(HelpActivity.PAGE, "view");
				startActivity(i);
				return(true);
				
			case CLOSE_ID:
				disableAlerts();
				finish();
				return(true);
		}

		return(super.onOptionsItemSelected(item));
	}
	
	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		getListView().setSelection(position);
	}
	
	public void onItemSelected(AdapterView parent, View v, int position, long id) {
		if (first) {
			first=false;
		}
		else {
			detailsPanel.setVisibility(View.VISIBLE);
		}
		
		Waypoint pt=(Waypoint)parent.obtainItem(position);
		String desc=pt.getDescription();
		
		if (desc==null) {
			desc=getString(R.string.na);
		}
		
		waypoint_desc.setText(desc);
		
		Double lat=pt.getLatitude();
		Double lon=pt.getLongitude();
		
		if (lat==null || lon==null) {
			waypoint_location.setText(R.string.na);
		}
		else {
			String format=getString(R.string.location_format);
			Object[] parms={lat, lon};
			String simpleResult=String.format(format, parms);
			waypoint_location.setText(Html.fromHtml(simpleResult));
		}
		
		Double ele=pt.getElevation();
		
		if (ele==null) {
			waypoint_elevation.setText(R.string.na);
		}
		else {
			waypoint_elevation.setText(ele.toString());
		}
		
		String source=pt.getDataSource();
		
		if (source==null) {
			waypoint_source.setText(R.string.na);
		}
		else {
			waypoint_source.setText(source);
		}
		
		direction_net_ele.setText(R.string.na);
		
		Direction from=pt.getFromDirection();
		if (from!=null) {
			if (ele!=null) {
				Waypoint prev=from.getFromWaypoint();
				
				if (prev!=null) {
					Double prevEle=prev.getElevation();
					
					if (prevEle!=null) {
						double delta=pt.getElevation().doubleValue()-prev.getElevation().doubleValue();
						
						direction_net_ele.setText(new Double(delta).toString());
					}
				}
			}
			
			direction_distance.setText(distanceFormat.format(from.getDistance()));
		}
	}
	
	public void onNothingSelected(AdapterView parent) {
		stopAnimation();
		detailsPanel.setVisibility(View.INVISIBLE);
	}
	
	private void startAnimation() {
		detailsPanel.startFlipping();
		isFlipping=true;
	}
	
	private void stopAnimation() {
		detailsPanel.stopFlipping();
		isFlipping=false;
	}
	
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		if (keyCode==KeyEvent.KEYCODE_DPAD_LEFT || keyCode==KeyEvent.KEYCODE_DPAD_RIGHT) {
			stopAnimation();
			
			if (keyCode==KeyEvent.KEYCODE_DPAD_LEFT) {
				detailsPanel.setInAnimation(AnimationUtils.loadAnimation(this,
											R.anim.push_right_in));
				detailsPanel.setOutAnimation(AnimationUtils.loadAnimation(this,
											R.anim.push_right_out));

				if (detailsPanel.getDisplayedChild()==0) {
					detailsPanel.setDisplayedChild(detailsPanel.getChildCount()-1);
				}
				else {
					detailsPanel.setDisplayedChild(detailsPanel.getDisplayedChild()-1);
				}
			}
			else {
				detailsPanel.setInAnimation(AnimationUtils.loadAnimation(this,
											R.anim.push_left_in));
				detailsPanel.setOutAnimation(AnimationUtils.loadAnimation(this,
											R.anim.push_left_out));
				detailsPanel.showNext();
			}
		}
		
		return(super.onKeyUp(keyCode, event));
	}
	
	boolean isStripe(int position) {
		return((position % 2)==1);
	}
	
	private void delete() {
		AlertDialog.Builder builder=new AlertDialog.Builder(this);
		
		builder
			.setCancelable(false)
			.setMessage(R.string.delete_confirm)
			.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					// no-op
				}
			})
			.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					c.deleteRow();
					finish();
				}
			})
			.setTitle(R.string.delete_title);
		
		builder.show();
	}
	
	private void enableAlerts() {
		if (provider!=null) {
			registerReceiver(receiver, proximitylocationIntentFilter);
			
			for (Waypoint pt : tour.getRoute()) {
				Intent i=new Intent(PROXIMITY_ALERT);
				
				myLocationManager.addProximityAlert(pt.getLatitude(),
													pt.getLongitude(),
													100.0f,
													43200000,
													i); // 12 hours max
				
				proximityIntents.add(i);
			}
		}
	}
	
	private void disableAlerts() {
		if (provider!=null && proximityIntents.size()>0) {
			unregisterReceiver(receiver);
			
			for (Intent i : proximityIntents) {
				myLocationManager.removeProximityAlert(i);
			}
			
			proximityIntents.clear();
		}
	}
	 
	class ProximityIntentReceiver extends IntentReceiver {
		@Override
		public void onReceiveIntent(Context context, Intent intent) {
			long tmp=lastAlertSeen.getAndSet(System.currentTimeMillis());
			
			if (tmp==-1L) {
				showNotification();
				
				new Thread(new Runnable() {
					public void run() {
						try {
							for (int i=0;i<1000 && lastAlertSeen.get()>-1L;i++) {
								Thread.sleep(1000);
								handler.sendMessage(handler.obtainMessage());
							}
						}
						catch (Throwable t) {
							android.util.Log.e("TourIt", "Exception in intent thread", t);
						}
					}
				}).start();
			}
		}
		
		private void showNotification() {
			NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
			Notification notif = new Notification(
						 TourViewActivity.this,
						 R.drawable.wheel_16,
						 "Waypoint nearby!",
						 System.currentTimeMillis(),
						null, null, null,
						 R.drawable.wheel_16,
						 "TourIt!",
						null);
	
			// after a 100ms delay, vibrate for 250ms, pause for 100 ms and
			// then vibrate for 500ms.
			
			if (alertVibrate) {
				notif.vibrate = new long[] { 100, 250, 100, 500};
			}
			
			if (alertSound) {
				notif.sound=Uri.parse("android.resource://com.commonsware.tourit/"+R.raw.alert);
			}
			
			notif.insistent=alertInsistent;
			
			nm.notify(R.string.go_button, notif);
		}
	}
	
	class RouteAdapter extends ArrayAdapter {
		Tour tour;
		Activity context;
		
		RouteAdapter(Tour tour, Activity context) {
			super(context, R.layout.tourview_std, tour.getRoute());
			
			this.tour=tour;
			this.context=context;
		}
		
		public View getView(int position, View convertView, ViewGroup parent) {
			if (position==0) {
				return(getFirstView(convertView));
			}
			
			return(getSubsequentView(position, convertView));
		}
		
		private View getFirstView(View convertView) {
			ViewInflate inflater=context.getViewInflate();
			View view=inflater.inflate(R.layout.tourview_std, null, null);
			TextView label=(TextView)view.findViewById(R.id.waypoint);
			
			label.setText(tour.getRoute().get(0).getTitle());
			
			return(view);
		}
		
		private View getSubsequentView(int position, View convertView) {
			Waypoint pt=tour.getRoute().get(position);
			
			if (pt.getFromDirection().getHint()==null) {
				return(getStandardView(pt, isStripe(position), convertView));
			}
			
			return(getTwoLineView(pt, isStripe(position), convertView));
		}
		
		private View getStandardView(Waypoint pt, boolean stripe, View convertView) {
			ViewInflate inflater=context.getViewInflate();
			View view=inflater.inflate(R.layout.tourview_std, null, null);
			TextView distance=(TextView)view.findViewById(R.id.distance);
			ImageView turn=(ImageView)view.findViewById(R.id.turn);
			ImageView marker=(ImageView)view.findViewById(R.id.marker);
			TextView waypoint=(TextView)view.findViewById(R.id.waypoint);
			
			distance.setText(distanceFormat.format(pt.getCumulativeDistance()));
			turn.setImageResource(getResourceForTurn(pt.getFromDirection().getTurn()));
			
			if (pt.getFromDirection().getMarker()!=null) {
				marker.setImageResource(getResourceForMarker(pt.getFromDirection().getMarker()));
			}
			
			waypoint.setText(pt.getTitle());
			
			return(view);
		}
		
		private View getTwoLineView(Waypoint pt, boolean stripe, View convertView) {
			ViewInflate inflater=context.getViewInflate();
			View view=inflater.inflate(R.layout.tourview_2line, null, null);
			TextView distance=(TextView)view.findViewById(R.id.distance);
			ImageView turn=(ImageView)view.findViewById(R.id.turn);
			ImageView marker=(ImageView)view.findViewById(R.id.marker);
			TextView waypoint=(TextView)view.findViewById(R.id.waypoint);
			TextView hint=(TextView)view.findViewById(R.id.hint);
			
			distance.setText(distanceFormat.format(pt.getCumulativeDistance()));
			turn.setImageResource(getResourceForTurn(pt.getFromDirection().getTurn()));
			
			if (pt.getFromDirection().getMarker()!=null) {
				marker.setImageResource(getResourceForMarker(pt.getFromDirection().getMarker()));
			}

			waypoint.setText(pt.getTitle());
			hint.setText(pt.getFromDirection().getHint());
			
			return(view);
		}
		
		private int getResourceForTurn(String turn) {
			if (turn.equals("L")) {
				return(R.drawable.left_turn);
			}
			else if (turn.equals("R")) {
				return(R.drawable.right_turn);
			}
			else if (turn.equals("BL")) {
				return(R.drawable.bear_left);
			}
			else if (turn.equals("BR")) {
				return(R.drawable.bear_right);
			}
			
			return(R.drawable.go_straight);
		}
		
		private int getResourceForMarker(String marker) {
			if (marker.equals("SS")) {
				return(R.drawable.stop_sign);
			}
			else if (marker.equals("Y")) {
				return(R.drawable.yield_sign);
			}
				
			return(R.drawable.traffic_lights);
		}
	}
}