#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <net/if.h>
#include <netdb.h>
#include <syslog.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h> 
#include <pthread.h>
#include "nn.h"
#include "pair.h"
#include "oled_dp.h"
#include "QR_Encode.h"
#include "cJSON.h"
#include "main.h"
#include "kk_log.h"
#define MAC_SIZE    18
#define IP_SIZE     16

#define IN_PORT     13000
#define OLED_NANOMSG_SOCKET_ADDR "ipc:///tmp/oled_pair.ipc"
typedef struct
{
    char ccu_id[16];
    char ccu_name[32];
    char ccu_version[32];
    char ccu_lan[16];
    int ccu_show_qr;
    char ccu_qr_str[128];
    int ccu_status;
    char ccu_udp_path[128];
    int hw_version;
} oled_config_t;

static char oled_cfg_default[] = "\
{\
    \"args\": {\
        \"ccu\": {\
            \"id\": \"*\",\
            \"version\": \"*\",\
            \"name\": \"*\"\
        },\
        \"net\": {\
            \"interface\": \"eth1\"\
        },\
        \"qr_code\": {\
            \"string\": \"http://www.ikonke.com\",\
            \"show\": true\
        }\
    },\
    \"config\":\
    {\
        \"path\": \"ipc:///tmp/oled_pair.ipc\",\
        \"hw_ver\": 1\
    }\
}";

oled_config_t g_oled_config;
extern int m_nSymbleSize;
extern uint8_t m_byModuleData[MAX_MODULESIZE][MAX_MODULESIZE];

uint8_t qr_change = 1;

uint8_t qr_img[128][128] = {0};
uint8_t oled_img[1024] = {0};

char cfg_file_name[80] = "/usr/kk/oled.config";
char ko_file_name[80] = "ssd1306-revision.ko";

void get_qr_img(char *str)
{
    int i = 0;
    int j = 0;
    int k = 0;

    memset((uint8_t *)m_byModuleData, 0, MAX_MODULESIZE * MAX_MODULESIZE);
    memset((uint8_t *)qr_img, 0xff, 128 * 128);
    memset((uint8_t *)oled_img, 0xff, 1024);
    
    if (strlen(str) > 88)
    {
        str[88] = 0;
    }

    EncodeData(str);

    #if 0
    {
        int l = 0;
        OLED_LOG_DBG("\r\n\r\nccu : %s\r\n\r\n", str);

        for (i = 0; i < m_nSymbleSize + 4; i ++)
        {
            OLED_LOG_DBG("%s", "$$");
        }
        OLED_LOG_DBG("\r\n");

        for (i = 0; i < m_nSymbleSize + 4; i ++)
        {
            OLED_LOG_DBG("%s", "$$");
        }
        OLED_LOG_DBG("\r\n");

        for (i = 0; i < m_nSymbleSize; i ++)
        {
            OLED_LOG_DBG("%s", "$$$$");
            for (j = 0; j < m_nSymbleSize; j ++)
            {
                // OLED_LOG_DBG("%s", m_byModuleData[i][j + l] == 0 ? "▇" : " ");
                // OLED_LOG_DBG("%s", m_byModuleData[i][j + l] == 0 ? "▇" : " ");
                OLED_LOG_DBG("%s", m_byModuleData[i][j + l] == 1 ? "  " : "$$");
            }
            OLED_LOG_DBG("%s", "$$$$");
            OLED_LOG_DBG("\r\n");
        }
        for (i = 0; i < m_nSymbleSize + 4; i ++)
        {
            OLED_LOG_DBG("%s", "$$");
        }
        OLED_LOG_DBG("\r\n");

        for (i = 0; i < m_nSymbleSize + 4; i ++)
        {
            OLED_LOG_DBG("%s", "$$");
        }
        OLED_LOG_DBG("\r\n");

        OLED_LOG_DBG("\r\n\r\n\r\n\r\n\r\n");
    }
    #endif

    #if 1
    {
        if (m_nSymbleSize < 33)
        {
            int m = (33 - m_nSymbleSize) / 2;

            for (i = m_nSymbleSize; i >= 0; i --)
            {
                for (j = m_nSymbleSize; j >= 0; j --)
                {
                    m_byModuleData[i + m][j + m] = m_byModuleData[i][j];
                }
            }

            for (i = 0; i < 33; i ++)
            {
                for (j = 0; j < 33; j ++)
                {
                    if (i < m || j < m)
                    {
                        m_byModuleData[i][j] = 0;
                    }
                }
            }
        }
    }
    #endif

    for (i = 0; i < 33; i ++)
    {
        qr_img[2 * i + 0][0] = 0;
        qr_img[2 * i + 0][1] = 0;
        qr_img[2 * i + 1][0] = 0;
        qr_img[2 * i + 1][1] = 0;

        for (j = 1; j < 33 + 1; j ++)
        {
            qr_img[2 * i + 0][2 * j + 0] = m_byModuleData[i][j - 1];
            qr_img[2 * i + 0][2 * j + 1] = m_byModuleData[i][j - 1];
            qr_img[2 * i + 1][2 * j + 0] = m_byModuleData[i][j - 1];
            qr_img[2 * i + 1][2 * j + 1] = m_byModuleData[i][j - 1];
        }

        qr_img[2 * i + 0][2 * (33 + 1) + 0] = 0;
        qr_img[2 * i + 0][2 * (33 + 1) + 1] = 0;
        qr_img[2 * i + 1][2 * (33 + 1) + 0] = 0;
        qr_img[2 * i + 1][2 * (33 + 1) + 1] = 0;
    }

    i = 1;
    j = 0;
    k = 0;

    for (k = 0; k < 1024; k ++)
    {
        uint8_t bit = 0;

        bit = qr_img[i + 0][j] << 0 | qr_img[i + 1][j] << 1 | qr_img[i + 2][j] << 2 | qr_img[i + 3][j] << 3 \
            | qr_img[i + 4][j] << 4 | qr_img[i + 5][j] << 5 | qr_img[i + 6][j] << 6 | qr_img[i + 7][j] << 7;

        oled_img[k] = ~bit;
        j ++;

        if (j >= 70)
        {
            i += 8;
            j = 0;
        }

        if (i >= 65)
        {
            return;
        }
    }
}

