#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>           // close()
#include <string.h>           // strcpy, memset(), and memcpy()
#include <stdarg.h>
#include <errno.h>            // errno, perror()
#include <stdbool.h>
#include <pthread.h>
#include <semaphore.h>
#include <math.h>
#include <dirent.h> 
#include <fcntl.h>
#include <signal.h>
#include <getopt.h>
#include <termios.h>
#include <netdb.h>            // struct addrinfo
#include <sys/stat.h>
#include <sys/ioctl.h>        // macro ioctl is defined
#include <bits/ioctls.h>      // defines values for argument "request" of ioctl.
#include <sys/syscall.h>
#include <sys/types.h>        // needed for socket(), uint8_t, uint16_t, uint32_t
#include <sys/select.h>
#include <sys/socket.h>       // needed for socket()
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <libgen.h>
#include <netinet/in.h>       // IPPROTO_ICMP, INET_ADDRSTRLEN
#include <netinet/ip.h>       // struct ip and IP_MAXPACKET (which is 65535)
#include <netinet/ip_icmp.h>  // struct icmp, ICMP_ECHO
#include <arpa/inet.h>        // inet_pton() and inet_ntop()
#include <net/if.h>           // struct ifreq
#include <net/ethernet.h>
#include <linux/if_ether.h>   // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD
#include <linux/if_packet.h>  // struct sockaddr_ll (see man 7 packet)
#include <linux/serial.h>
#include <linux/can.h>
#include <linux/can/raw.h>

#define printa(fmt,...)  printf("%s:%d, %s | "fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#define printe(fmt,...)  printf("%s:%d, %s | "fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#define printw(fmt,...)  printf("%s:%d, %s | "fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#define printi(fmt,...)  printf("%s:%d, %s | "fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#define printd(fmt,...)  printf("%s:%d, %s | "fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
#define printv(fmt,...)  printf("%s:%d, %s | "fmt, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)

#define UPDATE_SOCKET_SERVER "/tmp/app_run_server"
#define UPDATE_SOCKET_CLIENT "/tmp/app_run_client"

typedef struct update
{
    int quit;
    int fd;

    struct sockaddr_un self;
    struct sockaddr_un peer;
    char self_path[128];
    char peer_path[128];

    uint8_t rxbuff[1024];
    int rxbyte;

    uint8_t txbuff[1024];
    int txbyte;

    int major;
    int minor;
    int build;

    FILE *logfp;
}update_t;

static update_t update =
{
    .fd = -1,
	.logfp = NULL
};

static ssize_t af_socket_readin(int fd, void* buff, size_t size, char* from, size_t from_size)
{
    socklen_t socksize = sizeof(struct sockaddr_un);
    struct sockaddr_un addr;
    memset(buff, 0, size);
    int bytes = recvfrom(fd, buff, size, 0, (struct sockaddr*)&addr, &socksize);
    if (from != NULL && from_size > 0) {
        memset(from, 0, from_size);
        memcpy(from, addr.sun_path, from_size < sizeof(addr.sun_path) ? from_size : sizeof(addr.sun_path));
    }
    return bytes;
}

static ssize_t af_socket_sendto(int fd, const void* buff, size_t size, const char* pathname)
{
    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(struct sockaddr_un));
    strncpy(addr.sun_path, pathname, sizeof(addr.sun_path) - 1);
    addr.sun_family = AF_UNIX;
    return sendto(fd, buff, size, 0, (struct sockaddr*)&addr, sizeof(struct sockaddr_un));
}

static void af_update_app(update_t* dev, const char* app_name)
{
    sprintf(dev->txbuff, "pkill %s\n", app_name);
    printi("%s", dev->txbuff);
    system(dev->txbuff);

    system(dev->txbuff);
    sprintf(dev->txbuff, "/home/root/app_run/bin/%s", app_name);
	struct stat this_stat;
    if (lstat(dev->txbuff, &this_stat) == 0) unlink(dev->txbuff);
    sprintf(dev->txbuff, "ln -s /home/root/app_run/bin/%s_V%dR%dB%d /home/root/app_run/bin/%s\n", app_name, dev->major, dev->minor, dev->build, app_name);
    printi("%s", dev->txbuff);
    system(dev->txbuff);

    sprintf(dev->txbuff, "chmod +x /home/root/app_run/bin/%s_V%dR%dB%d\n", app_name, dev->major, dev->minor, dev->build);
    printi("%s", dev->txbuff);
    system(dev->txbuff);

    sprintf(dev->txbuff, "/home/root/app_run/bin/%s&\n", app_name);
    printi("%s", dev->txbuff);
    system(dev->txbuff);
}

