package ca.cumulonimbus.barometernetwork;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;

import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.RemoteViews;
import ca.cumulonimbus.barometernetwork.PressureNetApplication.TrackerName;
import ca.cumulonimbus.pressurenetsdk.CbApiCall;
import ca.cumulonimbus.pressurenetsdk.CbObservation;
import ca.cumulonimbus.pressurenetsdk.CbScience;
import ca.cumulonimbus.pressurenetsdk.CbService;
import ca.cumulonimbus.pressurenetsdk.CbSettingsHandler;

import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;

public class WidgetButtonService extends Service {
	
	private double mReading = 0.0;
	SensorManager sm;
	
	boolean running = false;
	
	boolean mIsBound = false;
	
	public static final String PREFS_NAME = "ca.cumulonimbus.barometernetwork_preferences";
	PressureUnit mUnit = new PressureUnit("mbar");
	
	private String mAppDir = "";

	CbSettingsHandler activeSettings;
	ArrayList<CbObservation> listRecents = new ArrayList<CbObservation>();
	
	private Intent mIntent;
		
	// PressureNet 4.0
	// SDK communication
	boolean mBound;
	private Messenger mMessenger = new Messenger(new IncomingHandler());
	Messenger mService = null;
	
	public void unBindCbService() {
		if (mBound) {
			unbindService(mConnection);
			mBound = false;
		}
	}
	
	/**
	 * Get the settings from the Cb database
	 */
	private void askForSettings() {
		if (mBound) {
			log("asking for settings");

			Message msg = Message.obtain(null, CbService.MSG_GET_SETTINGS,
					0, 0);
			try {
				msg.replyTo = mMessenger;
				mService.send(msg);
			} catch (RemoteException e) {
				//e.printStackTrace();
			}
		} else {
			log("widget error: not bound, cannot ask for settings");
		}
	}
	
	private void sendSingleObservation() {
		if (mBound) {
		
			// Get tracker.
			Tracker t = ((PressureNetApplication) getApplication()).getTracker(
			    TrackerName.APP_TRACKER);
			// Build and send an Event.
			t.send(new HitBuilders.EventBuilder()
			    .setCategory(BarometerNetworkActivity.GA_CATEGORY_MAIN_APP)
			    .setAction(BarometerNetworkActivity.GA_ACTION_BUTTON)
			    .setLabel("small_widget_sending_single_observation")
			    .build());
			
			log("widget sending single observation");
			Message msg = Message.obtain(null, CbService.MSG_SEND_OBSERVATION, 0, 0);
			try {
				msg.replyTo = mMessenger;
				mService.send(msg);
			} catch (RemoteException e) {
				log("widget cannot send single obs, " + e.getMessage());
			}
		} else {
			log("widget failed to send single obs; data management error: not bound");
		}
	}

	
	
	@Override
	public void onRebind(Intent intent) {
		log("widget onrebind");
		askForLocalRecents(3);
		
		askForSettings();
		super.onRebind(intent);
	}

	public void bindCbService() {
		log("widget bind cbservice");
		bindService(new Intent(getApplicationContext(), CbService.class),
				mConnection, Context.BIND_AUTO_CREATE);

	}
	
