package cc.makeblock.makeblock;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.util.ArrayList;

import cc.makeblock.modules.MeModule;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;

public class UpgradeFirm {

static byte STK_OK              = 0x10;
static byte STK_FAILED          = 0x11;  // Not used
static byte STK_UNKNOWN         = 0x12;  // Not used
static byte STK_NODEVICE        = 0x13;  // Not used
static byte STK_INSYNC          = 0x14;  // ' '
static byte STK_NOSYNC          = 0x15;  // Not used
static byte ADC_CHANNEL_ERROR   = 0x16;  // Not used
static byte ADC_MEASURE_OK      = 0x17;  // Not used
static byte PWM_CHANNEL_ERROR   = 0x18;  // Not used
static byte PWM_ADJUST_OK       = 0x19;  // Not used
static byte CRC_EOP             = 0x20;  // 'SPACE'
static byte STK_GET_SYNC        = 0x30;  // '0'
static byte STK_GET_SIGN_ON     = 0x31;  // '1'
static byte STK_SET_PARAMETER   = 0x40;  // '@'
static byte STK_GET_PARAMETER   = 0x41;  // 'A'
static byte STK_SET_DEVICE      = 0x42;  // 'B'
static byte STK_SET_DEVICE_EXT  = 0x45;  // 'E'
static byte STK_ENTER_PROGMODE  = 0x50;  // 'P'
static byte STK_LEAVE_PROGMODE  = 0x51;  // 'Q'
static byte STK_CHIP_ERASE      = 0x52;  // 'R'
static byte STK_CHECK_AUTOINC   = 0x53;  // 'S'
static byte STK_LOAD_ADDRESS    = 0x55;  // 'U'
static byte STK_UNIVERSAL       = 0x56;  // 'V'
static byte STK_PROG_FLASH      = 0x60;  // '`'
static byte STK_PROG_DATA       = 0x61; // 'a'
static byte STK_PROG_FUSE       = 0x62;  // 'b'
static byte STK_PROG_LOCK       = 0x63;  // 'c'
static byte STK_PROG_PAGE       = 0x64;  // 'd'
static byte STK_PROG_FUSE_EXT   = 0x65;  // 'e'
static byte STK_READ_FLASH      = 0x70;  // 'p'
static byte STK_READ_DATA       = 0x71;  // 'q'
static byte STK_READ_FUSE       = 0x72;  // 'r'
static byte STK_READ_LOCK       = 0x73;  // 's'
static byte STK_READ_PAGE       = 0x74;  // 't'
static byte STK_READ_SIGN       = 0x75;  // 'u'
static byte STK_READ_OSCCAL     = 0x76;  // 'v'
static byte STK_READ_FUSE_EXT   = 0x77;  // 'w'
static byte STK_READ_OSCCAL_EXT = 0x78;  // 'x'

static byte CAT_SETADDR = 0x41; // 'A'
static byte CAT_WRITE = 0x42; // 'B'
static byte CAT_QUIT = 0x45; // 'E'

static byte QUERY_HW_VER = (byte) 0x80;
static byte QUERY_SW_MAJOR = (byte) 0x81;
static byte QUERY_SW_MINOR = (byte) 0x82;

static byte DOWNLOAD_SENDADDR = (byte) 0xE0;
static byte DOWNLOAD_SENDCODE = (byte) 0xE1;

static int ST_NULLDEVICE = 0;
static int ST_PROBING = 1;
static int ST_FOUND = 2;
static int ST_DOWNLOADING = 3;
static int ST_READ = 4;

Context context;
byte[] hex;

byte prevCmd;
int state = ST_NULLDEVICE;
int ARDUINO_PAGE_SIZE=128;
int hexLen = 0;
int hexPtr = 0;
	public UpgradeFirm(Context context){
		this.context=context;
		hex = new byte[64*1024]; // the hex file is about 57k, and rom should be less than 32k
		state = ST_PROBING;
		loadFirm();
	}
	
	public int getDowningProcess(){
		return (int)((float)hexPtr*100/hexLen);
	}
	
	public byte[] getProbeCmd(){
		byte[] cmd = new byte[2];
		cmd[0] = STK_GET_SYNC;
		cmd[1] = CRC_EOP;
		//cmd[2] = STK_GET_PARAMETER;
		//cmd[3] = QUERY_HW_VER;
		//cmd[4] = CRC_EOP;
		
		prevCmd = STK_GET_SYNC;
		return cmd;
	}
	
	public int sendQuery()
	{
		
		return 0;
	}
	
	public int loadFirm()
	{
		// load hex file
        Resources res = context.getResources();
        InputStream in_s = res.openRawResource(R.raw.firmware);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in_s));
        try {
			String line = reader.readLine();
			parseHexLine(line);
			while (line != null) {
				line = reader.readLine();
				if(line==null)
					break;
				int ret = parseHexLine(line);
				if(ret>0) hexLen = ret;
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return 0;
	}
	
	public int startDownload()
	{
		// start downloading
        hexPtr = 0;
        state = ST_DOWNLOADING;
        prevCmd = STK_PROG_PAGE; // init ping-pong cmd of page upload
        
		return 0;
	}

	public int parseCmd(int[] c){
	    if(prevCmd==STK_GET_SYNC && state==ST_PROBING){
	    	if(c[0]==0x14 && c[1]==0x10){
	            // stop probing
	            state = ST_FOUND;
	            return 1;
	        }
	    }
	    
	    return 0;
	}
	
	public byte[] getHexPage(){
		byte[] ret;
		if(prevCmd == STK_PROG_PAGE){
			if(hexPtr>=hexLen){
				ret = new byte[2];
				ret[0] = STK_LEAVE_PROGMODE;
				ret[1] = CRC_EOP;
				prevCmd = STK_LEAVE_PROGMODE;
				return ret;
			}else{
				ret = new byte[4];
				int address = hexPtr/2;
				byte addrl = (byte) (address & 0xff);
			    byte addrh = (byte) ((address>>8) & 0xff);
				ret[0] = STK_LOAD_ADDRESS;
				ret[1] = addrl;
				ret[2] = addrh;
				ret[3] = CRC_EOP;
				prevCmd = STK_LOAD_ADDRESS;
				return ret;
			}
		}else if(prevCmd==STK_LOAD_ADDRESS){
			int len=((hexLen-hexPtr)>ARDUINO_PAGE_SIZE)?ARDUINO_PAGE_SIZE:hexLen-hexPtr;
			ret = new byte[4+len+1];
			byte lenl = (byte) (len & 0xff);
			byte lenh = (byte) ((len>>8) & 0xff);
			ret[0]=STK_PROG_PAGE;
			ret[1]=lenh;
			ret[2]=lenl;
			for(int i=0;i<len;i++){
				ret[4+i] = hex[hexPtr];
				hexPtr++;
			}
			ret[3]='F';
			ret[4+len] = ' '; // the last space of page
			prevCmd = STK_PROG_PAGE;
			return ret;
		}
		return null;
	}
	
	int parseHexLine(String str)
	{
		if(str.charAt(0)!=':') return -2;
		int cnt = Integer.parseInt(str.substring(1, 3),16);
		int addr = Integer.parseInt(str.substring(3, 7),16);
		int type = Integer.parseInt(str.substring(7, 9),16);
		if(type==1) return -1;
		int bias = 0;
		for(int i=9;i<(9+cnt*2);i+=2){
			String tmp = str.substring(i, i+2);
			int inte =  Integer.parseInt(tmp,16);
			hex[addr+bias] = (byte) inte;
			bias+=1;
		}
		
		return addr+cnt;
	}
	
	

}