package com.inputstick.api;

import java.lang.ref.WeakReference;

import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;

public class IPCConnectionManager extends ConnectionManager {
	
	//private static final String mTag = "IPCConnectionManager";
	
	public static final int SERVICE_CMD_CONNECT = 1;
	public static final int SERVICE_CMD_DISCONNECT = 2;
	public static final int SERVICE_CMD_DATA = 3;
	public static final int SERVICE_CMD_STATE = 4;
	
    Context mCtx;
	Messenger mService = null;    
	boolean mBound;
	boolean initSent;
    final Messenger mMessenger = new Messenger(new IncomingHandler(this)); 
    
    private static class IncomingHandler extends Handler {    	
    	private final WeakReference<IPCConnectionManager> ref; 

    	IncomingHandler(IPCConnectionManager manager) { 
    		ref = new WeakReference<IPCConnectionManager>(manager); 
        }    	
    	
        @Override
        public void handleMessage(Message msg) {      
        	if (ref == null) return;
        	IPCConnectionManager manager = ref.get();
        	if (manager != null) {
	        	switch (msg.what) {     	
		    		case SERVICE_CMD_DATA:
		            	byte[] data = null;        	
		            	Bundle b = msg.getData();
		            	if (b != null) {
		            		data = b.getByteArray("data");
		            		manager.onData(data);
		            	}             	
		    			break;
		    		case SERVICE_CMD_STATE:
		    			manager.stateNotify(msg.arg1);
		    			break;             	
	        	}  
        	}
        }
    }     
    
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            mBound = true;                  
            sendConnectMessage();
        }

        public void onServiceDisconnected(ComponentName className) {
            // unexpectedly disconnected from service
            mService = null;
            mBound = false;
			mErrorCode = InputStickError.ERROR_ANDROID_SERVICE_DISCONNECTED;
			stateNotify(STATE_FAILURE);
            stateNotify(STATE_DISCONNECTED);
        }
    };  
    //SERVICE=========================================================            

	
	
	
	
	private void sendConnectMessage() {
        Bundle b = new Bundle();
        b.putLong("TIME", System.currentTimeMillis());
        sendMessage(SERVICE_CMD_CONNECT, 0, 0, b); 	
	}

    
    private void sendMessage(int what, int arg1, int arg2, Bundle b) {
		Message msg;
		try {
			msg = Message.obtain(null, what, arg1, 0, null);
			msg.replyTo = mMessenger;
			msg.setData(b);				
			mService.send(msg);
		} catch (Exception e) {
			e.printStackTrace();
		}    	
    }
    
    private void sendMessage(int what, int arg1, int arg2, byte[] data) {
    	Bundle b;
		b = new Bundle();
		b.putByteArray("data", data);
		sendMessage(what, arg1, arg2, b);
    }    
    
    private void sendMessage(int what, int arg1, int arg2) {
    	sendMessage(what, arg1, arg2, (Bundle)null);	
    }  	
	
    @Override
    protected void onData(byte[] data) {
		super.onData(data);
	}    		
	
	
	public IPCConnectionManager(Application app) {
		mCtx = app.getApplicationContext();
	}

	@Override
	public void connect() {
		PackageManager pm = mCtx.getPackageManager();
		boolean exists = true;
		try {
			pm.getPackageInfo("com.inputstick.apps.inputstickutility", PackageManager.GET_META_DATA);
		} catch (NameNotFoundException e) {
			exists = false;
		}		
		
		if (exists) {
			mErrorCode = InputStickError.ERROR_NONE;
			Intent intent = new Intent();									
			intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService"));
			intent.putExtra("TIME", System.currentTimeMillis());
			mCtx.startService(intent);
			mCtx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 
	        if (mBound) {
	        	//already bound
	        	sendConnectMessage();
	        } 
		} else {
			mErrorCode = InputStickError.ERROR_ANDROID_NO_UTILITY_APP;
			stateNotify(STATE_FAILURE);
			stateNotify(STATE_DISCONNECTED);
		}
	}

	@Override
	public void disconnect() {
		if (mBound) {
			sendMessage(SERVICE_CMD_DISCONNECT, 0, 0); 
			Intent intent = new Intent();		   
			intent.setComponent(new ComponentName("com.inputstick.apps.inputstickutility","com.inputstick.apps.inputstickutility.service.InputStickService"));	
			mCtx.unbindService(mConnection);
			mCtx.stopService(intent);			
			mBound = false;
			//service will pass notification message (disconnected)
		} else {
			//just set state, there is nothing else to do
			stateNotify(STATE_DISCONNECTED);
		}
	}
	
	@Override
	public void sendPacket(Packet p) {
		if ((mState == ConnectionManager.STATE_READY) || (mState == ConnectionManager.STATE_CONNECTED)) {			
			if (p.getRespond()) {
				sendMessage(IPCConnectionManager.SERVICE_CMD_DATA, 1, 0, p.getBytes());
			} else {
				sendMessage(IPCConnectionManager.SERVICE_CMD_DATA, 0, 0, p.getBytes());
			}
		}		
	}


}