static void load_cfg_str_from_file(char *cfg_file_name, char *str)
{
    char *temp_str = NULL;
    char line_buf[512] = {0};

    FILE *fp = fopen(cfg_file_name, "r");
    
    if (fp == NULL)
    {
        INFO_PRINT("Unable to open file: %s\n", cfg_file_name);
        INFO_PRINT("Use default cfg\r\n");
        strcpy(str, oled_cfg_default);
        return;
    }

    if (str == NULL)
    {
        INFO_PRINT("string is null\r\n");
        return;
    }

    temp_str = str;

    while (!feof(fp))
    {
        int read_cnt = 0;

        memset(line_buf, 0, sizeof(line_buf));

        read_cnt = fread(line_buf, 1, sizeof(line_buf), fp);

        OLED_LOG_DBG("read_cnt %d \r\n", read_cnt);

        if (read_cnt > 0)
        {
            memcpy(temp_str, line_buf, read_cnt);
            temp_str += read_cnt;
        }
        else
        {
            break;
        }
    }
    
    fclose(fp);
    return;
}

static void save_cfg_str_to_file(const char *cfg_file_name, char *str)
{
    FILE *file_fd = NULL;

    file_fd = fopen(cfg_file_name, "w+");

    if (NULL == file_fd)
    {
        INFO_PRINT("config %s open error\n", cfg_file_name);
        return;
    }

    fwrite(str, 1, strlen(str), file_fd);

    fflush(file_fd);
    fclose(file_fd);
}

