#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "cJSON.h"
#include "com_api.h"
#include "kk_hal.h"
#include "kk_product.h"
#include "kcloud_config.h"
#include "infra_sha256.h"
#include "kk_log.h"
#define BUFSIZE 1024
#define PRODUCTSECRET  "123456"

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

    memset(buf,0,buf_len);

    FILE *fp = NULL;
     
  	fp = popen(cmd,"r");
  	while(fgets(buf,buf_len,fp)!=NULL){
  		INFO_PRINT("%s return %s",cmd,buf);  
  	}
  	pclose (fp);
    
    *ret_len = strlen(buf);
    return 0;
}
int kk_parse_deviceSecret(char *info){
	FILE *fp;    
    if(info == NULL){
        return -1;
    }
    cJSON* infoObj = cJSON_Parse(info);  
    if(infoObj == NULL){
        INFO_PRINT("[%s][%d]\n",__FUNCTION__,__LINE__);        
        return -1;
    }  
	//cJSON *data = cJSON_GetObjectItem(infoObj,"data");
	//if(data == NULL)
	//{
		//return -1;
	//}
	cJSON *deviceSecret = cJSON_GetObjectItem(infoObj,"deviceSecret");
	if(deviceSecret == NULL) return -1;    
	fp = fopen(KK_DEVICESECRET_PATH, "w+");
    if(fp != NULL){
        fwrite(deviceSecret->valuestring,strlen(deviceSecret->valuestring),1,fp);
	    fclose(fp);
    }  
    return 0;      
}
int kk_parse_token(char *info){
	FILE *fp;    
    if(info == NULL){
        INFO_PRINT("[%s][%d]\n",__FUNCTION__,__LINE__);
        return -1;
    }
    cJSON* infoObj = cJSON_Parse(info);  
    if(infoObj == NULL){
        return -1;
    }  
	cJSON *data = cJSON_GetObjectItem(infoObj,"data");
	if(data == NULL)
	{
		return -1;
	}
	cJSON *jwt = cJSON_GetObjectItem(data,"jwt");
	if(jwt == NULL) return -1;
	fp = fopen(KK_TOKEN_PATH, "w+");
	if(fp != NULL){
		fwrite(jwt->valuestring,strlen(jwt->valuestring),1,fp);
		fclose(fp);
		system("sync");
	}
	//cJSON *ccuid = cJSON_GetObjectItem(infoObj,"ccu_id");
	//if(ccuid == NULL) return -1;	
	//fp = fopen("/usr/kk/kk_ccuid.txt", "w+");
    //if(fp != NULL){
        //fwrite(ccuid->valuestring,strlen(ccuid->valuestring),1,fp);
	    //fclose(fp);
   // }
	return 0;
}
char *kk_dictionary_order(char *deviceCode, char *productCode,char *randomId)
{
	char str[3][16]={0};
	char temp[16];
	int i,l,len;
    char *signSource = NULL;  
    memcpy(&str[0],deviceCode,strlen(deviceCode));
    memcpy(&str[1],productCode,strlen(productCode));
    memcpy(&str[2],randomId,strlen(randomId));    
    //for (i=0;i<3;i++)
	//{
		//for (l=0;l<16;l++)
		//{
			//INFO_PRINT("%c",str[i][l]);
		//}
		//INFO_PRINT("\n");
	//}
	//排序
	INFO_PRINT("after order：\n");
	int j,k;
	for( j=0;j<3;j++)
	for( k=j+1;k<3;k++)
	{
		if(strcmp(str[j],str[k])>0)
		{//交换
			strcpy(temp,str[j]);
			strcpy(str[j],str[k]);
			strcpy(str[k],temp);
		}
	}
		//for(i=0;i<3;i++)//输出
			//INFO_PRINT("%s\n",str[i]);
    len = strlen(deviceCode) + strlen(productCode) + strlen(randomId) + 1;            
    signSource = malloc(len);
    if (signSource == NULL) {
        return NULL;
    }   
    memset(signSource, 0, len);
    snprintf(signSource, len, "%s%s%s", str[0],str[1],str[2]);  
    INFO_PRINT("signSource %s\n",signSource);
    return signSource;           
}

