#include "serial.h"

static int speed_arr[] = {B115200,B57600,B38400,B19200,B9600,B4800,B2400,B1200,B300};
static int name_arr[] =  {115200,57600,38400,19200,9600,4800,2400,1200,300};

// Common Helper /////////////////////////////////////////////////////////////
//
static void SetACommMode(int fd,int combaud,int databit,int stopbit,int checkmode)
{
	struct termios tty_termios;
	int	flags;
	unsigned int i;

	if ((tcgetattr(fd, &tty_termios) == -1)) {
		printf ("tcgetattr()");
		return;
	}
	cfmakeraw(&tty_termios);
	for (i=0; i<sizeof(speed_arr)/sizeof(int); i++) {
		if (combaud == name_arr[i]) {
			cfsetispeed(&tty_termios, speed_arr[i]);
			cfsetospeed(&tty_termios, speed_arr[i]);
			break;
		}
	}

	tty_termios.c_cflag &= ~CSIZE;
	switch (databit) {
	case 7:
		tty_termios.c_cflag |= CS7;
		break;
	case 8:
	default:
		tty_termios.c_cflag |= CS8;
		break;
	}

	switch (checkmode) {
	case 0:
		tty_termios.c_cflag &= ~PARENB;
		tty_termios.c_iflag &= ~INPCK;
		break;
	case 1:
		tty_termios.c_cflag |= (PARODD | PARENB);
		tty_termios.c_iflag |= INPCK;
		break;
	case 2:
		tty_termios.c_cflag |= PARENB;	// Enable parity????У?? 
		tty_termios.c_cflag &= ~PARODD; //?????У?? 
		tty_termios.c_iflag |= INPCK;   //???У????? 
		break;
	default:
		tty_termios.c_cflag &= ~PARENB;
		tty_termios.c_iflag &= ~INPCK;
		break;
	}

	// Set input parity option
	if (checkmode)
		tty_termios.c_iflag |= INPCK;

	// Set stop bit
	switch (stopbit) {
	case 1:
		tty_termios.c_cflag &= ~CSTOPB;
		break;
	case 2:
		tty_termios.c_cflag |= CSTOPB;
		break;
	default:
		tty_termios.c_cflag &= ~CSTOPB;
		break;
	}

	tty_termios.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  // Input
	tty_termios.c_oflag  &= ~OPOST;	// Output
	tty_termios.c_cc[VTIME] = 0;	// Set timeout 0 seconds
	tty_termios.c_cc[VMIN] = 0; 	// Update the tty_termios and do it NOW

	if(tcsetattr(fd,TCSANOW,&tty_termios)!=0) {
		perror("tcsetattr()");
	}
	tcflush(fd,TCIOFLUSH);
	flags = fcntl(fd,F_GETFL);
	fcntl(fd,F_SETFL,flags|O_NONBLOCK);
}


int SerialOpen(const char *dev_name,int baud)
{
  int com_fd;
  if((com_fd=open(dev_name,O_RDWR|O_NOCTTY|O_NONBLOCK))==-1) 
    {
      perror("SerialOpen failed");
      return -1;
    }		
  SetACommMode(com_fd,baud,8,1,0);//??У?? 
  return com_fd;
}


/**
 * Read from Serial device
 * @param	timeout		millisecond
 */
int SerialRead(int m_comFd,unsigned char *buf,int sz,int timeout)
{
   int iDataNum;
   int rxlen = 0;
   int recvflag = 0;

   if(timeout<0)
     return 0;

   if(timeout>0)
     timeout /= 500;

   
#ifdef DEBUG_COM
	int i;
	printf("RX:\t");
#endif

    for(;;) 
      {
		iDataNum = read(m_comFd,buf+rxlen,sz-rxlen);
#ifdef DEBUG_COM
		for (int i = 0; i < iDataNum; i++) {
			printf("%02X ", buf[rxlen + i]);
			if (!((i + 1) % 10))
				printf("\n\t");
		}
#endif

		if (iDataNum <= 0) {
			if (recvflag==0 && timeout--)
				usleep(500000);//500ms
			else
				break;
		} else {
			recvflag = 1;
			rxlen += iDataNum;
		}
	}

#ifdef DEBUG_COM
	printf("\n");
	fflush(stdout);
#endif

	return (rxlen > 0) ? rxlen : 0;
}

//timeout is in 30ms
int SerialReadEx(int m_comFd,unsigned char *buf,int sz,int timeout)
{
  int curr_length=0,last_length=0,rx_length=0;
  tcflush(m_comFd,TCIFLUSH);
  while(timeout--)
    {
 	  usleep(30000);//wait 30ms
      curr_length=read(m_comFd,buf+rx_length,sz-rx_length);
      if(curr_length>0)
       {
       	 rx_length+=curr_length;
         last_length=curr_length;
         if(rx_length>=sz)
           return rx_length;
       }
      else
       {
   	     if(last_length>0)
   	       {
       	   	 return rx_length; 
       	   }
   	     last_length=0;
 	     rx_length=0;
       }
    }
  return rx_length; 	
}

/**
 * Write to Serial device
 */
int SerialWrite(int m_comFd,const unsigned char *buf,int sz)
{
	// Avoid rush
	usleep(10000);
	//tcflush(m_comFd,TCOFLUSH);
	if (write(m_comFd, buf, sz) != sz) {
		printf("SerialWrite:write()\n");
	  return -1;
	}

#ifdef DEBUG_COM
	int i;
	printf("\nTX:\t");
	for (i = 0; i < len; i++) {
		printf("%02X ", buf[i]);
		if (!((i + 1) % 10))
			printf("\n\t");
	}
	printf("\n");
	fflush(stdout);
#endif
  return sz;
}
