package com.example.chaljaa;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbRequest;
import android.util.Log;

/**
 * (c) Neuxs-Computing GmbH Switzerland
 * @author Manuel Di Cerbo, 02.02.2012
 *
 */
@TargetApi(12)
public class UsbController extends Activity {

	private final Context mApplicationContext;
	private final UsbManager mUsbManager;
	private final IUsbConnectionHandler mConnectionHandler;
	private final int VID;
	private final int PID;
	protected static final String ACTION_USB_PERMISSION = "com.example.chaljaa.USB";

	/**
	 * Activity is needed for onResult
	 * 
	 * @param parentActivity
	 */
	public UsbController(Activity parentActivity,
			IUsbConnectionHandler connectionHandler, int vid, int pid) {
		mApplicationContext = parentActivity.getApplicationContext();
		mConnectionHandler = connectionHandler;
		mUsbManager = (UsbManager) mApplicationContext
				.getSystemService(Context.USB_SERVICE);
		VID = vid;
		PID = pid;
		init();
	}



	private void init() {
		enumerate(new IPermissionListener() {
			public void onPermissionDenied(UsbDevice d) {
				UsbManager usbman = (UsbManager) mApplicationContext
						.getSystemService(Context.USB_SERVICE);
				PendingIntent pi = PendingIntent.getBroadcast(
						mApplicationContext, 0, new Intent(
								ACTION_USB_PERMISSION), 0);
				mApplicationContext.registerReceiver(mPermissionReceiver,
						new IntentFilter(ACTION_USB_PERMISSION));
				usbman.requestPermission(d, pi);
			}
		});
	}

	public void stop() {
		mStop = true;
		synchronized (sSendLock) {
			sSendLock.notify();
		}
		try {
			if(mUsbThread != null)
				mUsbThread.join();
		} catch (InterruptedException e) {
			e(e);
			e.printStackTrace();
		}
		mStop = false;
		mLoop = null;
		mUsbThread = null;
		
		try{
			mApplicationContext.unregisterReceiver(mPermissionReceiver);
		}catch(IllegalArgumentException e){
			e.printStackTrace();
		};//bravo
	}

	private UsbRunnable mLoop;
	private Thread mUsbThread;

	private void startHandler(UsbDevice d) {
		ApplicationLog.log("handler mein aa gaye");
		if (mLoop != null) {
			mConnectionHandler.onErrorLooperRunningAlready();
			return;
		}
		mLoop = new UsbRunnable(d);
		mUsbThread = new Thread(mLoop);
		mUsbThread.start();
		
		ApplicationLog.log("thread k baad");
		
				
	}

	public void send(byte data) {
		mData = data;
		synchronized (sSendLock) {
			sSendLock.notify();
		}
	}

	private void enumerate(IPermissionListener listener) {
		ApplicationLog.log("enumerating");
		HashMap<String, UsbDevice> devlist = mUsbManager.getDeviceList();
		Iterator<UsbDevice> deviter = devlist.values().iterator();
		while (deviter.hasNext()) {
			UsbDevice d = deviter.next();
		/*	TextView t= (TextView) (findViewById(R.id.UsbData));
		       t.setText("HI");
		       */
			ApplicationLog.log("Found device: "
					+ String.format("%04X:%04X", d.getVendorId(),
							d.getProductId()));
		if (d.getVendorId() == VID && d.getProductId() == PID) {
				ApplicationLog.log("Device under: " + d.getDeviceName());
				if (!mUsbManager.hasPermission(d))
					listener.onPermissionDenied(d);
				else{
					startHandler(d);
					return;
				}
				break;
			} 

		}
		ApplicationLog.log("no more devices found");
		mConnectionHandler.onDeviceNotFound();
	}
	
	private class PermissionReceiver extends BroadcastReceiver {
		private final IPermissionListener mPermissionListener;

		public PermissionReceiver(IPermissionListener permissionListener) {
			mPermissionListener = permissionListener;
		}

