/*
 * Copyright (C) 2015-2018 Alibaba Group Holding Limited
 */





#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <memory.h>

#include <pthread.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/time.h>
#include <semaphore.h>
#include <errno.h>
#include <assert.h>
#include <net/if.h>       // struct ifreq
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/reboot.h>
#include <sys/time.h>
#include <time.h>
#include <signal.h>
#include "kk_product.h"
#include "ccu_ver.h"
#include "com_api.h"
#include "kk_log.h"

char g_product_type[PRODUCT_TYPE_LEN]={0};
char g_product_code[PRODUCT_CODE_LEN]={0};

#define PLATFORM_WAIT_INFINITE (~0)

int HAL_Execel_cmd(char * cmd,char * buf,int buf_len,int* ret_len)
{
	if(cmd == NULL || buf == NULL || buf_len == 0)
	{
		printf("arg error\n");
		return -1;
	}

	memset(buf,0,buf_len);

	FILE *fp = NULL;

	fp = popen(cmd,"r");
	while(fgets(buf,buf_len,fp)!=NULL){
		printf("%s return %s",cmd,buf);
	}
	pclose (fp);

	*ret_len = strlen(buf);
	return 0;
}
void *HAL_Malloc(_IN_ uint32_t size)
{
    return malloc(size);
}

void *HAL_Realloc(_IN_ void *ptr, _IN_ uint32_t size)
{
    return realloc(ptr, size);
}

void *HAL_Calloc(_IN_ uint32_t nmemb, _IN_ uint32_t size)
{
    return calloc(nmemb, size);
}

void HAL_Free(_IN_ void *ptr)
{
    free(ptr);
}

void *HAL_MutexCreate(void)
{
    int err_num;
    pthread_mutex_t *mutex = (pthread_mutex_t *)HAL_Malloc(sizeof(pthread_mutex_t));
    if (NULL == mutex) {
        return NULL;
    }

    if (0 != (err_num = pthread_mutex_init(mutex, NULL))) {
        printf("create mutex failed");
        HAL_Free(mutex);
        return NULL;
    }

    return mutex;
}

void HAL_MutexDestroy(_IN_ void *mutex)
{
    int err_num;

    if (!mutex) {
        printf("mutex want to destroy is NULL!");
        return;
    }
    if (0 != (err_num = pthread_mutex_destroy((pthread_mutex_t *)mutex))) {
        printf("destroy mutex failed");
    }

    HAL_Free(mutex);
}

void HAL_MutexLock(_IN_ void *mutex)
{
	int err_num;
	if (0 != (err_num = pthread_mutex_lock((pthread_mutex_t *)mutex))) {
		printf("lock mutex failed: - '%s' (%d)", strerror(err_num), err_num);
	}
}

void HAL_MutexUnlock(_IN_ void *mutex)
{
    int err_num;
    if (0 != (err_num = pthread_mutex_unlock((pthread_mutex_t *)mutex))) {
        printf("unlock mutex failed - '%s' (%d)", strerror(err_num), err_num);
    }
}

uint64_t HAL_UptimeMs(void)
{
    uint64_t            time_ms;
    struct timespec     ts;

    clock_gettime(CLOCK_MONOTONIC, &ts);
    time_ms = ((uint64_t)ts.tv_sec * (uint64_t)1000) + (ts.tv_nsec / 1000 / 1000);
	//printf("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&HAL_UptimeMs:%llu\n",time_ms);
    return time_ms;
}
time_t HAL_Uptimes(void)
{
    time_t            time_m;
    struct timespec     ts;

    clock_gettime(CLOCK_MONOTONIC, &ts);
    time_m = ts.tv_sec ;

    return time_m;
}

time_t HAL_GetTime(void)
{
    time_t t;  
    t = time(NULL);  
    return t;
}


time_t HAL_GetTime_s(char *outStr)
{
	time_t t;
	t = time(NULL);

	sprintf(outStr,"%d",t);
	
	return t;
}

