package com.crcmike;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.File;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

public class crcMike extends Activity implements OnClickListener {
    private static final char[] HEX_CHARS = {'0', '1', '2', '3',
        									 '4', '5', '6', '7',
        									 '8', '9', 'a', 'b',
        									 'c', 'd', 'e', 'f',};	

    private static final String TAG = "crcMike";
	private String mResults = null;	
	Button buttonCalc;
	RadioButton radioC, radioF;
	EditText editText;

    // Need handler for callbacks to the UI thread
    final Handler mHandler = new Handler(){
    	
        public void handleMessage(Message msg) {

            Log.i(TAG, "Got an incoming message from the child thread - "  + (String)msg.obj);

            /*
             * Handle the message coming from the child thread.
             */
            Toast.makeText(getApplicationContext(), (String)msg.obj + "\n", Toast.LENGTH_LONG).show();
        }
    };	
    	

    // Create runnable for posting
    final Runnable mUpdateResults = new Runnable() {
        public void run() {
            Message msg = mHandler.obtainMessage();
            msg.obj = mHandler.getLooper().getThread().getName() + " says Hello";
            mHandler.sendMessage(msg);
            Log.i(TAG, "Send a message to the child thread - " + (String)msg.obj);

            updateResultsInUi();
        }
    };	
	
    /** Called when the activity is first created. */
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        Log.d(TAG, "starting...");

        // find views by id
		editText = (EditText)findViewById(R.id.editText);
        radioC = (RadioButton)findViewById(R.id.radioC);
        radioF = (RadioButton)findViewById(R.id.radioF);
        buttonCalc = (Button)findViewById(R.id.buttonCalc);

        // add listener
        buttonCalc.setOnClickListener(this);    

        editText.setText("32");

    }
	
	public void onClick(View v){			

	    switch(v.getId()){
	    
	    case R.id.buttonCalc:
	
	    	startLongRunningOperation();
	
			// do something when the button is clicked

//			if (editText.getText().length() == 0) {
//				Toast.makeText(getApplicationContext(), "Please enter a valid temperature!", Toast.LENGTH_LONG).show();
//				return;
//			}
			
			float inputValue = Float.parseFloat(editText.getText().toString());
			if (radioC.isChecked()) {
				editText.setText(String
						.valueOf(fToC(inputValue)));
			} else {
				editText.setText(String
						.valueOf(cToF(inputValue)));
			}		
			
			// switch the selected buttons
			if (radioF.isChecked()) {
				radioF.setChecked(false);
				radioC.setChecked(true);
			} else {
				radioF.setChecked(true);
				radioC.setChecked(false);
			}				

			// need to add List interface?
			
		default:		
			break;
	    }
	}

	protected void startLongRunningOperation() {
        // Fire off a thread to do some work that we shouldn't do directly in the UI thread
        Thread t = new Thread() {
            public void run() {
                mResults = crc_calc();
                mHandler.post(mUpdateResults);
            }
        };
        t.start();

        Log.i(TAG, "Main handler is bound to - " + mHandler.getLooper().getThread().getName());
	}
	
	
	
    private void updateResultsInUi() {
        // Back in the UI thread -- update our UI elements based on the data in mResults
    	Toast.makeText(getApplicationContext(), mResults, Toast.LENGTH_LONG).show();
    	Log.d("huh", "mResults: " + mResults);
    	Log.i("huh", "updateUI - " + (String)mHandler.getLooper().getThread().getName());
    }
	
    public String crc_calc() {
    	final String INNER_TAG = "checksum";

    	byte[] buf = new byte[65536];
	    int num_read;
		String result = null;
		Message toMain = mHandler.obtainMessage();
		
	    // the external storage is assumed as connected here!
		String FILENAME = "test.log"; //hard coded filename!			
		File path = Environment.getExternalStoragePublicDirectory(
				Environment.DIRECTORY_DOWNLOADS);
		File file = new File(path,FILENAME);
	   
	    try{
			/*
			 *  testing the MD5 hash computation.
			 */
			java.security.MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
			InputStream in = new FileInputStream(file);
			//InputStream in = new StringBufferInputStream("dasdadad");			
			while ((num_read = in.read(buf)) != -1) {
				  digest.update(buf, 0, num_read);
			}
			//Toast.makeText(getApplicationContext(), asHex(digest.digest()), Toast.LENGTH_LONG).show();
			
			result = asHex(digest.digest());
			toMain.obj = mHandler.getLooper().getThread().getName() + ": " + result;
			in.close();
			
		}catch (IOException e){
			//Toast.makeText(getApplicationContext(), "Error reading from " + file, Toast.LENGTH_LONG).show();
			toMain.obj = mHandler.getLooper().getThread().getName() + "Error reading from " + file.toString();
			Log.d(INNER_TAG, "IOException");				
		}catch (Exception e){
			//Toast.makeText(getApplicationContext(), e.toString() + " / ID = " + id, Toast.LENGTH_LONG).show();
			toMain.obj = mHandler.getLooper().getThread().getName() + "Exception";
			Log.d(INNER_TAG, "Exception");				
		}
		
		/*
		 * Send the processing result back to the main thread.
		 */
		//Message toMain = mMainHandler.obtainMessage();
		//toMain.obj = "This is " + this.getLooper().getThread().getName() +
		//    ".  Did you send me \"" + (String)msg.obj + "\"?";
		mHandler.sendMessage(toMain);
		Log.i(INNER_TAG, "Send a message to the main thread - " + (String)toMain.obj);
		
		return(result);
    }

	// Converts to celcius
	private float fToC(float fahrenheit) {
		return ((fahrenheit - 32) * 5 / 9);
	}

	// Converts to fahrenheit
	private float cToF(float celsius) {
		return ((celsius * 9) / 5) + 32;
	}

    /*
     * Turns array of bytes into string representing each byte as
     * unsigned hex number.
     * 
     * @param hash Array of bytes to convert to hex-string
     * @return Generated hex string
     */
    public static String asHex (byte hash[]) {
        char buf[] = new char[hash.length * 2];
        for (int i = 0, x = 0; i < hash.length; i++) {
            buf[x++] = HEX_CHARS[(hash[i] >>> 4) & 0xf];
            buf[x++] = HEX_CHARS[hash[i] & 0xf];
        }
        return new String(buf);
    }	
	
}



/***************************************/

PROBLEM:

As per,
http://developer.android.com/guide/appendix/faq/commontasks.html#threading

I was expecting this to cause the workload to operate in as a child thread, but instead, it seems to still be apart of the main thread?  Here's logcat output:

I/crcMike ( 2469): Main handler is bound to - main
I/checksum( 2469): Send a message to the main thread - main: 7933d0f2409ca27b510912b6a64527a3
I/crcMike ( 2469): Got an incoming message from the child thread - main: 7933d0f2409ca27b510912b6a64527a3
I/crcMike ( 2469): Send a message to the child thread - main says Hello
D/huh     ( 2469): mResults: 7933d0f2409ca27b510912b6a64527a3
I/huh     ( 2469): updateUI - main
I/crcMike ( 2469): Got an incoming message from the child thread - main says Hello

The program above displays and activity, on clicking a button (ignore the temp conversion) it computes the MD5 hash of a file on the SD card; the idea was to do this computation as a separate thread.

So, is this how a runnable thread should work?

Your assistance is much appreciated, thanks :)