#include <avr/io.h>
#include <stdio.h>
#include <util/setbaud.h>
#include <util/delay.h>
#include "pins.h"
#include "debug.h"

/** UART notes
 * UCSR0A:
 *  0: MP communication mode
 *  1: U2X (double trans speed)
 *  2: USART parity error
 *  3: Data overrun (udr0 not read before next frame)
 *  4: Frame error
 *  5: Data register empty (new data can be transmitted)
 *  6: USART transmit complete
 *  7: USART recieve complete
 * ____________________________________________________________
 * UCSR0B:
 *  0: transmit data bit 8
 *  1: recieve data bit 8
 *  2: USART char size 0 (used with UCSZ01 and UCSZ00 to set data frame size)
 *  3: Transmitter enable
 *  4: Reciever  enable
 *  5: USART data reg empty interrupt table
 *  6: TX complete int enable
 *  7: RX complete int enable
 * ____________________________________________________________
 * UCSR0C:
 *  0: Clock polarity (1: falling edge trasmit, recieve rising edge)
 *  1: USART char size 0
 *  2: USART char size 1
 *  3: USART stop bit select (1: 1 stop bit, 0: 2 stop bits)
 *  4: USART parity mode (00: async, 01: sync)
 *  5: USART Parity mode (02: master SPI)
 *  6: USART mode select (00: async 01: sync)
 *  7: USART mode select (02: master SPI)
 * ____________________________________________________________
 * UBRR0(H,L): baudrates
 *
 *
 **/

void uart_debug_sendbyte(uint8_t byte) { 
  loop_until_bit_is_set(UCSR0A, UDRE0); 
  UDR0 = byte; 
}

uint8_t uart_debug_recvbyte() {
  loop_until_bit_is_set(UCSR0A, RXC0);
  UART_PORT |= _BV(UART_RTS); 
  return UDR0;
}

void uart_debug_init() {

  UBRR0H = UBRRH_VALUE;
  UBRR0L = UBRRL_VALUE;

/**
TODO figure out why USE_2X is enable when it shoudn't be
   //set baud rate
#ifdef USE_2X //is set by header
  UCSR0A |= _BV(U2X0);
#else
  UCSR0A &= ~(_BV(U2X0));
#endif
**/ 

  UCSR0A &= ~(_BV(U2X0));

  UCSR0C = ((_BV(UCSZ01)) | _BV(UCSZ00)); // set 8 bit char size, yes the '=' is intentional
  UCSR0B = (_BV(RXEN0) | _BV(TXEN0));

}

#ifdef FLASH_EEPROM
//this uart port recieves from the computer and writes the eeprom
void uart_flash_init() {
  //syncronization
  UART_DDR |= _BV(UART_RTS); 
  UART_DDR &= ~(_BV(UART_CTS)); //keep in mind cts/rts is inverted; low is 1 and high is 0
  UART_PORT |= _BV(UART_RTS); //keep in mind cts/rts is inverted; low is 1 and high is 0
  
  //for serial speed, we'll just use the same speed for now
  UBRR1H = UBRRH_VALUE;
  UBRR1L = UBRRL_VALUE;
  
  UCSR1A &= ~(_BV(U2X1));

  UCSR1C = ((_BV(UCSZ11)) | _BV(UCSZ10)); // set 8 bit char size, yes the '=' is intentional
  UCSR1B = (_BV(RXEN1) | _BV(TXEN1));

  //when the master initates serial via pyserial, for some reason it pulls our CTS low
  //for a brief moment. This hack avoids that
  printf("waiting for unwanted initation pulse...\r\n");
  loop_until_bit_is_clear(UART_PIN, UART_CTS);
  loop_until_bit_is_set(UART_PIN, UART_CTS);
  printf("complete\r\n");
}

void uart_flash_sendbyte(uint8_t byte) { 
  printf("send start\r\n");
  if(UART_PORT & _BV(UART_CTS)) {
    printf("send conflict!\r\n");
    debug_crash(); //make sure they're not trying to send
  }
  UART_PORT &= ~(_BV(UART_RTS)); //us: ready to send
  printf("waiting on master\r\n");
  loop_until_bit_is_clear(UART_PIN, UART_CTS); //them: go ahead
  printf("waiting for transmit buffer\r\n");
  loop_until_bit_is_set(UCSR1A, UDRE1);        //wait until buffer is empty
  UDR1 = byte;                                   
  UART_PORT |= _BV(UART_RTS);                  //okay, we be done sending
  printf("done splurting out the data, still need to mark end of packet\r\n"); 
  loop_until_bit_is_set(UART_PIN, UART_CTS); //wait for them to get the message so we don't double dip
  printf("we be done tho\r\n");
}

uint8_t uart_flash_recvbyte() {
  char byte_in;
  loop_until_bit_is_clear(UART_PIN, UART_CTS); //wait for their request
  UART_PORT &= ~(_BV(UART_RTS)); //hey, you can send now
  loop_until_bit_is_set(UCSR1A, RXC1); //wait for them to actually send
  byte_in = UDR1;
  UART_PORT |= _BV(UART_RTS); //okay, you can no longer send
  loop_until_bit_is_set(UART_PIN, UART_CTS); //wait for them to get the message so we don't double dip
  return byte_in;
}
#endif