static int load_cfg_from_str(char *cfg_json_str, oled_config_t *cfg)
{
    int ret = 0;

    cJSON *root = cJSON_Parse(cfg_json_str);
    if (root)
    {
        #if 1 // args
        {
            cJSON *args = cJSON_GetObjectItemCaseSensitive(root, "args");
            
            if (args)
            {
                #if 1 // ccu
                {
                    cJSON *ccu = cJSON_GetObjectItemCaseSensitive(args, "ccu");

                    if (ccu)
                    {
                        #if 1 // id
                        {
                            cJSON *id = cJSON_GetObjectItemCaseSensitive(ccu, "id");

                            if (id)
                            {
                                if (strncmp(cfg->ccu_id, id->valuestring, sizeof(cfg->ccu_id)))
                                {
                                    OLED_LOG_DBG("update ccu id from %s to %s\r\n", cfg->ccu_id, id->valuestring);
                                    strncpy(cfg->ccu_id, id->valuestring, sizeof(cfg->ccu_id));
                                    ret = 1;
                                }
                                
                                OLED_LOG_DBG("ccu id %s\r\n", cfg->ccu_id);
                            }
                        }
                        #endif

                        #if 1 // version
                        {
                            cJSON *version = cJSON_GetObjectItemCaseSensitive(ccu, "version");

                            if (version)
                            {
                                if (strncmp(cfg->ccu_version, version->valuestring, sizeof(cfg->ccu_version)))
                                {
                                    OLED_LOG_DBG("update ccu version from %s to %s\r\n", cfg->ccu_version, version->valuestring);
                                    strncpy(cfg->ccu_version, version->valuestring, sizeof(cfg->ccu_version));
                                    ret = 1;
                                }
                                
                                OLED_LOG_DBG("ccu version %s\r\n", cfg->ccu_version);
                            }
                        }
                        #endif

                        #if 1 // name
                        {
                            cJSON *name = cJSON_GetObjectItemCaseSensitive(ccu, "name");

                            if (name)
                            {
                                if (strncmp(cfg->ccu_name, name->valuestring, sizeof(cfg->ccu_name)))
                                {
                                    INFO_PRINT("update ccu name from %s to %s\r\n", cfg->ccu_name, name->valuestring);
                                    strncpy(cfg->ccu_name, name->valuestring, sizeof(cfg->ccu_name));
                                    ret = 1;
                                }
                                
                                INFO_PRINT("ccu name %s\r\n", cfg->ccu_name);
                            }
                        }
                        #endif

                        #if 1 // st
                        {
                            cJSON *st = cJSON_GetObjectItemCaseSensitive(ccu, "status");

                            if (st)
                            {
                                if (cfg->ccu_status != atoi(st->valuestring))
                                {
                                    INFO_PRINT("update ccu status from %d to %d\r\n", cfg->ccu_status, atoi(st->valuestring));
                                    cfg->ccu_status = atoi(st->valuestring);
                                    ret = 1;
                                }

                                INFO_PRINT("ccu status %d\r\n", cfg->ccu_status);
                            }
                        }
                        #endif
                    }
                }
                #endif

                #if 1 // net
                {
                    cJSON *net = cJSON_GetObjectItemCaseSensitive(args, "net");

                    if (net)
                    {
                        cJSON *interface = cJSON_GetObjectItemCaseSensitive(net, "interface");

                        if (interface)
                        {
                            if (strncmp(cfg->ccu_lan, interface->valuestring, sizeof(cfg->ccu_lan)))
                            {
                                INFO_PRINT("update ccu lan name from %s to %s\r\n", cfg->ccu_lan, interface->valuestring);
                                strncpy(cfg->ccu_lan, interface->valuestring, sizeof(cfg->ccu_lan));
                                ret = 1;
                            }
                            
                            INFO_PRINT("net interface %s\r\n", cfg->ccu_lan);
                        }
                    }
                }
                #endif

                #if 1 // qr
                {
                    cJSON *qr_code = cJSON_GetObjectItemCaseSensitive(args, "qr_code");

                    if (qr_code)
                    {
                        #if 1 // qr str
                        {
                            cJSON *qr_str = cJSON_GetObjectItemCaseSensitive(qr_code, "string");
                            
                            if (qr_str)
                            {
                                if (strncmp(cfg->ccu_qr_str, qr_str->valuestring, sizeof(cfg->ccu_qr_str)))
                                {
                                    OLED_LOG_DBG("update ccu qr from %s to %s\r\n", cfg->ccu_qr_str, qr_str->valuestring);
                                    strncpy(cfg->ccu_qr_str, qr_str->valuestring, sizeof(cfg->ccu_qr_str));
                                    ret = 1;
                                }

                                INFO_PRINT("qr string %s\r\n", cfg->ccu_qr_str);
                            }
                        }
                        #endif

                        #if 1 // qr show
                        {
                            cJSON *qr_show = cJSON_GetObjectItemCaseSensitive(qr_code, "show");

                            if (qr_show)
                            {
                                if (cfg->ccu_show_qr != qr_show->valueint)
                                {
                                    INFO_PRINT("update ccu show_qr from %d to %d\r\n", cfg->ccu_show_qr, qr_show->valueint);
                                    cfg->ccu_show_qr = qr_show->valueint;
                                    ret = 1;
                                }

                                INFO_PRINT("qr show %d\r\n", cfg->ccu_show_qr);
                            }
                        }
                        #endif
                    }
                }
                #endif
            }
        }
        #endif

        #if 1 // config
        {
            cJSON *config = cJSON_GetObjectItemCaseSensitive(root, "config");

            if (config)
            {
                #if 1 // path
                {
                    cJSON *path = cJSON_GetObjectItemCaseSensitive(config, "path");
                    
                    if (path)
                    {
                        if (strncmp(cfg->ccu_udp_path, path->valuestring, sizeof(cfg->ccu_udp_path)))
                        {
                            INFO_PRINT("update ccu udp path from %s to %s\r\n", cfg->ccu_udp_path, path->valuestring);
                            strncpy(cfg->ccu_udp_path, path->valuestring, sizeof(cfg->ccu_udp_path));
                            ret = 1;
                        }
                        
                        INFO_PRINT("ccu udp path %s\r\n", cfg->ccu_udp_path);
                    }
                }
                #endif

                #if 1 // version
                {
                    cJSON *version = cJSON_GetObjectItemCaseSensitive(config, "hw_ver");

                    if (version)
                    {
                        if (cfg->hw_version != version->valueint)
                        {
                            INFO_PRINT("update ccu hw_version from %d to %d\r\n", cfg->hw_version, version->valueint);
                            cfg->hw_version = version->valueint;
                            ret = 1;
                        }
                        
                        INFO_PRINT("ccu hw version %d\r\n", cfg->hw_version);
                    }
                }
                #endif
            }
        }
        #endif
        
        cJSON_Delete(root);
    }
    else
    {
        ret = -1;
    }

    return ret;
}