long long HAL_GetTime_ms()
{
    struct timeval begin;
    gettimeofday(&begin,NULL);
    long long nowTime = (long long)begin.tv_sec * 1000 + (long long)begin.tv_usec / 1000;
    return nowTime;

}


char *HAL_GetTimeStr(_IN_ char *buf, _IN_ int len)
{
    struct timeval tv;
    struct tm      tm;
    int str_len    = 0;

    if (buf == NULL || len < 28) {
        return NULL;
    }
    gettimeofday(&tv, NULL);
    localtime_r(&tv.tv_sec, &tm);
    strftime(buf, 28, "%m-%d %H:%M:%S", &tm);
    str_len = strlen(buf);
    if (str_len + 3 < len) {
        snprintf(buf + str_len, len, ".%3.3d", (int)(tv.tv_usec) / 1000);
    }
    return buf;
}


 
int HAL_Get_TimeZone(void)
{
 
     int timezone = 0;
     time_t t1, t2 ;
     struct tm *tm_local, *tm_utc;
 
	time(&t1);
	t2 = t1;
	tm_local = localtime(&t1);
	t1 = mktime(tm_local) ;
	tm_utc = gmtime(&t2);

	t2 = mktime(tm_utc);

	
    timezone = (t1 - t2) / 3600;
	printf("%d\n", timezone);
 
	return timezone;
}

void HAL_SleepMs(_IN_ uint32_t ms)
{
	usleep(1000 * ms);
}

void HAL_Srandom(uint32_t seed)
{
    srandom(seed);
}

uint32_t HAL_Random(uint32_t region)
{
    return (region > 0) ? (random() % region) : 0;
}

int HAL_Snprintf(_IN_ char *str, const int len, const char *fmt, ...)
{
    va_list args;
    int     rc;

    va_start(args, fmt);
    rc = vsnprintf(str, len, fmt, args);
    va_end(args);

    return rc;
}

int HAL_Vsnprintf(_IN_ char *str, _IN_ const int len, _IN_ const char *format, va_list ap)
{
    return vsnprintf(str, len, format, ap);
}

void HAL_Printf(_IN_ const char *fmt, ...)
{
    va_list args;

    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);

    fflush(stdout);
}

int HAL_SetProduct_Type(_IN_ char *product_type)
{
    int len = strlen(product_type);

    if (len > PRODUCT_TYPE_LEN) {
        return -1;
    }
    memset(g_product_type, 0x0, PRODUCT_TYPE_LEN);
    strncpy(g_product_type, product_type, len);
    return len;
}


int HAL_SetProduct_Code(_IN_ char *product_code)
{
    int len = strlen(product_code);
    if (len > PRODUCT_CODE_LEN) {
        return -1;
    }
    memset(g_product_code, 0x0, PRODUCT_CODE_LEN);
    strncpy(g_product_code, product_code, len);
    return len;
}


int HAL_GetProduct_Code(_OU_ char *product_code)
{
	strncpy(product_code, KK_CCU_PRODUCTID, strlen(KK_CCU_PRODUCTID));
	product_code[strlen(KK_CCU_PRODUCTID)] = '\0';
	return strlen(KK_CCU_PRODUCTID);
}

int HAL_GetDevice_Code(_OU_ char *dev_code)
{
	strncpy(dev_code, KK_CCU_ID, strlen(KK_CCU_ID));
	dev_code[strlen(KK_CCU_ID)] = '\0';
	return strlen(KK_CCU_ID);
}
/*
 * This need to be same with app version as in uOTA module (ota_version.h)

    #ifndef SYSINFO_APP_VERSION
    #define SYSINFO_APP_VERSION "app-1.0.0-20180101.1000"
    #endif
 *
 */
int HAL_GetFirmwareVersion(_OU_ char *version)
{
    char *ver = "app-1.0.0-20180101.1000";
    int len = strlen(ver);
    memset(version, 0x0, FIRMWARE_VERSION_MAXLEN);
    strncpy(version, ver, len);
    version[len] = '\0';

    return strlen(version);
}

