#include "msystem.h"
#include "mgpio.h"
#include "mlogger.h"
#include "ccu_config.h"
#include "ocpp16_client.h"
#include "ocpp16.h"
#include "ocpp16_encode.h"
#include "ocpp16_decode.h"
#include "ocpp16_configkey.h"
#include "ocpp16_authorization.h"
#include "ocpp16_ChargingProfiles.h"
#include "ocpp16_OfflineChargingRecords.h"
#include "interface.h"
#include "ccu_bsp.h"
#include "pcutc.h"
#include "pcuhw.h"

static char app_version[128] = {0};
static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
char* formatAppVersion(void)
{
	int i;
	int month, mday, year, hour, min, sec;
    #define ASCII_CODE(x)  ((x)==32? 0:((x)-'0'))
    mday = ASCII_CODE(__DATE__[4])*10 + ASCII_CODE(__DATE__[5]);
    year = ASCII_CODE(__DATE__[7])*1000 + ASCII_CODE(__DATE__[8])*100 + ASCII_CODE(__DATE__[9])*10 + ASCII_CODE(__DATE__[10]);
    hour = ASCII_CODE(__TIME__[0])*10 + ASCII_CODE(__TIME__[1]);
    min  = ASCII_CODE(__TIME__[3])*10 + ASCII_CODE(__TIME__[4]);
    sec  = ASCII_CODE(__TIME__[6])*10 + ASCII_CODE(__TIME__[7]);
    #undef ASCII_CODE
  	for (i = 0; i < 12; i++)
  	{
    	if (strncmp(__DATE__, months[i], 3)==0)
    	{
      		month = i + 1;
     	 	break;
    	}
  	}

    snprintf(app_version, 128, "app_CCU_V1R1B%04d%02d%02d %02d:%02d:%02d", year, month, mday, hour, min, sec);
    return app_version;
}

static long *lock_count;
static pthread_mutex_t *lock_cs;
void pthread_locking_callback(int mode, int type, const char *file, int line)
{
	if (mode & CRYPTO_LOCK)
	{
        lock_count[type]++;
		pthread_mutex_lock(&(lock_cs[type]));
	}
	else
	{
		pthread_mutex_unlock(&(lock_cs[type]));
	}
}

pthread_t pthreads_thread_id(void)
{
    pthread_t ret;
    ret=pthread_self();
    return(ret);
}

static void CRYPTO_thread_setup(void)
{
	int i;
	lock_cs = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
    lock_count=OPENSSL_malloc(CRYPTO_num_locks() * sizeof(long));
	for (i = 0; i < CRYPTO_num_locks(); i++)
	{
        lock_count[i]=0;
		pthread_mutex_init(&(lock_cs[i]), NULL);
	}

	CRYPTO_set_id_callback((unsigned long (*)())pthread_thread_id);
	CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))pthread_locking_callback);
}

static void CRYPTO_thread_cleanup(void)
{
	int i;
	CRYPTO_set_locking_callback(NULL);
	for (i = 0; i < CRYPTO_num_locks(); i++)
	{
		pthread_mutex_destroy(&(lock_cs[i]));
	}
	OPENSSL_free(lock_cs);
}

int openSSL_check_certificate(SSL*  ssl)
{
    int result = 0;
    /* 基于 ctx 产生一个新的 SSL */
    printi("ssl(%p)\n", ssl);
    // SSL_get_verify_result()是重点，SSL_CTX_set_verify()只是配置启不启用并没有执行认证，调用该函数才会真证进行证书认证
    // 如果验证不通过，那么程序抛出异常中止连接
    if(SSL_get_verify_result(ssl) == X509_V_OK){
        printi("ssl(%p), 证书验证通过\n", ssl);
        result = 0;
    }else{
        printi("ssl(%p), 证书验证不通过\n", ssl);
        result = -1;
    }
    X509 *cert = SSL_get_peer_certificate(ssl);
    if (cert != NULL) {
        printi("ssl(%p), 数字证书信息:\n", ssl);
        char *line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printi("ssl(%p), 证书: %s\n", ssl, line);
        free(line);
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printi("ssl(%p), 颁发者: %s\n", ssl, line);
        free(line);
        X509_free(cert);
    } else {
        printi("ssl(%p), 无证书信息！\n", ssl);
    }
    return result;
}