static int save_cfg_to_json_str(oled_config_t *cfg, char *str)
{
    int ret = 0;
    char *str_tmp = NULL;
    cJSON *root = cJSON_CreateObject();

    if (root)
    {
        {
            cJSON *args = cJSON_CreateObject();

            if (args)
            {
                cJSON_AddItemToObject(root, "args", args);

                cJSON *ccu = cJSON_CreateObject();

                if (ccu)
                {
                    cJSON_AddItemToObject(args, "ccu", ccu);

                    cJSON_AddStringToObject(ccu, "id", cfg->ccu_id);
                    cJSON_AddStringToObject(ccu, "version", cfg->ccu_version);
                    cJSON_AddStringToObject(ccu, "name", cfg->ccu_name);
                }
                else
                {
                    cJSON_Delete(root);
                    ret = -1;
                }

                cJSON *net = cJSON_CreateObject();

                if (net)
                {
                    cJSON_AddItemToObject(args, "net", net);
                    cJSON_AddStringToObject(net, "interface", cfg->ccu_lan);
                }
                else
                {
                    cJSON_Delete(root);
                    ret = -1;
                }

                cJSON *qr_code = cJSON_CreateObject();

                if (qr_code)
                {
                    cJSON_AddItemToObject(args, "qr_code", qr_code);

                    cJSON_AddStringToObject(qr_code, "string", cfg->ccu_qr_str);
                    cJSON_AddBoolToObject(qr_code, "show", (cfg->ccu_show_qr == 1) ? 1 : 0);
                }
                else
                {
                    cJSON_Delete(root);
                    ret = -1;
                }
            }
            else
            {
                cJSON_Delete(root);
                ret = -1;
            }

            cJSON *config = cJSON_CreateObject();

            if (config)
            {
                cJSON_AddItemToObject(root, "config", config);

                cJSON_AddStringToObject(config, "path", cfg->ccu_udp_path);
                cJSON_AddNumberToObject(config, "hw_ver", cfg->hw_version);
            }
            else
            {
                cJSON_Delete(root);
                ret = -1;
            }
        }

        str_tmp = cJSON_PrintUnformatted(root);
        INFO_PRINT("JSON %s\r\n", str_tmp);
        strcpy(str, str_tmp);
        free(str_tmp);
        cJSON_Delete(root);
    }
    else
    {
        ret = -1;
    }

    return ret;
}