void *HAL_SemaphoreCreate(void)
{
    sem_t *sem = (sem_t *)malloc(sizeof(sem_t));
    if (NULL == sem) {
        return NULL;
    }

    if (0 != sem_init(sem, 0, 0)) {
        free(sem);
        return NULL;
    }

    return sem;
}

void HAL_SemaphoreDestroy(_IN_ void *sem)
{
    sem_destroy((sem_t *)sem);
    free(sem);
}

void HAL_SemaphorePost(_IN_ void *sem)
{
    sem_post((sem_t *)sem);
}

int HAL_SemaphoreWait(_IN_ void *sem, _IN_ uint32_t timeout_ms)
{
    if (PLATFORM_WAIT_INFINITE == timeout_ms) {
        sem_wait(sem);
        return 0;
    } else {
        struct timespec ts;
        int s;
        /* Restart if interrupted by handler */
        do {
            if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
                return -1;
            }

            s = 0;
            ts.tv_nsec += (timeout_ms % 1000) * 1000000;
            if (ts.tv_nsec >= 1000000000) {
                ts.tv_nsec -= 1000000000;
                s = 1;
            }

            ts.tv_sec += timeout_ms / 1000 + s;

        } while (((s = sem_timedwait(sem, &ts)) != 0) && errno == EINTR);

        return (s == 0) ? 0 : -1;
    }
}

int HAL_ThreadCreate(
            _OU_ void **thread_handle,
            _IN_ void *(*work_routine)(void *),
            _IN_ void *arg,
            _OU_ int *stack_used)
{
    int ret = -1;

    ret = pthread_create((pthread_t *)thread_handle, NULL, work_routine, arg);

    return ret;
}

void HAL_ThreadDetach(_IN_ void *thread_handle)
{
    pthread_detach((pthread_t)thread_handle);
}

void HAL_ThreadDelete(_IN_ void *thread_handle)
{
    if (NULL == thread_handle) {
        pthread_exit(0);
    } else {
        /*main thread delete child thread*/
        pthread_cancel((pthread_t)thread_handle);
        pthread_join((pthread_t)thread_handle, 0);
    }
}
#if 1

static FILE *fp;

#define otafilename "/tmp/kkota.bin"
#define __DEMO__

void HAL_Firmware_Persistence_Start(void* fileName)
{
#ifdef __DEMO__
    char * file = NULL;
    if (fileName == NULL){
        file = otafilename;
    }else{
        file = fileName;
    }
    fp = fopen(file, "w");
    //    assert(fp);
#endif
    return;
}

int HAL_Firmware_Persistence_Write(_IN_ char *buffer, _IN_ uint32_t length)
{
#ifdef __DEMO__
    unsigned int written_len = 0;
    written_len = fwrite(buffer, 1, length, fp);

    if (written_len != length) {
        return -1;
    }
#endif
    return 0;
}

int HAL_Firmware_Persistence_Stop(void)
{
#ifdef __DEMO__
    if (fp != NULL) {
        fclose(fp);
    }
#endif

    /* check file md5, and burning it to flash ... finally reboot system */

    return 0;
}

int HAL_Config_Write(const char *buffer, int length)
{
    FILE *fp;
    size_t written_len;
    char filepath[128] = {0};

    if (!buffer || length <= 0) {
        return -1;
    }

    snprintf(filepath, sizeof(filepath), "./%s", "alinkconf");
    fp = fopen(filepath, "w");
    if (!fp) {
        return -1;
    }

    written_len = fwrite(buffer, 1, length, fp);

    fclose(fp);

    return ((written_len != length) ? -1 : 0);
}