static void* ccu_SYS_cmd(void* argv)
{
    uint32_t cmd;
    ccu_port_t* dev = (ccu_port_t*)argv;
    secc_port_t *seccA = (secc_port_t*)dev->seccA;
    secc_port_t *seccB = (secc_port_t*)dev->seccB;
    modbusTCP_QT_t *qttcu = (modbusTCP_QT_t*)dev->qttcu;
    uint8_t buff[64];
    printd("ENTER.\n");

    while(dev->quit == 0)
    {
        {
            printf("\n*************************************************\n");
            printf("\n\n      ccu cmd list\n\n");
            printf("\t0, quit.\n");
            printf("\t1, show EMG, GAT, WAT state\n");
            printf("\t2, show LGPT_FBA LGPT_FBB state\n");
            printf("\t3, show FAN state\n");
            printf("\t4, setOFF SSR\n");
            printf("\t5, setON  SSR\n");
            printf("\t6, setOFF LED\n");
            printf("\t7, setON  LED\n");
            printf("\t8, show  GUN temp\n");
            printf("\t9, show  BOX temp\n\n");

            printf("\t10, show GUNA->ConnectorID\n");
            printf("\t11, show GUNA->HomePlugin Fuse \n");
            printf("\t12, show GUNA->K1, K2, ELock state\n");
            printf("\t13, setOFF GUNA->Aux12V, K1K2, ELock\n");
            printf("\t14, setON  GUNA->Aux12V, K1K2, ELock\n");
            printf("\t15, show  GUNA->CC, CP, PP\n");
            printf("\t16, show  GUNA->GUNLineTemp\n");

            printf("\t18, GUNA->setPWM(100)\n");
            printf("\t19, GUNA->setPWM(50)\n\n");

            printf("\t20, show GUNB->ConnectorID\n");
            printf("\t21, show GUNB->HomePlugin Fuse \n");
            printf("\t22, show GUNB->K1, K2, ELock state\n");
            printf("\t23, setOFF GUNB->Aux12V, K1K2, ELock\n");
            printf("\t24, setON  GUNB->Aux12V, K1K2, ELock\n");
            printf("\t25, show  GUNB->CC, CP, PP\n");
            printf("\t26, show  GUNB->GUNLineTemp\n");

            printf("\t28, GUNB->setPWM(100)\n");
            printf("\t29, GUNB->setPWM(50)\n\n");

            printf("\t30, NFC.read.\n");
            printf("\t31, log.debug(1).\n");
            printf("\t32, log.debug(0).\n");
            printf("\t33, SECCA start Transmation.\n");
            printf("\t34, SECCA stop Transmation.\n");
            printf("\t35, SECCB start Transmation.\n");
            printf("\t36, SECCB stop Transmation.\n");

            printf("\t37, pcutc.request_Power.\n");
            printf("\t38, pcutc.getVoltageCurrent.\n");
            printf("\t39, pcutc.set_GUNLine_PermittedVoltageCurrent.\n");

            printf("\n\n*************************************************\n");
        }

        printf("please input cmd:");
        int ret;
        do {
            ret = scanf("%d", &cmd);
            if (ret == -1) sleep(1);
        }while(ret == -1);
        printf("input cmd:%d, ret=%d", cmd, ret);
        switch(cmd)
        {
            case 0: printf("\tquit...\n"); return 0;
            case 1: printf("\tccu.EMG(%d), GAT(%d), WAT(%d)\n", ccu_Get_EMGFB(dev), ccu_Get_GATFB(dev), ccu_Get_WATFB(dev)); break;
            case 2: printf("\tccu.LGPT_FBA(%d), LGPT_FBB(%d)\n", ccu_Get_LGPTFA(dev), ccu_Get_LGPTFA(dev)); break;
            case 3: printf("\tccu.FAN(%d, %d, %d)\n", ccu_Get_FanFBA(dev), ccu_Get_FanFBB(dev), ccu_Get_FanFBC(dev)); break;
            case 4: printf("\tccu.setSSR(0)\n"); ccu_Set_FanSSR(dev, 0); break;
            case 5: printf("\tccu.setSSR(1)\n"); ccu_Set_FanSSR(dev, 1); break;
            case 6: printf("\tccu.set_LED(1)\n"); gunA_set_LED_state(dev, LED_RED, 1); gunA_set_LED_state(dev, LED_BLUE, 1);  gunA_set_LED_state(dev, LED_GREEN, 1);  gunB_set_LED_state(dev, LED_RED, 1); gunB_set_LED_state(dev, LED_BLUE, 1);   gunB_set_LED_state(dev, LED_GREEN, 1); break;
            case 7: printf("\tccu.set_LED(0)\n"); gunA_set_LED_state(dev, LED_RED, 0); gunA_set_LED_state(dev, LED_BLUE, 0);  gunA_set_LED_state(dev, LED_GREEN, 0);  gunB_set_LED_state(dev, LED_RED, 0); gunB_set_LED_state(dev, LED_BLUE, 0);   gunB_set_LED_state(dev, LED_GREEN, 0); break;
            case 8: printf("\tccu.GUN_temp(%f, %f, %f, %f)\n", ccu_Get_GUN_Temp(dev, 0), ccu_Get_GUN_Temp(dev, 1), ccu_Get_GUN_Temp(dev, 2), ccu_Get_GUN_Temp(dev, 3)); break;
            case 9: printf("\tccu.BOX_temp(%f, %f, %f, %f)\n", ccu_Get_BOX_Temp(dev, 0), ccu_Get_BOX_Temp(dev, 1), ccu_Get_BOX_Temp(dev, 2), ccu_Get_BOX_Temp(dev, 3)); break;

            case 10: printf("\tGUNA->connectorId=%d\n ", seccA->config->connectorId); break;
            case 11: printf("\tGUNA->HomePlugin(%d), Fuse(%d) \n", gunA_get_PluginHomeState(dev), gunA_get_FuseState(dev)); break;
            case 12: printf("\tGUNA->K1:%d, K2:%d, ELock:%d\n", get_gpio_value(TCA6416_0, 2), get_gpio_value(TCA6416_0, 3), !get_gpio_value(TCA6416_0, 10)); break;
            case 13: printf("\tGUNA->set_Aux12V(0), set_K1K2(0)=%d, set_ELock(0)=%d\n", gunA_set_K1K2_state(dev, 0, 1000), gunA_set_ELock_state(NULL, 0, 2000)); gunA_set_Aux12V_state(dev, 0); break;
            case 14: printf("\tGUNA->set_Aux12V(1), set_K1K2(1)=%d, set_ELock(1)=%d\n", gunA_set_K1K2_state(dev, 1, 1000), gunA_set_ELock_state(NULL, 1, 2000)); gunA_set_Aux12V_state(dev, 1); break;
            case 15: printf("\tGUNA->CC(%f), CP(%f), PP(%f)\n", gunA_get_CC1_voltage(dev), gunA_get_CP_voltage(dev, NULL), gunA_get_PP_voltage(dev)); break;
            case 16: printf("\tGUNA->GUNTemp(%f, %f)\n", gunA_get_GunLine_temp(dev, 0), gunA_get_GunLine_temp(dev, 2)); break;

            case 18: printf("\tGUNA->setPWM(100)\n"); gunA_set_PWM_ratio(dev, 100);break;
            case 19: printf("\tGUNA->setPWM( 50)\n"); gunA_set_PWM_ratio(dev,  50);break;

            case 20: printf("\tGUNB->connectorId=%d\n ", seccB->config->connectorId); break;
            case 21: printf("\tGUNB->HomePlugin(%d), Fuse(%d) \n", gunB_get_PluginHomeState(dev), gunB_get_FuseState(dev)); break;
            case 22: printf("\tGUNB->K1:%d, K2:%d, ELock:%d\n", get_gpio_value(TCA6416_3, 2), get_gpio_value(TCA6416_3, 3), !get_gpio_value(TCA6416_3, 10)); break;
            case 23: printf("\tGUNB->set_Aux12V(0), set_K1K2(0)=%d, set_ELock(0)=%d\n", gunB_set_K1K2_state(dev, 0, 1000), gunB_set_ELock_state(NULL, 0, 2000)); gunB_set_Aux12V_state(dev, 0); break;
            case 24: printf("\tGUNb->set_Aux12V(1), set_K1K2(0)=%d, set_ELock(1)=%d\n", gunB_set_K1K2_state(dev, 1, 1000), gunB_set_ELock_state(NULL, 1, 2000)); gunB_set_Aux12V_state(dev, 1); break;
            case 25: printf("\tGUNB->CC(%f), CP(%f), PP(%f)\n", gunB_get_CC1_voltage(dev), gunB_get_CP_voltage(dev, NULL), gunB_get_PP_voltage(dev)); break;
            case 26: printf("\tGUNB->GUNTemp(%f, %f)\n", gunB_get_GunLine_temp(dev, 0), gunB_get_GunLine_temp(dev, 1)); break;

            case 28: printf("\tGUNB->setPWM(100)\n"); gunB_set_PWM_ratio(dev, 100);break;
            case 29: printf("\tGUNB->setPWM( 50)\n"); gunB_set_PWM_ratio(dev,  50);break;

            case 30: printf("\tNFC.read...\n"); if (ccu_Get_NFC_CardID(dev, 10000, buff) == 0) printf("\tNFC CardID:%s\n", buff); break;
            case 31: printf("\tlog.debug(1)\n"); mlog_control(MLOG_LVL_DEBUG, 1); break;
            case 32: printf("\tlog.debug(0)\n"); mlog_control(MLOG_LVL_DEBUG, 0); break;

            case 33: printf("\tSECCA.start_transaton\n"); secc_eim_startup(seccA, "NULL", 10000, 0, 0); break;
            case 34: printf("\tSECCA.stop_transaton\n"); secc_eim_shutdown(seccA); break;
            case 35: printf("\tSECCB.start_transaton\n"); secc_eim_startup(seccB, "NULL", 10000, 0, 0); break;
            case 36: printf("\tSECCB.stop_transaton\n"); secc_eim_shutdown(seccB); break;

            case 37: printf("\tpcutc.request_Power\n"); {
                pcutcA.request_Power(&pcutcA, 0, 200, 100); 
                pcutcB.request_Power(&pcutcB, 0, 400, 300);
            }break;
            case 38: printf("\tpcutc.getVoltageCurrent\n"); {
                float maxV,minV,maxI,minI;
                pcutcA.get_PCU_maxVoltageCurrent(&pcutcA, &maxV, &maxI);
                pcutcA.get_PCU_minVoltageCurrent(&pcutcA, &minV, &minI);
                printf("GUNA.maxV:%f, minV:%f, maxI:%f, minI:%f\n", maxV, minV, maxI, minI);
                pcutcB.get_PCU_maxVoltageCurrent(&pcutcB, &maxV, &maxI);
                pcutcB.get_PCU_minVoltageCurrent(&pcutcB, &minV, &minI);
                printf("GUNB.maxV:%f, minV:%f, maxI:%f, minI:%f\n", maxV, minV, maxI, minI);
            }break;
            case 39: printf("\tpcutc.set_GUNLine_PermittedVoltageCurrent\n"); {
                pcutcA.set_GUNLine_PermittedVoltageCurrent(&pcutcA, 250);
                pcutcB.set_GUNLine_PermittedVoltageCurrent(&pcutcB, 200);
            }break;
        }
        // printf("SECC.A:%d,%f\n", dev->get_PluginHomeState(dev), dev->get_CC_voltage(dev));
    }
    printd("LEAVE.\n");
    return NULL;
}