pthread_t key_thread;
pthread_attr_t key_attr;

pthread_t oled_dp_thread;
pthread_attr_t oled_dp_attr;

pthread_t nanomsg_recv_thread;
pthread_attr_t nanomsg_recv_attr;

pthread_mutex_t data_mutex;

uint32_t run_level_1 = 3600;

uint32_t run_time = 0;

int key_pressed = 0;

int nanomsg_sock_init(void)
{
    int sockfd;

    /* Create Socket*/
    sockfd = nn_socket(AF_SP, NN_PAIR);
    if (sockfd == -1)
    {
        INFO_PRINT("Failed to create socket\r\n");
        return -1;
    }
    nn_bind (sockfd, OLED_NANOMSG_SOCKET_ADDR);
    return sockfd;
}

void *nanomsg_recv(void *rec_data)
{
    char *buf = NULL;
    int sockfd = nanomsg_sock_init();
    if(sockfd < 0){
        INFO_PRINT("Failed to nanomsg_sock_init\r\n");
        return NULL;
    }
    char nanomsg_str[1500] = {0};
    int nanomsg_len = 0;

    while(1)
    {
        int sz = nn_recv(sockfd, &buf, NN_MSG, 0);
        if (sz > 0){
            if (sz >= 1499){
                sz = 1499;
            }     
            if (sz != nanomsg_len || strcmp(buf, nanomsg_str) != 0)
            {
                memset(nanomsg_str, 0, sizeof(nanomsg_str));

                nanomsg_len = sz;
                memcpy(nanomsg_str, buf, nanomsg_len);     
                pthread_mutex_lock(&data_mutex);
                {
                    int json_result = load_cfg_from_str(nanomsg_str, &g_oled_config);

                    INFO_PRINT("JSON len %d \r\n%s\r\nresult %d\r\n", strlen(nanomsg_str), nanomsg_str, json_result);
                    
                    if (json_result == 1)
                    {
                        #if 1
                        {
                            char str[1024] = {0};

                            if (save_cfg_to_json_str(&g_oled_config, str) >= 0)
                            {
                                save_cfg_str_to_file(cfg_file_name, str);
                            }
                        }
                        #endif
                        get_qr_img(g_oled_config.ccu_qr_str);
                        qr_change = 1;
                    }
                }

                pthread_mutex_unlock(&data_mutex);
            }
            
        }
        //usleep(500000);
    }
}

int oled_dp_qr(void)
{
    int key = 0;

    if (g_oled_config.ccu_show_qr)
    {
        int delay_cnt = 0;
        qr_change = 1;

        while (delay_cnt ++ < 10)
        {
            if (qr_change == 1)
            {
                clear_screen();
                
                pthread_mutex_lock(&data_mutex);
                {
                    qr_change = 0;
                    show_bmp_set(29, 0, 29 + 70, 64, oled_img, 70 * 8);
                }
                pthread_mutex_unlock(&data_mutex);
            }

            for (int c = 0; c < 10; c ++)
            {
                usleep(100000);

                if (qr_change == 1)
                {
                    continue;
                }

                if (run_time > run_level_1)
                {
                    if (key == 0 && key_pressed > 20)
                    {
                        key = 1;
                    }

                    if (key == 1 && key_pressed == 0)
                    {
                        return 1;
                    }
                }
            }

            run_time ++;
        }
    }

    return 0;
}

