#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <time.h>
#include <pthread.h>

#include <arpa/inet.h>
#include <sys/socket.h>

#include <wiringPi.h>
#include <wiringPiI2C.h>



#define BUFFER_MAX 3
#define DIRECTION_MAX 45

#define IN 0
#define OUT 1
#define LOW 0
#define HIGH 1
#define VALUE_MAX 256

#define PIN    24
#define POUT   23

#define PIN2   27
#define POUT2  17

#define B_PIN  20    // 버튼 1
#define B_PIN2 21    // 버튼 2
#define I_PIN  26     // 적외선 센서
#define LCDAddr 0x27

double distance[2] = {0,};
char msg[10];
int fd;

static int GPIOExport(int pin) {
   
   char buffer[BUFFER_MAX];
   ssize_t bytes_written;
   int fd;
   
   fd = open("/sys/class/gpio/export", O_WRONLY);
   if (-1 == fd) {
      fprintf(stderr, "Failed to open export for writing!\n");
      return(-1);
   }
   
   bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
   write(fd, buffer, bytes_written);
   close(fd);
   return(0);
}

static int GPIOUnexport(int pin){
   char buffer[BUFFER_MAX];
   ssize_t bytes_written;
   int fd;
   
   fd = open("/sys/class/gpio/unexport", O_WRONLY);
   if (-1 == fd){
      fprintf(stderr, "Failed to open unexport for writing! \n");
      return(-1);
   } bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
   write(fd, buffer, bytes_written);
   close(fd);
   return(0);
}

static int GPIODirection(int pin, int dir){
   static const char s_directions_str[] = "in\0out";
   char path[DIRECTION_MAX] = "/sys/class/gpio/gpio%d/direction";
   int fd;
   
   snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin);
   fd = open(path, O_WRONLY);
   if(-1 == fd){
      fprintf(stderr, "Failed to open gpio direction for writing! \n");
      return(-1);
   }
   if(-1 == write(fd, &s_directions_str[IN == dir? 0 : 3], IN == dir ? 2 : 3)) {
      fprintf(stderr, "Failed to set direction! \n");
      return(-1);
   }
   close(fd);
   return(0);
}

static int GPIORead(int pin) {
   char path[VALUE_MAX];
   char value_str[3];
   int fd;
   
   snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
   fd = open(path, O_RDONLY);
   if (-1 == fd) {
      fprintf(stderr, "Failed to open gpio value for reading!\n");
      return(-1);
   }
   
   if (-1 == read(fd, value_str, 3)) {
      fprintf(stderr, "Failed to read value!\n");
      return(-1);
   }
   
   close(fd);
   
   return(atoi(value_str));
}

static int GPIOWrite(int pin, int value) {
   static const char s_values_str[] = "01";
   char path[VALUE_MAX];
   int fd;
   
   snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
   fd = open(path, O_WRONLY);
   if (-1 == fd) {
      fprintf(stderr, "Failed to open gpio value for writing!\n");
      return(-1);
   }
   
   if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1)) {
      fprintf(stderr, "Failed to write value!\n");
      return(-1);
      
   close(fd);
   return(0);
   }
}

int BLEN=1;
 
void write_word(int data){
    int temp = data;
    if ( BLEN == 1 )
        temp |= 0x08;
    else
        temp &= 0xF7;
    wiringPiI2CWrite(fd, temp);
}

void send_command(int comm){
    int buf;
    // Send bit7-4 firstly
    buf = comm & 0xF0;
    buf |= 0x04;                    // RS = 0, RW = 0, EN = 1
    write_word(buf);
    delay(2);
    buf &= 0xFB;                    // Make EN = 0
    write_word(buf);

    // Send bit3-0 secondly
    buf = (comm & 0x0F) << 4;
    buf |= 0x04;                    // RS = 0, RW = 0, EN = 1
    write_word(buf);
    delay(2);
    buf &= 0xFB;                    // Make EN = 0
    write_word(buf);
}

void send_data(int data){
    int buf;
    // Send bit7-4 firstly
    buf = data & 0xF0;
    buf |= 0x05;                    // RS = 1, RW = 0, EN = 1
    write_word(buf);
    delay(2);
    buf &= 0xFB;                    // Make EN = 0
    write_word(buf);

    // Send bit3-0 secondly
    buf = (data & 0x0F) << 4;
    buf |= 0x05;                    // RS = 1, RW = 0, EN = 1
    write_word(buf);
    delay(2);
    buf &= 0xFB;                    // Make EN = 0
    write_word(buf);
}