		@Override
		public void onReceive(Context context, Intent intent) 
		{
			mApplicationContext.unregisterReceiver(this);
			ApplicationLog.log("in onReceive function");
			if (intent.getAction().equals(ACTION_USB_PERMISSION)) 
			{
				if (!intent.getBooleanExtra(
						UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
					mPermissionListener.onPermissionDenied((UsbDevice) intent
							.getParcelableExtra(UsbManager.EXTRA_DEVICE));
					ApplicationLog.log("permission denied");
				} else {
					ApplicationLog.log("Permission granted");
					UsbDevice dev = (UsbDevice) intent
							.getParcelableExtra(UsbManager.EXTRA_DEVICE);
					
					
					if (dev != null) {
						if (dev.getVendorId() == VID
								&& dev.getProductId() == PID) {
							ApplicationLog.log("device not null");
							startHandler(dev);// has new thread
						}
					} else {
						e("device not present!");
					}
					
				}
			}
		}

	}

	// MAIN LOOP
	private static final Object[] sSendLock = new Object[]{};//learned this trick from some google example :)
	//basically an empty array is lighter than an  actual new Object()...
	private boolean mStop = false;
	private byte mData = 0x00;
    private byte[] byteArrayBuffer = new byte[100];
    private ByteBuffer buffer;
	private class UsbRunnable implements Runnable 
	{
		private final UsbDevice mDevice;
	