int oled_dp_info(void)
{
    int key = 0;

    char str[32] = {0};
    char ip[32] = "0.0.0.0";

    #if 1
    {
        int sd;
        struct sockaddr_in sin;
        struct ifreq ifr;

        sd = socket(AF_INET, SOCK_DGRAM, 0);

        if (-1 == sd)
        {
            INFO_PRINT("socket error: %s\n", strerror(errno));
        }
        else
        {
            strncpy(ifr.ifr_name, g_oled_config.ccu_lan, IFNAMSIZ);
            
            ifr.ifr_name[IFNAMSIZ - 1] = 0;

            if (ioctl(sd, SIOCGIFADDR, &ifr) < 0)
            {
                INFO_PRINT("ioctl error: %s\n", strerror(errno));
                snprintf(ip, IP_SIZE, "0.0.0.0");
            }
            else
            {
                memcpy(&sin, &ifr.ifr_addr, sizeof(sin));
                snprintf(ip, IP_SIZE, "%s", inet_ntoa(sin.sin_addr));
            }

            close(sd);
        }
    }
    #endif

    time_t now;
    struct tm *timenow;

    {
        time(&now);
        timenow = localtime(&now);
    }
    
    int i = 0;

    int hour = timenow->tm_hour;
    int min = timenow->tm_min;

    qr_change = 1;

    while (i ++ < 9)
    {
        if (qr_change == 1)
        {
            pthread_mutex_lock(&data_mutex);
            
            {
                qr_change = 0;

                clear_screen();

                sprintf(str, "%s", ip);
                show_str_set(str, 0, 0, show_6x8_mode);

                #if defined (AR9331)
                {
                    show_str_set("3", 120, 0, show_6x8_mode);
                }
                #elif defined (AR9533)
                {
                    show_chn_set(104, 0, 12, 12);
                    show_chn_set(116, 0, 13, 12);
                }
                #endif
                
                sprintf(str, "%d-%d-%d", timenow->tm_year + 1900, timenow->tm_mon + 1, timenow->tm_mday);
                show_str_set(str, 35, 2, show_6x8_mode);

                sprintf(str, "%02d%c%02d", timenow->tm_hour, timenow->tm_sec % 2 == 1 ? ':' : ' ', timenow->tm_min);
                show_str_set(str, 45, 3, show_8x16_mode);

                sprintf(str, "%s(%s)", g_oled_config.ccu_id, g_oled_config.ccu_version);
                show_str_set(str, 0, 7, show_6x8_mode);
                printf("g_oled_config.ccu_status:%d\n",g_oled_config.ccu_status);
                if (g_oled_config.ccu_status == 0)
                {
                    show_chn_set(104, 6, 4, 12);
                    show_chn_set(116, 6, 5, 12);
                }
                else if (g_oled_config.ccu_status == 1)
                {
                    show_chn_set(104, 6, 8, 12);
                    show_chn_set(116, 6, 9, 12);
                }
                else if (g_oled_config.ccu_status == 2)
                {
                    show_chn_set(104, 6, 10, 12);
                    show_chn_set(116, 6, 11, 12);
                }
                else
                {
                    sprintf(str, "E%02d", g_oled_config.ccu_status);
                    show_str_set(str, 92, 7, show_6x8_mode);
                }
            }

            pthread_mutex_unlock(&data_mutex);
        }

        {
            time(&now);
            timenow = localtime(&now);
        }

        if (hour != timenow->tm_hour || min != timenow->tm_min)
        {
            hour = timenow->tm_hour;
            min = timenow->tm_min;

            sprintf(str, "%02d%c%02d", timenow->tm_hour, timenow->tm_sec % 2 == 1 ? ':' : ' ', timenow->tm_min);
            show_str_set(str, 45, 3, show_8x16_mode);
        }
        else
        {
            char str_ch[8] = {0};
            sprintf(str_ch, "%c", timenow->tm_sec % 2 == 1 ? ':' : ' ');
            show_str_set(str_ch, 45 + 8 * 2, 3, show_8x16_mode);
        }
        
        for (int c = 0; c < 10; c ++)
        {
            usleep(100000);

            if (qr_change == 1)
            {
                continue;
            }

            if (run_time > run_level_1)
            {
                if (key == 0 && key_pressed > 20)
                {
                    key = 1;
                }

                if (key == 1 && key_pressed == 0)
                {
                    return 1;
                }
            }
        }

        run_time ++;
    }

    return 0;
}