void init(){
    send_command(0x33);     // Must initialize to 8-line mode at first
    delay(5);
    send_command(0x32);     // Then initialize to 4-line mode
    delay(5);
    send_command(0x28);     // 2 Lines & 5*7 dots
    delay(5);
    send_command(0x0C);     // Enable display without cursor
    delay(5);
    send_command(0x01);     // Clear Screen
    wiringPiI2CWrite(fd, 0x08);
}

void clear(){
    send_command(0x01);     //clear Screen
}


void write_l(int x, int y, char data[]){
    int addr, i;
    int tmp;
    if (x < 0)  x = 0;
    if (x > 15) x = 15;
    if (y < 0)  y = 0;
    if (y > 1)  y = 1;

    // Move cursor
    addr = 0x80 + 0x40 * y + x;
    send_command(addr);

    tmp = strlen(data);
    for (i = 0; i < tmp; i++){
        send_data(data[i]);
    }
}

void *lcd_thd(){
	char s[10];
	
    fd = wiringPiI2CSetup(LCDAddr);
    init();


	
	write_l(0, 0, "Don't worry!");
	write_l(1, 1, "CCTV is on.");
    delay(15000);
    clear();
    
    pthread_exit(0);
}


void *infraRed_thd(){
   
   if(-1 == GPIODirection(I_PIN,IN))
      pthread_exit(0);
   
   while(1){
      // snprintf(msg,4,"%d %d",GPIORead(I_PIN)==1?3:0,GPIORead(I_PIN)==1?3:0);
      if(GPIORead(I_PIN) == 1){
         int thr_id;
         int status;
         pthread_t p_thread;
         
         thr_id = pthread_create(&p_thread,NULL,lcd_thd,NULL);
         pthread_join(p_thread, (void**)&status);

         system("raspivid -t 20000 -o vidio.h264");
      }
      usleep(1000*1000);
   }  
      
   pthread_exit(0);
}

void *button_thd(void* arg){
   int clnt_sock =(int)arg;
   char buf[BUFFER_MAX];
   
   if(-1 == GPIODirection(B_PIN,IN))
      pthread_exit(0);
   
   while(1){
      
         
      snprintf(msg,4,"%d",GPIORead(B_PIN));
      write(clnt_sock,msg,sizeof(msg));

      printf("msg = %s\n",msg);
      usleep(1000*1000);
   }  
      
   close(clnt_sock);
   pthread_exit(0);
}



void error_handling(char *message){
   fputs(message,stderr);
   fputc('\n',stderr);
   exit(1);
}




int main(int argc, char *argv[]) {
   char* buffer;
   int size;
   int count;
   int sock;
   int serv_sock,clnt_sock=-1;
   int status;
   struct sockaddr_in serv_addr,clnt_addr;
   socklen_t clnt_addr_size;
   
   pthread_t t_id;
   pthread_t b_id;
   int thr_id;

    

   if (-1 == GPIOExport(B_PIN) || -1 == GPIOExport(B_PIN2) || GPIOExport(I_PIN))
		return(1);
   
   if (-1 == GPIOExport(POUT) || -1 == GPIOExport(PIN))
		return(1);
   
   if (-1 == GPIOExport(POUT2) || -1 == GPIOExport(PIN2))
		return(1);

    //클라이언트 소켓 세팅
    sock = socket(PF_INET, SOCK_STREAM, 0);
    if(sock == -1)
        error_handling("socket() error");

    //서버로 보낼준비하고 서버로 연결 시도    
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
    serv_addr.sin_port = htons(atoi(argv[2]));  
        
    if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
        error_handling("connect() error");

   pthread_create(&t_id,NULL,infraRed_thd,NULL);
   pthread_create(&b_id,NULL,button_thd,(void*)sock);
   pthread_join(b_id, (void **)status);
   pthread_join(t_id, (void **)&status);

   


   if(-1 == GPIOUnexport(POUT) || GPIOUnexport(PIN) || GPIOUnexport(B_PIN) || GPIOUnexport(I_PIN))
      return 4;
   if(-1 == GPIOUnexport(POUT2) || GPIOUnexport(PIN2) || GPIOUnexport(B_PIN2))
      return 4;


    // 전송 완료 후 소켓 종료
   close(sock);

   printf("파일 전송 완료\n");
   return 0;
}