static void af_reset_app(update_t* dev, const char* app_name)
{
    sprintf(dev->txbuff, "pkill %s\n", app_name);
    printi("%s", dev->txbuff);
    system(dev->txbuff);
    sprintf(dev->txbuff, "/home/root/app_run/bin/%s&\n", app_name);
    printi("%s", dev->txbuff);
    system(dev->txbuff);
}

void* af_update_loop(void* argv)
{
    update_t* dev = (update_t*)argv;
    while(dev->quit == 0)
    {
        dev->rxbyte = af_socket_readin(dev->fd, dev->rxbuff, 32, dev->peer_path, 128);
        if (dev->rxbyte > 8)
        {
            dev->rxbuff[dev->rxbyte] = '\0';
            printi("recv:%s, from:%s\n", dev->rxbuff, dev->peer_path);
            if (strncmp(dev->rxbuff, "reset app_CCU", 13)==0)
            {
                af_reset_app(dev, "app_CCU");
            }
            if (strncmp(dev->rxbuff, "reset app_PCU", 13)==0)
            {
                af_reset_app(dev, "app_PCU");
            }
            if (strncmp(dev->rxbuff, "reset app_TCU", 13)==0)
            {
                af_reset_app(dev, "app_TCU");
            }
            if (strncmp(dev->rxbuff, "update app_CCU", 14)==0)
            {
                sscanf(dev->rxbuff, "app_CCU_V%dR%dB%d", &dev->major, &dev->minor, &dev->build);
                af_update_app(dev, "app_CCU");
            }
            if (strncmp(dev->rxbuff, "update app_TCU", 14)==0)
            {
                sscanf(dev->rxbuff, "app_TCU_V%dR%dB%d", &dev->major, &dev->minor, &dev->build);
                af_update_app(dev, "app_TCU");
            }
            if (strncmp(dev->rxbuff, "update app_PCU", 14)==0)
            {
                sscanf(dev->rxbuff, "app_PCU_V%dR%dB%d", &dev->major, &dev->minor, &dev->build);
                af_update_app(dev, "app_PCU");
            }
        }
    }
    return NULL;
}

int af_update_init(update_t *dev)
{
    if (dev)
    {
        dev->quit = 0;
        dev->fd = socket(AF_UNIX, SOCK_DGRAM, 0);
        memset(&dev->self, 0, sizeof(struct sockaddr_un));
        strncpy(dev->self.sun_path, dev->self_path, sizeof(dev->self.sun_path) - 1);
        dev->self.sun_family = AF_UNIX;
        int enable = 1;
        setsockopt(dev->fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
        //地址可重用设置(有效避免bind超时)
        if (access(dev->self_path, F_OK) == 0) unlink(dev->self_path);
        return bind(dev->fd, (struct sockaddr*)&dev->self, sizeof(struct sockaddr_un));
    }
    return -1;
}

void af_update_exit(update_t *dev)
{
    if (dev)
    {
        dev->quit = 1;
        if (dev->fd > 0) close(dev->fd);
        if (access(dev->self_path, F_OK) == 0) unlink(dev->self_path);
        dev->fd = -1;
    }
}

int main(int argc, char* argv[])
{
    sleep(10);
    update_t* dev = (update_t*)&update;
    {
        int c;
        sprintf(dev->self_path, "%s", UPDATE_SOCKET_SERVER);
        sprintf(dev->peer_path, "%s", UPDATE_SOCKET_CLIENT);
        while ((c = getopt(argc, argv, "s:p:")) != -1) {
            switch (c) {
            case 's':
                sprintf(dev->self_path, "%s", optarg);
                break;
            case 'p':
                sprintf(dev->peer_path, "%s", optarg);
                break;
            case '?':
                break;
            default:
                exit(2);
            }
        }
        af_update_init(dev);
        af_update_loop(dev);
        af_update_exit(dev);
    }
    return 0;
}