void *oled_dp(void *parameter)
{
    int key = 0;

    wakeup_screen();
    clear_screen();

    while (1)
    {
        if (run_time < run_level_1)
        {
            oled_dp_qr();
            oled_dp_info();
        }
        else
        {
            if (g_oled_config.hw_version == 2)
            {
                clear_screen();

                while (1)
                {
                    usleep(1000 * 20);

                    if (key == 0 && key_pressed > 20)
                    {
                        key = 1;
                    }

                    if (key == 1 && key_pressed == 0)
                    {
                        key = 0;
                        break;
                    }
                }

                int delay = 3;

                while (1)
                {
                    delay --;

                    if (oled_dp_qr())
                    {
                        delay = 3;
                    }

                    if (oled_dp_info())
                    {
                        delay = 0;
                    }
                    
                    if (delay == 0)
                    {
                        break;
                    }
                }
            }
            else
            {
                oled_dp_qr();
                oled_dp_info();

                clear_screen();

                for (int i = 0; i < 400; i++)
                {
                    usleep(1000 * 100);

                    if (i % 10 == 0)
                    {
                        run_time ++;
                    }
                }
            }
        }
    }
}

int key_file_fd = -1;

void key_init(int gpio_num)
{
    char str[120] = {0};
    int re = 0;

    memset(str, 0, sizeof(str));
    snprintf(str, sizeof(str), "/sys/class/gpio/gpio%d", gpio_num);

    if ((access(str, F_OK)) == -1)
    {
        snprintf(str, sizeof(str), "echo %d > /sys/class/gpio/export", gpio_num);
        re = system(str);

        if (re != 0)
        {
            return;
        }
    }

    snprintf(str, sizeof(str), "echo in > /sys/class/gpio/gpio%d/direction", gpio_num);
    re = system(str);

    if (re != 0)
    {
        return;
    }
}

void key_file_init(int gpio_num)
{
    char file_str[80] = {0};

    snprintf(file_str, sizeof(file_str), "/sys/class/gpio/gpio%d/value", gpio_num);

    key_file_fd = open(file_str, O_RDONLY);
    
    if (key_file_fd == -1)
    {
        OLED_LOG_DBG("Unable to open file: %s\n", file_str);
        return;
    }

    return;
}

int key_read(void)
{
    int ret = -1;
    char line_buf[80] = {0};

    if (key_file_fd < 0)
    {
        return ret;
    }

    memset(line_buf, 0, sizeof(line_buf));

    if (lseek(key_file_fd, 0, SEEK_SET) < 0)
    {
        return ret;
    }

    if (read(key_file_fd, line_buf, sizeof(line_buf)) < 0)
    {
        return ret;
    }

    if (strlen(line_buf) > 0)
    {
        ret = atoi(line_buf);
    }

    return ret;
}

int key_file_read(int gpio_num)
{
    int ret = -1;
    char file_str[80] = {0};
    char line_buf[80] = {0};
    FILE *fp = NULL;

    snprintf(file_str, sizeof(file_str), "/sys/class/gpio/gpio%d/value", gpio_num);

    fp = fopen(file_str, "r");
    
    if (fp == NULL)
    {
        OLED_LOG_DBG("Unable to open file: %s\n", file_str);
        return ret;
    }

    memset(line_buf, 0, sizeof(line_buf));
    fgets(line_buf, sizeof(line_buf), fp);

    if (strlen(line_buf) > 0)
    {
        ret = atoi(line_buf);
    }
    
    fclose(fp);
    return ret;
}

void *key_process(void *parameter)
{
    #if defined (AR9331)
    {
    }
    #elif defined (AR9533)
    {
        key_init(12);
        key_file_init(12);    
    }
    #endif

    while (1)
    {
        // OLED_LOG_DBG("gpio 12 is %d\r\n", key_file_read(12));
        // OLED_LOG_DBG("gpio 12 is %d\r\n", key_read());

        usleep(10000);

        if (key_read() == 0)
        {
            if (key_pressed == 0)
            {
                INFO_PRINT("Key pressed\r\n");
            }

            key_pressed += 10;
        }
        else
        {
            if (key_pressed > 0)
            {
                if (g_oled_config.hw_version == 1)
                {
                    g_oled_config.hw_version = 2;

                    #if 1
                    {
                        char str[1024] = {0};

                        if (save_cfg_to_json_str(&g_oled_config, str) >= 0)
                        {
                            save_cfg_str_to_file(cfg_file_name, str);
                        }
                    }
                    #endif
                }

                INFO_PRINT("Key released\r\n");
                key_pressed = 0;
            }
        }
    }
}