int main(int argc, char* argv[])
{
    int MLOG_MAX_FILESIZE = MLOG_BUFF_SIZE*60*24;
    int MLOG_MAX_FILENUMB = 7;
    // signal(SIGPIPE, SIG_IGN);
    // signal(SIGTERM, singal_term_handle);
	// parse_options(argc, argv);
    {
        int c;
        while ((c = getopt(argc, argv, "sn:")) != -1) {
            switch (c) {
            case 's':
                MLOG_MAX_FILESIZE = atoi(optarg);
                break;
            case 'n':
                MLOG_MAX_FILENUMB = atoi(optarg);
                break;
            case '?':
                break;
            default:
                exit(2);
            }
        }
    }
    // 初始化 OpenSSL
    SSL_library_init();
    OpenSSL_add_all_algorithms();
    SSL_load_error_strings();
    CRYPTO_thread_setup();
    mlog_init("/home/root/app_run/log/ccu", MLOG_MAX_FILESIZE, MLOG_MAX_FILENUMB);
    printa("\t%s\n", formatAppVersion());
	printe("\t%s\n", formatAppVersion());
    printw("\t%s\n", formatAppVersion());
    printi("\t%s\n", formatAppVersion());
    printd("\t%s\n", formatAppVersion());
    printv("\t%s\n", formatAppVersion());
    printv("\tMLOG_MAX_FILESIZE:%d, MLOG_MAX_FILENUMB:%d\n", MLOG_MAX_FILESIZE, MLOG_MAX_FILENUMB);

    mgpio_init();
	ccu_config_init();
    ocpp16_configkey_init();
    ocpp16_authorization_init();
    ocpp16_ChargingProfiles_init();
    ocpp16_OfflineChargingRecords_upload();

    ccu_SYS_init();
    ccu_SYS_cmd(&ccu_port);
    ccu_SYS_exit();

    ocpp16_OfflineChargingRecords_exit();
    ocpp16_ChargingProfiles_exit();
    ocpp16_authorization_exit();
    ocpp16_configkey_exit();
    ccu_config_exit();

    mgpio_exit();
    mlog_exit();
    CRYPTO_thread_cleanup();

	return 0;
}