	/**
	 * Handle communication with CbService. Listen for messages
	 * and act when they're received, sometimes responding with answers.
	 * 
	 * @author jacob
	 *
	 */
	class IncomingHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case CbService.MSG_SETTINGS:
				activeSettings = (CbSettingsHandler) msg.obj;
				if (activeSettings != null) {
					log("widgetbuttonservice got settings, share level " + activeSettings.getShareLevel());
					log("Client Received from service "
							+ activeSettings.getServerURL());
				} else {
					log("settings null");
				}
				break;
			case CbService.MSG_LOCAL_RECENTS:
				ArrayList<CbObservation> recents = (ArrayList<CbObservation>) msg.obj;
				RemoteViews remoteView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.small_widget_layout);
				CbObservation mostRecent = new CbObservation();
				if(recents == null) {
					break;
				}
				if(recents.size() == 0) {
					remoteView.setTextViewText(R.id.widgetSmallText, "No data");
					AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
					ComponentName component = new ComponentName(getApplicationContext().getPackageName(), WidgetProvider.class.getName());    
					appWidgetManager.updateAppWidget(component, remoteView);
					break;
				}
				try {
					mReading = recents.get(recents.size() - 1).getObservationValue();
					mostRecent = recents.get(recents.size() - 1);
				} catch (NullPointerException npe ) {
					break;
				}
				log("widget msg_local_recents received " + recents.size() + " mreading " + mReading);
				DecimalFormat df = new DecimalFormat("####.#");
				String message = "0.00";
				if(mReading>1) {
					message = df.format(mReading);
				} else {
					 try {
						 message = mIntent.getStringExtra("msg");
						 //Toast.makeText(getApplicationContext(), "msg: " + msg, Toast.LENGTH_SHORT).show();
					 } catch(NullPointerException e) {
						 log("widget tried mintent getstringextra, failed");
					 }
				}
				
				try {
					
					// TODO: fix ugly localization hack
					if(message.contains(",")) {
						message = message.replace(",", ".");
					}
					
					if(Double.parseDouble(message) != 0) {
						// send the reading
			
						// This is messy. Fix it.
						SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
			    		String abbrev = settings.getString("units", "mbar"); 
			    		mUnit = new PressureUnit(abbrev);
			    		double val = Double.valueOf(message);
			    		
			    		if(settings.getBoolean("mslp", false)) {
			    			val = CbScience.estimateMSLP(val, mostRecent.getLocation().getAltitude(), 15);
			    		}
			    		
			    		mUnit.setValue(val);
			    		String toPrint = mUnit.getDisplayText();
			    		toPrint = toPrint.replace(" ", "\n");
						
			    		sendSingleObservation();
			    		
			    		remoteView.setTextViewText(R.id.widgetSmallText, toPrint);
						
						try {
							String tendency = CbScience.findApproximateTendency(recents);
							
							log("widget getting tendency, updating and sending: " + tendency + " from " + recents.size() + " recents");
							
							if(tendency.contains("Rising")) {
								remoteView.setInt(R.id.widget_tendency_image_up, "setVisibility", View.VISIBLE);
								remoteView.setInt(R.id.widget_tendency_image_down, "setVisibility", View.GONE);
								remoteView.setInt(R.id.widget_tendency_image_steady, "setVisibility", View.GONE);
							} else if(tendency.contains("Falling")) {
								remoteView.setInt(R.id.widget_tendency_image_up, "setVisibility", View.GONE);
								remoteView.setInt(R.id.widget_tendency_image_down, "setVisibility", View.VISIBLE);
								remoteView.setInt(R.id.widget_tendency_image_steady, "setVisibility", View.GONE);
							} else if(tendency.contains("Steady")) {
								remoteView.setInt(R.id.widget_tendency_image_up, "setVisibility", View.GONE);
								remoteView.setInt(R.id.widget_tendency_image_down, "setVisibility", View.GONE);
								remoteView.setInt(R.id.widget_tendency_image_steady, "setVisibility", View.VISIBLE);
							} else {
								remoteView.setInt(R.id.widget_tendency_image_up, "setVisibility", View.INVISIBLE);
								remoteView.setInt(R.id.widget_tendency_image_down, "setVisibility", View.GONE);
								remoteView.setInt(R.id.widget_tendency_image_steady, "setVisibility", View.GONE);
								//remoteView.setInt(R.id.widgetSmallSubmitButton, "setImageResource", R.drawable.widget_button_drawable);
								//remoteView.setFloat(R.id.widgetSmallSubmitButton, "setImageResource", R.drawable.widget_button_drawable);
								//remoteView.setTextViewText(R.id.widgetSmallText, toPrint + "\n" + "--");
							}
			
						} catch(Exception e) {
							//log("oy! " + e.getMessage());
						}
						
						AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
						ComponentName component = new ComponentName(getApplicationContext().getPackageName(), WidgetProvider.class.getName());    
						appWidgetManager.updateAppWidget(component, remoteView);
					} else {
						//log("widget value is 0.0, didn't update");
						
					}
				
				} catch(Exception e) {
					// :(
				}
				break;
			case CbService.MSG_CHANGE_NOTIFICATION:
				String change = (String) msg.obj;
				// TODO: handle change notification
			default:
				break;
			}
		}
	}
	
	/**
	 * Communicate with CbService
	 */
	private ServiceConnection mConnection = new ServiceConnection() {
		public void onServiceConnected(ComponentName className, IBinder service) {
			log("widget client says : service connected");
			mService = new Messenger(service);
			mBound = true;
			Message msg = Message.obtain(null, CbService.MSG_OKAY);
			log("widget client received " + msg.arg1 + " " + msg.arg2);
			askForLocalRecents(3);
			
			askForSettings();
		}

		public void onServiceDisconnected(ComponentName className) {
			log("widget client: service disconnected");
			mMessenger = null;
			mBound = false;
		}
	};

		
	private void askForLocalRecents(int hoursAgo) {
		CbApiCall api = new CbApiCall();
		api.setMinLat(-90);
		api.setMaxLat(90);
		api.setMinLon(-180);
		api.setMaxLon(180);
		api.setStartTime(System.currentTimeMillis()
				- (hoursAgo * 60 * 60 * 1000));
		api.setEndTime(System.currentTimeMillis());

		Message msg = Message.obtain(null, CbService.MSG_GET_LOCAL_RECENTS,
				api);
		try {
			msg.replyTo = mMessenger;
			mService.send(msg);
		} catch (RemoteException e) {
			log("widget ask for local recents failed, " + e.getMessage());
		}

	}
	
	public void update(Intent intent, double reading) {
		log("widget binding to service (update call, reading " + reading + ")");
		mIntent = intent;
		bindCbService();
		// TODO: clean this up, (sometimes runs twice?)
		try {
			askForLocalRecents(3);
		} catch(Exception e) {
			log("no recents, exception");
			//e.printStackTrace();
		}
			
	}
	
	@Override
	public void onDestroy() {
	    super.onDestroy();
	 }
	
	@Override
	public void onStart(Intent intent, int startId) {
		try {
			mAppDir = intent.getStringExtra("appdir"); 
			
		} catch(Exception e) {
			
		}
		
		
		update(intent,0.1);
		
		super.onStart(intent, startId);
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
	

	// Log data to SD card for debug purposes.
	// To enable logging, ensure the Manifest allows writing to SD card.
	public void logToFile(String text) {
		try {
			OutputStream output = new FileOutputStream(mAppDir + "/log.txt", true);
			String logString = (new Date()).toString() + ": " + text + "\n";
			output.write(logString.getBytes());
			output.close();
			
		} catch(FileNotFoundException e) {
			
		} catch(IOException ioe) {
			
		}
	}
	
    public void log(String text) {
    	if(PressureNETConfiguration.DEBUG_MODE) {
    		System.out.println(text);
    		//logToFile(text);
    	}
    }
}