using UnityEngine;
using System.Collections;
using System.IO.Ports;

public class ArduinoHandler : MonoBehaviour {

    SerialPort arduinoPort;

    public int COMPort = 4;
	
    public int[] motorvals;

    private byte[] motorData;

    private float timeSinceMotorUpdate = -2f;
    private float motorUpdateFreq = .1f;

    private float timeSinceInputUpdate = -2f;
    private float inputUpdateFreq = .05f;

    public Nunchuck Nunchuck;

    public int gyroVal = 0;
	
	public int expectedData = 0;
	
	private float playerDeathTime = -1.0F;
	
	private bool playerDead = false;
	
	
	Player player; 
	
	// Use this for initialization
	void Start () {
        arduinoPort = new SerialPort("COM" + COMPort, 9600, Parity.None, 8, StopBits.One);
		
		arduinoPort.Open();
		arduinoPort.Close();
        arduinoPort.Open();
		//arduinoPort.Write("1");

        arduinoPort.WriteTimeout = 300;
		arduinoPort.ReadTimeout = 100;
		
        motorvals = new int[] { 0, 0, 0, 0, 0 };

        motorData = new byte[] { (byte)'M' , 0, 0, 0, 0, 0 };

        Nunchuck = new Nunchuck();
		
		player = GameObject.FindWithTag("Player").GetComponent<Player>();
		
		player.init();
	}
	
	// Update is called once per frame
	void Update () {
		
		
		// Read in gyro and nunchuck data from the ArduinoHandler
		if (player && player.health <= 0) {
			
			PlayerIsDead();
			return;
		}
		
		
		// Write the motor values out to the Arduino
		if(Time.time < .5f) 
		{
			arduinoPort.Write(new byte[]{(byte)'M',255,255,255,255,255},0,motorData.Length);
		}
		else
		{
			timeSinceMotorUpdate += Time.deltaTime;
			if (timeSinceMotorUpdate > motorUpdateFreq)
			{

				for (int i = 0; i < motorvals.Length; i++)
				{
					motorData[i + 1] =  (byte)motorvals[i];
				}
				
				arduinoPort.Write(motorData, 0, motorData.Length);

				timeSinceMotorUpdate = 0;				
			}
		}

		timeSinceInputUpdate += Time.deltaTime;
        if (timeSinceInputUpdate > inputUpdateFreq)
		{
            UpdateAllData();
			
			timeSinceInputUpdate = 0;
		}

        //UpdateNunchuck();
        //UpdateGyro();
		
		// request bytes for nunchuck and gyro data
        //if(arduinoPort != null && arduinoPort.IsOpen) {
        //    try {
        //        arduinoPort.DiscardOutBuffer();
        //        arduinoPort.Write("N");
        //        arduinoPort.Write("G");
        //        expectedData += 2;
				
        //        ReadMessage();
				
        //    } catch (System.Exception e) {
        //        Debug.Log("EXCEPTION: " + e.StackTrace);
        //    }
        //}

        
	}

    private void UpdateAllData()
    {
        arduinoPort.Write("A");

        string readLine = arduinoPort.ReadLine();
		
		Debug.Log(readLine);
		
        if (readLine.StartsWith("S"))
        {
            if (Time.timeSinceLevelLoad > 2.5f)
            {
                arduinoPort.Close();
                Application.LoadLevel(0);
            }
            else
            {
                readLine = arduinoPort.ReadLine();
            }
        }
		
		if (readLine.StartsWith("A"))
		{
			Debug.Log(readLine);
			readLine = arduinoPort.ReadLine();
		}

        string[] readParams = readLine.Split(new char[] { ',' });

        UpdateGyro(readParams[0]);
        
		//Debug.Log(readLine.Remove(0, readParams[0].Length));
		
        UpdateNunchuck(readLine.Remove(0, readParams[0].Length + 1));
    }