		UsbRunnable(UsbDevice dev) 
		{
			mDevice = dev;
		}
	
		
		public void run()
		{
			//here the main USB functionality is implemented
            UsbInterface intf = mDevice.getInterface(0);
			UsbDeviceConnection conn = mUsbManager.openDevice(mDevice);
			if (!conn.claimInterface(intf, true)) 
			{
				ApplicationLog.log("in run(), no connection");
				return;
			}
			ApplicationLog.log("in run(), connection");
		/*	PackageManager pm = getPackageManager();
			Intent intent = pm.getLaunchIntentForPackage("com.example.package.SlickUSB2Serial.apk");
			startActivity(intent);
		
*/
			UsbEndpoint epIN = null;
			UsbEndpoint epOUT = null;
			
			UsbInterface usbIf = mDevice.getInterface(0);
			for (int i = 0; i < usbIf.getEndpointCount(); i++)
			{
				if (usbIf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_CONTROL) 
				{
					epOUT = usbIf.getEndpoint(i);
					
					ApplicationLog.log("control Endpoint " + i + "EndPoint is :: " + epOUT.getDirection());
				}
			}
			
			int retVal;
			
			 retVal = conn.controlTransfer(UsbConstants.USB_DIR_OUT, 0, 0, 0, null, 0, 0);// reset
             // mConnection.controlTransfer(0×40,
             // 0, 1, 0, null, 0,
             // 0);//clear Rx
			 ApplicationLog.log("Returned Values is :: Reset :: " + retVal);
			 retVal = conn.controlTransfer(0x40, 0, 2, 0, null, 0, 0);// clear Tx
			 ApplicationLog.log("Returned Values is :: Clear TX :: " + retVal);
			 retVal = conn.controlTransfer(0x40, 0x02, 0x0000, 0, null, 0, 0);// flow
			 ApplicationLog.log("Returned Values is :: Flow :: " + retVal);
                     // control
                     // none
			 retVal = conn.controlTransfer(0x40, 0x03,  0xC04E, 0, null, 0, 0);// baudrate
			 ApplicationLog.log("Returned Values is :: BaudRate :: " + retVal);
                     // 38400
			 retVal = conn.controlTransfer(0x40, 0x04, 0x0008, 0, null, 0, 0);// data bit
			 ApplicationLog.log("Returned Values is :: DataBit :: " + retVal);
                     // 8, parity
                     // none,
                     // stop bit
                     // 1, tx off	
			
			
/*			
			// uses channel 15 freq 2425Mhz
			conn.controlTransfer(0x128, 34, 0, 0, null, 0, 0);
			conn.controlTransfer(0x128, 32, 0, 0, new byte[] { (byte) 0x80,
			0x25, 0x00, 0x00, 0x00, 0x00, 0x08 }, 7, 0);
*/			
			/*		conn.controlTransfer(0x82, 34, 0, 0, null, 0, 0);

			byte[] buffer = new byte[]{ (byte) 0x80,0x25, 0x00, 0x00, 0x00, 0x00, 0x08 };

			conn.controlTransfer(0x82, 32, 0, 0, buffer , 10, 0);  //8N1, 9600 baud
*/
			
			
			byte counter=0;
		//conn.bulkTransfer(epOUT, new byte[]{msData}, 1, 0);
			
			usbIf = mDevice.getInterface(0);
			for (int i = 0; i < usbIf.getEndpointCount(); i++) 
			{
				ApplicationLog.log("EP: "
                        + String.format("0x%02X", usbIf.getEndpoint(i)
                                .getAddress()));
				ApplicationLog.log("Type:"+usbIf.getEndpoint(i).getType());
				if (usbIf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_CONTROL) 
				{
					ApplicationLog.log("control Endpoint " + i);
				}
				if (usbIf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_INT) {
					ApplicationLog.log("Bulk Endpoint " + i);
					ApplicationLog.log("direction: "+usbIf.getEndpoint(i).getDirection());
					if (usbIf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
					{
						epIN = usbIf.getEndpoint(i);
						ApplicationLog.log("USB_DIR_IN");
					}
					
				
//					TextView t= (TextView) (findViewById(R.id.UsbData));
	//		       t.setText((CharSequence) epIN);
		//	       ApplicationLog.log("Data: "+epIN.toString());
				}
				else ApplicationLog.log("no bulk");
				ApplicationLog.log("epIN: "+epIN.toString());
			}
			
			usbIf = mDevice.getInterface(1);
			for (int i = 0; i < usbIf.getEndpointCount(); i++)
			{
				if (usbIf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_CONTROL) 
				{
					ApplicationLog.log("control Endpoint " + i);
				}
			}
	
			for (;;) {// this is the main loop for transferring
/*				try {
                   Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
*/				
                // This is where it is meant to receive
 /*               for(int j=0;j<63;j++){ 
                	buffer[j]= 0;
                	ApplicationLog.log("print: " +buffer[j]);
                }
              ApplicationLog.log("buffer at first: " +buffer.toString());
         */       
                StringBuilder str = new StringBuilder();

                //ApplicationLog.log("bulk transfer : " + conn.bulkTransfer(epIN, buffer, 64, 6400));         
            
                ApplicationLog.log("buffer part 1: " +byteArrayBuffer.toString());
                int bufferMaxLength = epIN.getMaxPacketSize();
                
                UsbRequest usbRequest = new UsbRequest();
                usbRequest.initialize(conn, epIN);
                
                
                //added :: harsh vardhan
//                StringBuilder sb = new StringBuilder();
//                while(conn.bulkTransfer(epIN, buffer, 64, 6400) > 2){
//                    for(int i = 2; i < buffer.length; i++){
//                        sb.append((char) buffer[i]);
//                    }
//                }

                
/*               String s=new String();
               s=buffer.toString();
                for(int j=0;j<s.length();j++){
                	char c=s.charAt(j);
                	ApplicationLog.log("char: " +c);
                	
                }
                
                for(int j=0;j<63;j++){
                	
                	ApplicationLog.log("buffer part 2: " +buffer[j]);
                }*/
                
                if (conn.bulkTransfer(epIN, byteArrayBuffer, 64, 6400) > 0)
                {
                	ApplicationLog.log("bulk transfer is success");
                    for (int i = 2; i < 64; i++) {
                        if (byteArrayBuffer[i] != 0) {
                            str.append((char) byteArrayBuffer[i]);
                        } else {
                            ApplicationLog.log(str.toString());
                            break;
                        }
                  }

                }
                // this shows the complete string
                ApplicationLog.log(str.toString());
               
/*				synchronized (sSendLock) {//ok there should be a OUT queue, no guarantee that the byte is sent actually
					try {
						sSendLock.wait();
					} catch (InterruptedException e) {
						if (mStop) {
							mConnectionHandler.onUsbStopped();
							return;
						}
						e.printStackTrace();
					}
				}
				conn.bulkTransfer(epOUT, new byte[] { mData }, 1, 0);
*/	
				if (mStop) {
					mConnectionHandler.onUsbStopped();
					return;
				}
				 ApplicationLog.log("sent " + counter);
	                counter++;
	                counter = (byte) (counter % 16);
			}

		}
	}

	// END MAIN LOOP
	private BroadcastReceiver mPermissionReceiver = new PermissionReceiver(
			new IPermissionListener() 
			{
				public void onPermissionDenied(UsbDevice d)
				{
					ApplicationLog.log("Permission denied on " + d.getDeviceId());
				}
			});

	private static interface IPermissionListener {
		void onPermissionDenied(UsbDevice d);
	}

	public final static String TAG = "USBController";

	private void l(Object msg) {
		Log.d(TAG, ">==< " + msg.toString() + " >==<");
	}

	private void e(Object msg) {
		Log.e(TAG, ">==< " + msg.toString() + " >==<");
	}
}