static const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int base64_encode( const unsigned char *bindata, char *base64, int binlength )
{
    int i, j;
    unsigned char current;

    for ( i = 0, j = 0 ; i < binlength ; i += 3 )
    {
        current = (bindata[i] >> 2) ;
        current &= (unsigned char)0x3F;
        base64[j++] = base64char[(int)current];

        current = ( (unsigned char)(bindata[i] << 4 ) ) & ( (unsigned char)0x30 ) ;
        if ( i + 1 >= binlength )
        {
            base64[j++] = base64char[(int)current];
            base64[j++] = '=';
            base64[j++] = '=';
            break;
        }
        current |= ( (unsigned char)(bindata[i+1] >> 4) ) & ( (unsigned char) 0x0F );
        base64[j++] = base64char[(int)current];

        current = ( (unsigned char)(bindata[i+1] << 2) ) & ( (unsigned char)0x3C ) ;
        if ( i + 2 >= binlength )
        {
            base64[j++] = base64char[(int)current];
            base64[j++] = '=';
            break;
        }
        current |= ( (unsigned char)(bindata[i+2] >> 6) ) & ( (unsigned char) 0x03 );
        base64[j++] = base64char[(int)current];

        current = ( (unsigned char)bindata[i+2] ) & ( (unsigned char)0x3F ) ;
        base64[j++] = base64char[(int)current];
    }
    base64[j] = '\0';
    return 0;
}
int arrayToStr(unsigned char *buf, unsigned int buflen, char *out)
{
    char strBuf[65] = {0};
    char pbuf[32];
    int i;
    for(i = 0; i < buflen; i++)
    {
        sprintf(pbuf, "%02x", buf[i]);
        strncat(strBuf, pbuf, 2);
    }
    strncpy(out, strBuf, buflen * 2);
    INFO_PRINT("out = %s\n", out);
    return buflen * 2;
}