    private void UpdateNunchuck(string retData)
    {
        //arduinoPort.DiscardInBuffer();
        //arduinoPort.Write("N");

        //string retData = arduinoPort.ReadLine();

        if (retData != null)
        {
            string[] chuckData = retData.Split(new char[] { ',' });

            if (chuckData.Length >= 7)
            {
                Nunchuck.JoyX = int.Parse(chuckData[0]);
                Nunchuck.JoyY = int.Parse(chuckData[1]);

                Nunchuck.AccelX = int.Parse(chuckData[2]);
                Nunchuck.AccelY = int.Parse(chuckData[3]);
                Nunchuck.AccelZ = int.Parse(chuckData[4]);

                Nunchuck.ButtonZ = int.Parse(chuckData[5]) == 0;
                Nunchuck.ButtonC = int.Parse(chuckData[6]) == 0;
            }
        }
    }

    private void UpdateGyro(string retData)
    {
        //arduinoPort.DiscardInBuffer();
        //arduinoPort.Write("G");

        //string retData = arduinoPort.ReadLine();

        gyroVal = int.Parse(retData);
    }
	
	// read messages from the Arduino, parse the message type, and take the appropriate action
	// messages sent from the Arduino should consist of a single character header byte, then 
	// any length of data terminated by a newline
	private void ReadMessage()
	{
        Debug.Log("Reading");
		while(expectedData > 0)
		{
			//expectedData--;
			char header = (char)arduinoPort.ReadChar();
            Debug.Log(header);
			switch(header)
			{
				case 'S':
					Debug.Log("S read");
					arduinoPort.ReadLine();
					if(Time.timeSinceLevelLoad > 1.5f) {
						arduinoPort.Close();
						Application.LoadLevel(0);
					}
					break;
				case 'N':
                    Debug.Log("N Read");
					UpdateNunchuck(arduinoPort.ReadLine());
                    expectedData--;
					break;
				case 'G':
                    Debug.Log("G Read");
					UpdateGyro(arduinoPort.ReadLine());
                    expectedData--;
					break;
				default:
					Debug.Log("Unknown Message Header: " + header + 
						" / Message: " + arduinoPort.ReadLine());
					break;
			}
		}

        //arduinoPort.DiscardInBuffer();
	}
	
	public void PlayerIsDead()
	{
		playerDead = true;
		try
		{
			if (playerDeathTime < 0) {
				
				playerDeathTime = Time.time;
				arduinoPort.Write("D");
			}
			
			if (Time.time - playerDeathTime > 5.0F) {
				
				arduinoPort.Close();
				Application.LoadLevel(1);
			}
		
			if((Time.time - playerDeathTime) % .3F < 0.05F) {
				
				arduinoPort.Write(new byte[]{(byte)'M',(byte)0,(byte)Random.Range(0,255),(byte)Random.Range(0,255),(byte)Random.Range(0,255),(byte)Random.Range(0,255)},0,motorData.Length);
			}
		
		} catch (System.Exception e) {
				
		}
	}
	
	
	private int count = 0;
	private bool up = true;
	public void WriteHealth(byte shiftLedMask, bool hp9, bool hp10)
	{
		if (!playerDead)
		{
			WriteLeds(shiftLedMask, hp9, hp10);
		}
		else
		{
			Debug.Log("DEAD");
			shiftLedMask = 0;
			hp9 = false;
			hp10 = false;
			if (count == 9)
			{
				hp9 = true;
			}
			else if (count == 10)
			{
				hp10 = true;
			}
			else
			{
				shiftLedMask |= (byte)((int)Mathf.Pow(2, count - 1));
			}
			
			if (count == 0 || count == 10)
				up = !up;
			
			
			if (up)
				count++;
			else
				count--;
			
			WriteLeds(shiftLedMask, hp9, hp10);
		}
	}
	
	private void WriteLeds(byte shiftLedMask, bool hp9, bool hp10)
	{
		byte[] bytesToWrite = new byte[] { (byte)'L', 0, 0, 0};
		
		bytesToWrite[1] = shiftLedMask;
		
		if (hp9)
			bytesToWrite[2] = 10;
		
		if (hp10)
			bytesToWrite[3] = 10;
		
		//Debug.Log(bytesToWrite[1]);
		//Debug.Log(bytesToWrite[2]);
		//Debug.Log(bytesToWrite[3]);
		
		arduinoPort.Write(bytesToWrite, 0, bytesToWrite.Length);
	}
	
	public void MonsterKilled()
	{
		arduinoPort.Write("K");
	}
	
	public void PlayerDamaged()
	{
		arduinoPort.Write("Z");
	}
}
