package ca.carsonbrown.android.runon;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.concurrent.LinkedBlockingDeque;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.media.AudioManager;
import android.net.Uri;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.provider.ContactsContract.PhoneLookup;
import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
import android.util.Log;

public class SpeakSmsService extends Service implements TextToSpeech.OnInitListener, OnUtteranceCompletedListener {
	
	private static final String TAG = "SpeakSmsService";
	
	private SharedPreferences mSharedPrefs;
	private TextToSpeech mTts;
	private LinkedBlockingDeque<String> mMessages;
    private int mTtsStatus;

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		int retValue = super.onStartCommand(intent, flags, startId);
		mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
		if (checkPreferences()) {
			String message = "";
			try {
				message = buildNotificationString(intent.getStringExtra("originatingAddress"), intent.getStringExtra("messageBody"));
			} catch (NullPointerException e) {
				//error in getting Intent's extra strings, cannot continue
				return retValue;
			}
            if (mMessages == null) {
                mMessages = new LinkedBlockingDeque<String>();
            }
            mMessages.add(message);
            if (mTts == null) {
                mTts = new TextToSpeech(this, this);
                mTts.setOnUtteranceCompletedListener(this);
            }
		}
		return retValue;
	}

    @Override
    public void onDestroy() {
        if (mTts != null) {
            mTts.stop();
            mTts.shutdown();
        }
        super.onDestroy();
    }

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

	/**
	 * Check if preferences allow this text message to be announced at all.
	 * @return true if allowed, false otherwise
	 */
	private boolean checkPreferences() {
		//check if the app is enabled at all
		if (mSharedPrefs.getBoolean("enable", true)) {
			//check for headset rule
			int policy = Integer.parseInt(mSharedPrefs.getString(getString(R.string.run_policy_key), "1"));
			AudioManager am = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
			boolean headsetEnabled = am.isWiredHeadsetOn() || am.isBluetoothA2dpOn();
			switch(policy) {
			case 1:	//only with headset
				return headsetEnabled;
			case 2: //only without headset
				return !headsetEnabled;
			case 3:
				return true;
			}
			return true;
		} else {
			return false;
		}
	}
	
	private String buildNotificationString(String originatingAddress, String messageBody) {
		String notificationString = "";
		notificationString += "New message ";
		
		if (!mSharedPrefs.getString("say_sender", "1").equals("3")) {
			notificationString += "from ";
			if (mSharedPrefs.getString("say_sender", "1").equals("1")) {
				notificationString += contactName(originatingAddress);
			} else {
				notificationString += originatingAddress;
			}
		}
		notificationString += ". ";
		
		if (mSharedPrefs.getBoolean("say_message", true)) {
			notificationString += messageBody;
		}
		
		return notificationString;
	}
	
	private String contactName(String number) {
		//short circuit for the Settings activity test message
		if (number.equals(getString(R.string.tts_test_number))) {
			return getString(R.string.tts_test_name);
		}
		/// number is the phone number
		Uri lookupUri = Uri.withAppendedPath(
		PhoneLookup.CONTENT_FILTER_URI, 
		Uri.encode(number));
		String[] mPhoneNumberProjection = { PhoneLookup._ID, PhoneLookup.NUMBER, PhoneLookup.DISPLAY_NAME };
		Cursor cur = getApplicationContext().getContentResolver().query(lookupUri,mPhoneNumberProjection, null, null, null);
		String displayName = "" + number;
		try {
		   if (cur.moveToFirst()) {
		      displayName = cur.getString(2);
		   }
		} finally {
            if (cur != null) {
               cur.close();
            }
		}
		return displayName;
	}

    private void speakMessageFromQueue() {
        String message = null;
        try {
            message = mMessages.pop();
        } catch (NoSuchElementException e) {
            //Nothing to do
        }
        if (mTtsStatus != TextToSpeech.ERROR && mTtsStatus != TextToSpeech.LANG_MISSING_DATA && mTtsStatus != TextToSpeech.LANG_NOT_SUPPORTED && message != null) {
            HashMap<String, String> params = new HashMap<String, String>();
            params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "" + message.hashCode());
            params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, "" + AudioManager.STREAM_NOTIFICATION);

            mTtsStatus = mTts.speak(message, TextToSpeech.QUEUE_FLUSH, params);
        }
    }

	@Override
	public void onInit(int status) {
        mTtsStatus =  status;
        if (status == TextToSpeech.SUCCESS) {
            Locale locale = null;
            //check if default locale works
            if (mSharedPrefs.getBoolean(getString(R.string.default_locale_key), false)) {
                locale = Locale.getDefault();
            } else {
                locale = Locale.US;
            }
            mTtsStatus = mTts.setLanguage(locale);
        }
        speakMessageFromQueue();


	}

    @Override
    public void onUtteranceCompleted(String uttId) {
        if (mMessages.size() == 0) {
            stopSelf();
        } else {
            speakMessageFromQueue();
        }
    }
}

@@ -1037,16 +1037,244 @@
 tatus;%0A%0A
+    @Override%0A    public void onCreate() %7B%0A        super.onCreate();%0A        mMessages = new LinkedBlockingDeque%3CString%3E();%0A        mTts = new TextToSpeech(this, this);%0A        mTts.setOnUtteranceCompletedListener(this);%0A    %7D%0A%0A
 %09@Overri
@@ -1803,304 +1803,31 @@
     
-if (mMessages == null) %7B%0A                mMessages = new LinkedBlockingDeque%3CString%3E();%0A            %7D%0A            mMessages.add(message);%0A            if (mTts == null) %7B%0A                mTts = new TextToSpeech(this, this);%0A                mTts.setOnUtteranceCompletedListener(this);%0A            %7D
+mMessages.add(message);
 %0A%09%09%7D
@@ -3046,17 +3046,16 @@
  message
- 
 %22;%0A%09%09%0A%09%09
@@ -3142,16 +3142,17 @@
 ing += %22
+ 
 from %22;%0A

Build the components needed for service lifecycle on onCreate