int HAL_Config_Read(char *buffer, int length)
{
    FILE *fp;
    size_t read_len;
    char filepath[128] = {0};

    if (!buffer || length <= 0) {
        return -1;
    }

    snprintf(filepath, sizeof(filepath), "./%s", "alinkconf");
    fp = fopen(filepath, "r");
    if (!fp) {
        return -1;
    }

    read_len = fread(buffer, 1, length, fp);
    fclose(fp);

    return ((read_len != length) ? -1 : 0);
}

#define REBOOT_CMD "reboot"
void HAL_Reboot(void)
{
	if (system(REBOOT_CMD)) {
		perror("HAL_Reboot failed");
	}
}
#endif
#define ROUTER_INFO_PATH        "/proc/net/route"
#define ROUTER_RECORD_SIZE      256


static char *_get_default_routing_ifname(char *ifname, int ifname_size)
{
    FILE *fp = NULL;
    char line[ROUTER_RECORD_SIZE] = {0};
    char iface[IFNAMSIZ] = {0};
    char *result = NULL;
    unsigned int destination, gateway, flags, mask;
    unsigned int refCnt, use, metric, mtu, window, irtt;

    fp = fopen(ROUTER_INFO_PATH, "r");
    if (fp == NULL) {
        perror("fopen");
        return result;
    }

    char *buff = fgets(line, sizeof(line), fp);
    if (buff == NULL) {
        perror("fgets");
        goto out;
    }

    while (fgets(line, sizeof(line), fp)) {
        if (11 !=
            sscanf(line, "%s %08x %08x %x %d %d %d %08x %d %d %d",
                   iface, &destination, &gateway, &flags, &refCnt, &use,
                   &metric, &mask, &mtu, &window, &irtt)) {
            perror("sscanf");
            continue;
        }

        /*default route */
        if ((destination == 0) && (mask == 0)) {
            strncpy(ifname, iface, ifname_size - 1);
            result = ifname;
            break;
        }
    }

out:
    if (fp) {
        fclose(fp);
    }

    return result;
}