static char * kk_create_register_info()
{
	cJSON *root;
	char *out = NULL;
	char *productCode = KK_CCU_PRODUCTID;
	char *signSource,*base64Out = NULL;
	char mac[16] = {0};
	char ramdonId[16] = {0};
	unsigned char sign_hex[32] = {0};
	char sign_hex_str[65] = {0}; 
	uint8_t productSecret[64] = {0};
	char ccuid[32] = {0};
	int productSecret_len = 0;
	root=cJSON_CreateObject();
	HAL_Get_mac(mac);
	HAL_GetTime_s(ramdonId);
	KK_Get_ccuid(ccuid);
	cJSON_AddStringToObject(root, "deviceCode", ccuid);
	cJSON_AddStringToObject(root, "productCode",productCode);
	signSource = kk_dictionary_order(ccuid,productCode,KK_CCU_RANDOM);
	HAL_Execel_cmd(GET_KEY_CMD,(char *)productSecret,sizeof(productSecret),&productSecret_len);
	if(productSecret_len == 0){
		utils_hmac_sha256((const uint8_t *)signSource, strlen(signSource), (const uint8_t *)PRODUCTSECRET, strlen(PRODUCTSECRET), sign_hex);
	}else{
		utils_hmac_sha256((const uint8_t *)signSource, strlen(signSource), (const uint8_t *)productSecret, strlen((const char*)productSecret), sign_hex);
	}

	arrayToStr(sign_hex,32,sign_hex_str);

    base64Out = malloc(128);
    if (base64Out == NULL) {
        return NULL;
    }
    INFO_PRINT("[%s][%d]sign_hex:%s\n",__FUNCTION__,__LINE__,sign_hex_str);
    //memset(base64Out, 0, 128);     
    //base64_encode(sign_hex_str,base64Out,strlen(sign_hex_str));  
    INFO_PRINT("[%s][%d]base64Out:%s\n",__FUNCTION__,__LINE__,base64Out);
    cJSON_AddStringToObject(root, "sign",sign_hex_str);
    cJSON_AddStringToObject(root, "random",KK_CCU_RANDOM);      
    free(signSource);
    free(base64Out);
	out=cJSON_Print(root);
	//cJSON_Minify(out);
	cJSON_Delete(root);
    INFO_PRINT("--------------------->kk_create_register_info:%s\n",out);
    return out;
}
char g_clientId[64] = {0};
static char * kk_create_get_jwt_info()
{
	cJSON *root;
	FILE *fp;
	char *out = NULL;
	char timestamp[16] = {0};
	char deviceSecret[64] = {0};
	char ccuid[32] = {0};
	HAL_GetTime_s(timestamp);
	KK_Get_ccuid(ccuid);
	sprintf(g_clientId, CLIENTID, ccuid,timestamp);
	root=cJSON_CreateObject();
	cJSON_AddStringToObject(root, "deviceCode", ccuid);
	cJSON_AddStringToObject(root, "productCode",KK_CCU_PRODUCTID);
	cJSON_AddStringToObject(root, "clientId",g_clientId);
	fp = fopen(KK_DEVICESECRET_PATH, "r");
	if(fp != NULL){
		fread(deviceSecret,1,sizeof(deviceSecret),fp);
		fclose(fp);
	}
	cJSON_AddStringToObject(root, "deviceSecret",deviceSecret);

	out=cJSON_Print(root);
	//cJSON_Minify(out);
	cJSON_Delete(root);
    INFO_PRINT("--------------------->kk_create_get_jwt_info:%s\n",out);
    return out;
}
static int kk_send_register_info(int sockfd,const char * path,int type)
{
    char str1[1024], str2[1024],str[16] = {0};
    char tmp[128] = {0};
    int len = 0;
    memset(str2, 0, sizeof(str2));
    if(type == 0){
        strcat(str2, kk_create_register_info());
    }else{
        strcat(str2, kk_create_get_jwt_info());
    }
    len = strlen(str2);
    sprintf(str, "%d", len);

    memset(str1, 0, sizeof(str1));
    memset(tmp, 0, sizeof(tmp));   
    sprintf(tmp, "POST %s HTTP/1.1\n", path);     
    strcat(str1, tmp);
    memset(tmp, 0, sizeof(tmp)); 
    sprintf(tmp, "Host: %s:%d\n",IPSTR, PORT);  
    strcat(str1, tmp);
    strcat(str1, "Content-Type: application/json\n");
    strcat(str1, "Content-Length: ");
    strcat(str1, str);
    strcat(str1, "\n\n");
    //str2的值为post的数据
    strcat(str1, str2);
    strcat(str1, "\r\n\r\n");
    INFO_PRINT("%s\n",str1);   
    return write(sockfd,str1,strlen(str1));
}
int kk_start_ccu_register(void)
{
    int sockfd, ret, i, h;
    struct sockaddr_in servaddr;
    char buf[1024] = {0}, buf2[1024] = {0};
    fd_set   t_set1;
    struct timeval  tv;
    int registerType = 0;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
        INFO_PRINT("---socket error!\n");
        return -1;
    };
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    if (inet_pton(AF_INET, IPSTR, &servaddr.sin_addr) <= 0 ){
        INFO_PRINT("--inet_pton error!\n");
        close(sockfd);
        return -1;
    };

    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
        INFO_PRINT("connect error!\n");
        close(sockfd);
        return -1;
    }
    INFO_PRINT("connect successful\n");
    ret = kk_send_register_info(sockfd,"/deviceRegister/register",0);
    if (ret < 0) {
            INFO_PRINT("send fail\n");
            close(sockfd);
            return -1;
    }else{
            INFO_PRINT("send success send %d bytes！\n\n", ret);
    }
    registerType = 1;
    FD_ZERO(&t_set1);
    FD_SET(sockfd, &t_set1);
    tv.tv_sec= 1;
    tv.tv_usec= 0;
    h= 0;
    while(1){
        h = select(sockfd +1, &t_set1, NULL, NULL, &tv);
        //if (h == 0) continue;
        if (h == -1) {
            close(sockfd);
            return -1;
        };
        if ( FD_ISSET(sockfd, &t_set1) ){
                memset(buf, 0, 1024);
                memset(buf2, 0, 1024);
                i= read(sockfd, buf, 1024);
                if (i==0){
                    close(sockfd);
                    return -1;
                }
                INFO_PRINT("\n get %s\n",buf);             
                if(registerType == 1){//get deviceSecret
                    char* deviceSecret = strstr(buf,"deviceSecret");
                    if(deviceSecret != NULL){ 
                         char* start = strchr(buf,'{');
                         char* end = strrchr(buf,'}');
                         strncpy(buf2,start,end-start+1);
                         INFO_PRINT("[%s][%d]buf2:%s\n",__FUNCTION__,__LINE__,buf2);
                        ret = kk_parse_deviceSecret(buf2);
                        if(ret == 0){
                            ret = kk_send_register_info(sockfd,"/auth/generateJwt",1);
                            if (ret < 0) {
                                INFO_PRINT("send fail'\n");
                                close(sockfd);
                                return -1;
                            }else{
                                printf("send success send %d bytes！\n\n", ret);
                            }
                            registerType = 2;                            
                        }
                    }
                }else if(registerType == 2){
                    char* jwt = strstr(buf,"jwt");
                    if(jwt != NULL){
                        char* start = strchr(buf,'{');
                        char* end = strrchr(buf,'}');
                        if(start != NULL && end != NULL){
                            strncpy(buf2,start,end-start+1);
                            ret = kk_parse_token(buf2);
                            if(ret == 0){
                                close(sockfd);
                                return 0;
                            }
                        }
                    }
                }
        }
    }
    close(sockfd);
    return -1;
}