#if 1

int main(int argc, char **argv)
{
    void *status;
    int rc;

    openlog("OLED", LOG_PID, LOG_USER);
    memset((uint8_t *)oled_img, 0xff, 1024);

    #if 1 // process input args
    {
        int opt;
        char *opt_str = "f:k:";
        
        while ((opt = getopt(argc, argv, opt_str)) !=  -1)
        {
            if (opt == 'f')
            {
                strncpy(cfg_file_name, optarg, sizeof(cfg_file_name));
            }
            else if (opt == 'k')
            {
                strncpy(cfg_file_name, optarg, sizeof(cfg_file_name));
            }
        }

        if ((strlen(cfg_file_name) == 0) || (strlen(ko_file_name) == 0))
        {
            INFO_PRINT("Usage : %s [-f cfg_file_name] [-k ko_file_nmae]\r\n", argv[0]);
            return 0;
        }
    }
    #endif

    #if 1 // process file cfg
    {
        char *str = malloc(2000);

        if (str)
        {
            load_cfg_str_from_file(cfg_file_name, str);
            INFO_PRINT("%d %s\r\n", strlen(str), str);
            load_cfg_from_str(str, &g_oled_config);
            get_qr_img(g_oled_config.ccu_qr_str);
            free(str);
        }
        else
        {
            OLED_LOG_DBG("malloc memory for read cfg failed\r\n");
            return 0;
        }
    }
    #endif
    pthread_mutex_init(&data_mutex, NULL);
    #if 1 // init udp thread
    {
        /*创建线程*/
        pthread_attr_init(&nanomsg_recv_attr);
        size_t s = 1500;
        pthread_attr_setstacksize(&nanomsg_recv_attr, s);
        pthread_attr_setdetachstate(&nanomsg_recv_attr, PTHREAD_CREATE_JOINABLE);
        rc = pthread_create(&nanomsg_recv_thread, &nanomsg_recv_attr, nanomsg_recv, (void *)NULL);   //收数据
        if (rc)
        {
            INFO_PRINT("Error : unable to create thread udp_recv \r\n");
            return 0;
        }
    }
    #endif

    #if 1 // init oled thread
    {
        pthread_attr_init(&oled_dp_attr);

        size_t s = 1500;
        pthread_attr_setstacksize(&oled_dp_attr, s);

        pthread_attr_setdetachstate(&oled_dp_attr, PTHREAD_CREATE_JOINABLE);

        rc = pthread_create(&oled_dp_thread, &oled_dp_attr, oled_dp, NULL);

        if (rc)
        {
            INFO_PRINT("Error : unable to create thread oled_dp\r\n");
            return 0;
        }
    }
    #endif

    #if 1
    {
        pthread_attr_init(&key_attr);

        size_t s = 1500;
        pthread_attr_setstacksize(&key_attr, s);

        pthread_attr_setdetachstate(&key_attr, PTHREAD_CREATE_JOINABLE);

        rc = pthread_create(&key_thread, &key_attr, key_process, NULL);

        if (rc)
        {
            INFO_PRINT("Error : unable to create thread key_process\r\n");
            return 0;
        }
    }
    #endif

    pthread_attr_destroy(&nanomsg_recv_attr);
    pthread_attr_destroy(&oled_dp_attr);
    pthread_attr_destroy(&key_attr);

    {
        rc = pthread_join(nanomsg_recv_thread, &status);
        
        if (rc)
        {
            INFO_PRINT("Error : unable to join udp_recv\r\n");
            return 0;
        }
    }

    {
        rc = pthread_join(oled_dp_thread, &status);

        if (rc)
        {
            INFO_PRINT("Error : unable to join oled_dp\r\n");
            return 0;
        }
    }
    {
        rc = pthread_join(key_thread, &status);

        if (rc)
        {
            INFO_PRINT("Error : unable to join key_thread\r\n");
            return 0;
        }
    }

    pthread_exit(NULL);
    return 0;
}

#else

int main(int argc, char **argv)
{
    if (argc >= 2)
    {
        get_qr_img(argv[1]);
    }
}

#endif