static char ifname_buf[2048];
static char *ifnames = ifname_buf;
static int mac_count = 0;
static void add_interface_name(const char * name)
{
    int i;
    for (i=0;i<mac_count;i++)
    {
        if (!strcmp(ifnames+i*IFNAMSIZ, name))
            return;
    }

    strncpy(ifnames+(mac_count++)*IFNAMSIZ, name, IFNAMSIZ-1);
}
static char * get_name(char *name, char *p)
{
    while (isspace(*p))
    p++;
    while (*p) {
    if (isspace(*p))
        break;
    if (*p == ':') {    /* could be an alias */
        char *dot = p, *dotname = name;
        *name++ = *p++;
        while (isdigit(*p))
        *name++ = *p++;
        if (*p != ':') {    /* it wasn't, backup */
        p = dot;
        name = dotname;
        }
        if (*p == '\0')
        return NULL;
        p++;
        break;
    }
    *name++ = *p++;
    }
    *name++ = '\0';
    return p;
}
static int get_procnet_list()
{
    FILE *fh;
    char buf[512];
    fh = fopen("/proc/net/dev", "r");
    if (!fh)
        return -1;

    fgets(buf, sizeof buf, fh); /* eat title lines */
    fgets(buf, sizeof buf, fh);
    while (fgets(buf, sizeof buf, fh))
    {
        char name[IFNAMSIZ];
        get_name(name, buf);
        add_interface_name(name);
    }
    fclose(fh);
    return 0;
}
static int HAL_Get_mac_ubuntu(char* mac)
{
    struct ifreq tmp;
    int sock_mac;
    char mac_addr[30];
    sock_mac = socket(AF_INET, SOCK_STREAM, 0);
    if( sock_mac == -1){
        perror("create socket fail\n");
        return -1;
    }
    memset(&tmp,0,sizeof(tmp));
    strncpy(tmp.ifr_name,"ens33",sizeof(tmp.ifr_name)-1 );
    if( (ioctl( sock_mac, SIOCGIFHWADDR, &tmp)) < 0 ){
        printf("mac ioctl error\n");
        return -1;
    }
    sprintf(mac_addr, "%02x%02x%02x%02x%02x%02x",
            (unsigned char)tmp.ifr_hwaddr.sa_data[0],
            (unsigned char)tmp.ifr_hwaddr.sa_data[1],
            (unsigned char)tmp.ifr_hwaddr.sa_data[2],
            (unsigned char)tmp.ifr_hwaddr.sa_data[3],
            (unsigned char)tmp.ifr_hwaddr.sa_data[4],
            (unsigned char)tmp.ifr_hwaddr.sa_data[5]
            );
    printf("local mac:%s\n", mac_addr);
    close(sock_mac);
    memcpy(mac,mac_addr,strlen(mac_addr));
    return 0;
}
int HAL_Get_mac(char* mac)
{
 struct ifreq ifr;
    struct ifreq *IFR;
    struct ifconf ifc;
    char buf[1024];
    int s, i;
    int ok = 0;
    char mac_addr[30] = {0};
    // clear buffer
    memset(ifname_buf, 0, sizeof(ifname_buf));
    mac_count = 0;

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s==-1) {
        return -1;
    }

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    ioctl(s, SIOCGIFCONF, &ifc);

    IFR = ifc.ifc_req;
    // put the ioctl interface names in the list
    for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; IFR++) {
            add_interface_name(IFR->ifr_name);
    }
    // put the /proc/net/dev interface names in the list
    if (get_procnet_list())
        return -1;

    // get the first mac address of eth* device hardware address
    for (i = 0; i < mac_count; i++) {
        strcpy(ifr.ifr_name, ifnames + i*IFNAMSIZ);
        if (!strncmp(ifr.ifr_name, "eth", 3))
         	if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) {
                 if (! (ifr.ifr_flags & IFF_LOOPBACK)) {
                     if (ioctl(s, SIOCGIFHWADDR, &ifr) == 0) {
                         char *p = (char *)ifr.ifr_hwaddr.sa_data;
                         if (!*((int *)p) && !*((int *)(p+2)) )
                             continue;
                         // if not 00:00:00:00:00:00, yes, we get the real mac addr
                         ok = 1;
                         break;
                     }
                 }
             }
    }

    close(s);
    if (ok) {
        //bcopy( ifr.ifr_hwaddr.sa_data, addr, 6);
        sprintf(mac_addr, "%02x%02x%02x%02x%02x%02x",
                     (unsigned char)ifr.ifr_hwaddr.sa_data[0],
                     (unsigned char)ifr.ifr_hwaddr.sa_data[1],
                     (unsigned char)ifr.ifr_hwaddr.sa_data[2],
                     (unsigned char)ifr.ifr_hwaddr.sa_data[3],
                     (unsigned char)ifr.ifr_hwaddr.sa_data[4],
                     (unsigned char)ifr.ifr_hwaddr.sa_data[5]   
        );    
    }
    else {
        int ret_ = 0;
        printf("***************************UBUNTU***************************\n");
        ret_ = HAL_Get_mac_ubuntu(mac);
        return ret_;
    }
    memcpy(mac,mac_addr,strlen(mac_addr));
    return 0;  
}


int HAL_Get_SN(char sn[SN_ADDR_LEN])
{
	strncpy(sn,"15",SN_ADDR_LEN);
	return 0;//just for test
}

int HAL_Get_port(void)
{
	return 5555;//just for test
}
uint32_t HAL_Get_IP(char ip_str[NETWORK_ADDR_LEN], const char *ifname)
{
    struct ifreq ifreq;
    int sock = -1;
    char ifname_buff[IFNAMSIZ] = {0};

    if ((NULL == ifname || strlen(ifname) == 0) &&
        NULL == (ifname = _get_default_routing_ifname(ifname_buff, sizeof(ifname_buff)))) {
        perror("get default routeing ifname");
        return -1;
    }

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        return -1;
    }

    ifreq.ifr_addr.sa_family = AF_INET; //ipv4 address
    strncpy(ifreq.ifr_name, ifname, IFNAMSIZ - 1);

    if (ioctl(sock, SIOCGIFADDR, &ifreq) < 0) {
        close(sock);
        perror("ioctl");
        return -1;
    }

    close(sock);

    strncpy(ip_str,
            inet_ntoa(((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr),
            NETWORK_ADDR_LEN);

    return ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr;
}


#if 0

static kv_file_t *kvfile = NULL;

int HAL_Kv_Set(const char *key, const void *val, int len, int sync)
{
    if (!kvfile) {
        kvfile = kv_open("/tmp/kvfile.db");
        if (!kvfile) {
            return -1;
        }
    }

    return kv_set_blob(kvfile, (char *)key, (char *)val, len);
}

int HAL_Kv_Get(const char *key, void *buffer, int *buffer_len)
{
    if (!kvfile) {
        kvfile = kv_open("/tmp/kvfile.db");
        if (!kvfile) {
            return -1;
        }
    }

    return kv_get_blob(kvfile, (char *)key, buffer, buffer_len);
}

int HAL_Kv_Del(const char *key)
{
    if (!kvfile) {
        kvfile = kv_open("/tmp/kvfile.db");
        if (!kvfile) {
            return -1;
        }
    }

    return kv_del(kvfile, (char *)key);
}

static long long os_time_get(void)
{
    struct timeval tv;
    long long ms;
    gettimeofday(&tv, NULL);
    ms = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
    return ms;
}

static long long delta_time = 0;

void HAL_UTC_Set(long long ms)
{
    delta_time = ms - os_time_get();
}

long long HAL_UTC_Get(void)
{
    return delta_time + os_time_get();
}

void *HAL_Timer_Create(const char *name, void (*func)(void *), void *user_data)
{
    timer_t *timer = NULL;

    struct sigevent ent;

    /* check parameter */
    if (func == NULL) {
        return NULL;
    }

    timer = (timer_t *)malloc(sizeof(time_t));

    /* Init */
    memset(&ent, 0x00, sizeof(struct sigevent));

    /* create a timer */
    ent.sigev_notify = SIGEV_THREAD;
    ent.sigev_notify_function = (void (*)(union sigval))func;
    ent.sigev_value.sival_ptr = user_data;

    printf("HAL_Timer_Create\n");

    if (timer_create(CLOCK_MONOTONIC, &ent, timer) != 0) {
        free(timer);
        return NULL;
    }

    return (void *)timer;
}

int HAL_Timer_Start(void *timer, int ms)
{
    struct itimerspec ts;

    /* check parameter */
    if (timer == NULL) {
        return -1;
    }

    /* it_interval=0: timer run only once */
    ts.it_interval.tv_sec = 0;
    ts.it_interval.tv_nsec = 0;

    /* it_value=0: stop timer */
    ts.it_value.tv_sec = ms / 1000;
    ts.it_value.tv_nsec = (ms % 1000) * 1000;

    return timer_settime(*(timer_t *)timer, 0, &ts, NULL);
}

int HAL_Timer_Stop(void *timer)
{
    struct itimerspec ts;

    /* check parameter */
    if (timer == NULL) {
        return -1;
    }

    /* it_interval=0: timer run only once */
    ts.it_interval.tv_sec = 0;
    ts.it_interval.tv_nsec = 0;

    /* it_value=0: stop timer */
    ts.it_value.tv_sec = 0;
    ts.it_value.tv_nsec = 0;

    return timer_settime(*(timer_t *)timer, 0, &ts, NULL);
}

int HAL_Timer_Delete(void *timer)
{
    int ret = 0;

    /* check parameter */
    if (timer == NULL) {
        return -1;
    }

    ret = timer_delete(*(timer_t *)timer);

    free(timer);

    return ret;
}
#endif

