/*
 * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "bt_app_core.h"
#include "bt_app_hf.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_hf_client_api.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/ringbuf.h"
#include "time.h"
#include "sys/time.h"
#include "sdkconfig.h"

#include "app_BT_User.h"
// #include "app_hf_msg_set.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"

const char *c_hf_evt_str[] = {
    "CONNECTION_STATE_EVT",              /*!< connection state changed event */
    "AUDIO_STATE_EVT",                   /*!< audio connection state change event */
    "VR_STATE_CHANGE_EVT",                /*!< voice recognition state changed */
    "CALL_IND_EVT",                      /*!< call indication event */
    "CALL_SETUP_IND_EVT",                /*!< call setup indication event */
    "CALL_HELD_IND_EVT",                 /*!< call held indicator event */
    "NETWORK_STATE_EVT",                 /*!< network state change event */
    "SIGNAL_STRENGTH_IND_EVT",           /*!< signal strength indication event */
    "ROAMING_STATUS_IND_EVT",            /*!< roaming status indication event */
    "BATTERY_LEVEL_IND_EVT",             /*!< battery level indication event */
    "CURRENT_OPERATOR_EVT",              /*!< current operator name event */
    "RESP_AND_HOLD_EVT",                 /*!< response and hold event */
    "CLIP_EVT",                          /*!< Calling Line Identification notification event */
    "CALL_WAITING_EVT",                  /*!< call waiting notification */
    "CLCC_EVT",                          /*!< listing current calls event */
    "VOLUME_CONTROL_EVT",                /*!< audio volume control event */
    "AT_RESPONSE",                       /*!< audio volume control event */
    "SUBSCRIBER_INFO_EVT",               /*!< subscriber information event */
    "INBAND_RING_TONE_EVT",              /*!< in-band ring tone settings */
    "LAST_VOICE_TAG_NUMBER_EVT",         /*!< requested number from AG event */
    "RING_IND_EVT",                      /*!< ring indication event */
    "PKT_STAT_EVT",                      /*!< requested number of packet status event */
};

// esp_hf_client_connection_state_t
const char *c_connection_state_str[] = {
    "disconnected",
    "connecting",
    "connected",
    "slc_connected",
    "disconnecting",
};

// esp_hf_client_audio_state_t
const char *c_audio_state_str[] = {
    "disconnected",
    "connecting",
    "connected",
    "connected_msbc",
};

/// esp_hf_vr_state_t
const char *c_vr_state_str[] = {
    "disabled",
    "enabled",
};

// esp_hf_service_availability_status_t
const char *c_service_availability_status_str[] = {
    "unavailable",
    "available",
};

// esp_hf_roaming_status_t
const char *c_roaming_status_str[] = {
    "inactive",
    "active",
};

// esp_hf_client_call_state_t
const char *c_call_str[] = {
    "NO call in progress",
    "call in progress",
};

// esp_hf_client_callsetup_t
const char *c_call_setup_str[] = {
    "NONE",
    "INCOMING",
    "OUTGOING_DIALING",
    "OUTGOING_ALERTING"
};

// esp_hf_client_callheld_t
const char *c_call_held_str[] = {
    "NONE held",
    "Held and Active",
    "Held",
};

// esp_hf_response_and_hold_status_t
const char *c_resp_and_hold_str[] = {
    "HELD",
    "HELD ACCEPTED",
    "HELD REJECTED",
};

// esp_hf_client_call_direction_t
const char *c_call_dir_str[] = {
    "outgoing",
    "incoming",
};

// esp_hf_client_call_state_t
const char *c_call_state_str[] = {
    "active",
    "held",
    "dialing",
    "alerting",
    "incoming",
    "waiting",
    "held_by_resp_hold",
};

// esp_hf_current_call_mpty_type_t
const char *c_call_mpty_type_str[] = {
    "single",
    "multi",
};

// esp_hf_volume_control_target_t
const char *c_volume_control_target_str[] = {
    "SPEAKER",
    "MICROPHONE"
};

// esp_hf_at_response_code_t
const char *c_at_response_code_str[] = {
    "OK",
    "ERROR"
    "ERR_NO_CARRIER",
    "ERR_BUSY",
    "ERR_NO_ANSWER",
    "ERR_DELAYED",
    "ERR_BLACKLILSTED",
    "ERR_CME",
};

// esp_hf_subscriber_service_type_t
const char *c_subscriber_service_type_str[] = {
    "unknown",
    "voice",
    "fax",
};

// esp_hf_client_in_band_ring_state_t
const char *c_inband_ring_state_str[] = {
    "NOT provided",
    "Provided",
};

extern esp_bd_addr_t peer_addr;
//static uint8_t call_timer;
// If you want to connect a specific device, add it's address here
// esp_bd_addr_t peer_addr = {0xac, 0x67, 0xb2, 0x53, 0x77, 0xbe};

#if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI

#define ESP_HFP_RINGBUF_SIZE 3600
static RingbufHandle_t m_rb = NULL;

static void bt_app_hf_client_audio_open(void)
{
    m_rb = xRingbufferCreate(ESP_HFP_RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
}

static void bt_app_hf_client_audio_close(void)
{
    if (!m_rb) {
        return ;
    }

    vRingbufferDelete(m_rb);
}

static uint32_t bt_app_hf_client_outgoing_cb(uint8_t *p_buf, uint32_t sz)
{
    if (!m_rb) {
        return 0;
    }

    size_t item_size = 0;
    uint8_t *data = xRingbufferReceiveUpTo(m_rb, &item_size, 0, sz);
    if (item_size == sz) {
        memcpy(p_buf, data, item_size);
        vRingbufferReturnItem(m_rb, data);
        return sz;
    } else if (0 < item_size) {
        vRingbufferReturnItem(m_rb, data);
        return 0;
    } else {
        // data not enough, do not read
        return 0;
    }
}

static void bt_app_hf_client_incoming_cb(const uint8_t *buf, uint32_t sz)
{
    if (! m_rb) {
        return;
    }
    BaseType_t done = xRingbufferSend(m_rb, (uint8_t *)buf, sz, 0);
    if (! done) {
        ESP_LOGE(BT_HF_TAG, "rb send fail");
    }

    esp_hf_client_outgoing_data_ready();
}
#endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */

/* callback for HF_CLIENT */
void bt_app_hf_client_cb(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_t *param)
{
    if (event <= ESP_HF_CLIENT_PKT_STAT_NUMS_GET_EVT) {
        ESP_LOGI(BT_HF_TAG, "APP HFP event: %s", c_hf_evt_str[event]);
    } else {
        ESP_LOGE(BT_HF_TAG, "APP HFP invalid event %d", event);
    }

    switch (event) {
        case ESP_HF_CLIENT_CONNECTION_STATE_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--connection state %s, peer feats 0x%"PRIx32", chld_feats 0x%"PRIx32,
                    c_connection_state_str[param->conn_stat.state],
                    param->conn_stat.peer_feat,
                    param->conn_stat.chld_feat);
            memcpy(peer_addr,param->conn_stat.remote_bda,ESP_BD_ADDR_LEN);

            if(param->conn_stat.state == 0)
            {
            }
            else if(param->conn_stat.state == 2)
            {
            }
            break;
        }

        case ESP_HF_CLIENT_AUDIO_STATE_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--audio state %s",
                    c_audio_state_str[param->audio_stat.state]);
    #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI
            if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED ||
                param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC) {
                esp_hf_client_register_data_callback(bt_app_hf_client_incoming_cb,
                                                    bt_app_hf_client_outgoing_cb);
                bt_app_hf_client_audio_open();
            } else if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_DISCONNECTED) {
                bt_app_hf_client_audio_close();
            }
    #endif /* #if CONFIG_BT_HFP_AUDIO_DATA_PATH_HCI */

            if (param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED ||
                param->audio_stat.state == ESP_HF_CLIENT_AUDIO_STATE_CONNECTED_MSBC) {
                    hf_disc_audio_handler();
                }

            
            break;
        }

        case ESP_HF_CLIENT_BVRA_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--VR state %s",
                    c_vr_state_str[param->bvra.value]);
            break;
        }

        case ESP_HF_CLIENT_CIND_SERVICE_AVAILABILITY_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--NETWORK STATE %s",
                    c_service_availability_status_str[param->service_availability.status]);
            break;
        }

        case ESP_HF_CLIENT_CIND_ROAMING_STATUS_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--ROAMING: %s",
                    c_roaming_status_str[param->roaming.status]);
            break;
        }

        case ESP_HF_CLIENT_CIND_SIGNAL_STRENGTH_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "-- signal strength: %d",
                    param->signal_strength.value);
            break;
        }

        case ESP_HF_CLIENT_CIND_BATTERY_LEVEL_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--battery level %d",
                    param->battery_level.value);
            break;
        }

        case ESP_HF_CLIENT_COPS_CURRENT_OPERATOR_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--operator name: %s",
                    param->cops.name);
            break;
        }

        case ESP_HF_CLIENT_CIND_CALL_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--Call indicator %s",
                    c_call_str[param->call.status]);

            if(param->call.status == 0)
            {
                BT_User.Call_Sts = Call_Idle;
               // call_timer = 0;
                //printf("call Hangup\r\n");
            }
            else if(param->call.status == 1)
            {
                BT_User.Call_Sts = Call_InProcess;
                // printf("call Call_InProcess\r\n");
            }
            break;
        }

        case ESP_HF_CLIENT_CIND_CALL_SETUP_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--Call setup indicator %s",
                    c_call_setup_str[param->call_setup.status]);
            
            switch(param->call_setup.status)
            {
                case 0:
                {
                     if(BT_User.Call_Sts == Call_IncomeCalling || BT_User.Call_Sts == Call_OutCalling)
                     {
                         BT_User.Call_Sts = Call_Idle;
                     }
                        // BT_User.Call_Sts = Call_Idle;
                     //   call_timer = 0;
                       // printf("call Miss\r\n");
                    break;
                }
                case 1:
                {
                    BT_User.Call_Sts = Call_IncomeCalling;

                    //printf("call income\r\n");
                    break;
                }
                case 2:
                {
                    BT_User.Call_Sts = Call_OutCalling;
                   // printf("call outcall\r\n");
                    break;
                }
                case 3:
                {
                    BT_User.Call_Sts = Call_OutCalling;
                   // printf("call outcall\r\n");
                    break;
                }
            }
            
            if(param->call_setup.status == 0)
             break;
        }

        case ESP_HF_CLIENT_CIND_CALL_HELD_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--Call held indicator %s",
                    c_call_held_str[param->call_held.status]);
            break;
        }

        case ESP_HF_CLIENT_BTRH_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--response and hold %s",
                    c_resp_and_hold_str[param->btrh.status]);
            break;
        }

        case ESP_HF_CLIENT_CLIP_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--clip number %s",
                    (param->clip.number == NULL) ? "NULL" : (param->clip.number));
        //     if(param->clip.number != NULL)
        //     {
        //         for(int i = 0 ; i < 11 ; i++)
        //         {
        //             BT_User.Number[i] = param->clip.number[i];
        //         }
        //     }
        //     if(BT_User.Call_Sts != Call_InProcess)
        //     BT_User.Call_Sts = Call_IncomeCalling;
        //     printf("call income\r\n");
        //     printf("%s\n",param->clip.number);
        //   //  printf("1\n");
            break;
        }

        case ESP_HF_CLIENT_CCWA_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--call_waiting %s",
                    (param->ccwa.number == NULL) ? "NULL" : (param->ccwa.number));
            break;
        }

        case ESP_HF_CLIENT_CLCC_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--Current call: idx %d, dir %s, state %s, mpty %s, number %s",
                    param->clcc.idx,
                    c_call_dir_str[param->clcc.dir],
                    c_call_state_str[param->clcc.status],
                    c_call_mpty_type_str[param->clcc.mpty],
                    (param->clcc.number == NULL) ? "NULL" : (param->clcc.number));
            // if((strstr(param->clcc.number,"10000000") == NULL) && (BT_User.Call_Sts != Call_IncomeCalling) && (BT_User.Call_Sts != Call_InProcess))
            // {
            //     if(call_timer == 7)
            //     {
            //         BT_User.Call_Sts = Call_OutCalling;
            //         printf("call outcall\r\n");
            //         printf("%s\n",param->clcc.number);
            //     }
            //     else
            //     {
            //         call_timer ++;
            //     }
            //    // call_timer ++;
            // }
            // else
            // {
            //     call_timer = 0;
            // }
            // break;
            if((strstr(param->clcc.number,"10000000") == NULL) && (strstr(param->clcc.number,"00000000") == NULL))
            {
                // printf("%s\n",param->clcc.number);
                BT_User.Get_Call_Num = 1;
            }
        }

        case ESP_HF_CLIENT_VOLUME_CONTROL_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--volume_target: %s, volume %d",
                    c_volume_control_target_str[param->volume_control.type],
                    param->volume_control.volume);
            break;
        }

        case ESP_HF_CLIENT_AT_RESPONSE_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--AT response event, code %d, cme %d",
                    param->at_response.code, param->at_response.cme);
            break;
        }

        case ESP_HF_CLIENT_CNUM_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--subscriber type %s, number %s",
                    c_subscriber_service_type_str[param->cnum.type],
                    (param->cnum.number == NULL) ? "NULL" : param->cnum.number);
            break;
        }

        case ESP_HF_CLIENT_BSIR_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--inband ring state %s",
                    c_inband_ring_state_str[param->bsir.state]);
            break;
        }

        case ESP_HF_CLIENT_BINP_EVT:
        {
            ESP_LOGI(BT_HF_TAG, "--last voice tag number: %s",
                    (param->binp.number == NULL) ? "NULL" : param->binp.number);
            break;
        }
        case ESP_HF_CLIENT_PKT_STAT_NUMS_GET_EVT:
        {
            ESP_LOGE(BT_HF_TAG, "ESP_HF_CLIENT_PKT_STAT_NUMS_GET_EVT: %d", event);
            break;
        }
        default:
            ESP_LOGE(BT_HF_TAG, "HF_CLIENT EVT: %d", event);
            break;
    }
}
// uint8_t lasttest;
void Get_Iphone_Fitter_callsts(void )
{
    if(BT_User.Call_Sts == Call_Idle)
    {
        BT_User.Get_Call_Num = 0;
        BT_User.Call_Fitter_sts = BT_User.Call_Sts;
    }
    else
    {
        if(BT_User.Get_Call_Num == 1)
        {
            BT_User.Call_Fitter_sts = BT_User.Call_Sts;
        }
        else
        {
            BT_User.Call_Fitter_sts = Call_Idle;
        }
    }   

//     if(BT_User.Call_Fitter_sts != lasttest)
//    printf("%d\n",BT_User.Call_Fitter_sts);

//    lasttest = BT_User.Call_Fitter_sts;
}



#define HF_CMD_HANDLER(cmd)    int hf_##cmd##_handler(void )

HF_CMD_HANDLER(conn)
{
    // printf("connect\n");
    esp_hf_client_connect(peer_addr);
    return 0;
}

HF_CMD_HANDLER(disc)
{
    // printf("disconnect\n");
    esp_hf_client_disconnect(peer_addr);
    return 0;
}

HF_CMD_HANDLER(conn_audio)
{
    // printf("connect audio\n");
    esp_hf_client_connect_audio(peer_addr);
    return 0;
}

HF_CMD_HANDLER(disc_audio)
{
    // printf("disconnect audio\n");
    esp_hf_client_disconnect_audio(peer_addr);
    return 0;
}

HF_CMD_HANDLER(query_op)
{
    // printf("Query operator\n");
    esp_hf_client_query_current_operator_name();
    return 0;
}

HF_CMD_HANDLER(answer)
{
    // printf("Answer call\n");
    esp_hf_client_answer_call();
    return 0;
}
HF_CMD_HANDLER(reject)
{
    esp_err_t err;
    err = esp_hf_client_reject_call();
    // printf("Reject call err %d\n",err);
    return 0;
}

const char test_dial_num[] = "17800536969";
HF_CMD_HANDLER(dial)
{
    // printf("Dial number %s\n", test_dial_num);
    esp_hf_client_dial(test_dial_num);
    return 0;
}

HF_CMD_HANDLER(redial)
{
    // printf("Dial number\n");
    esp_hf_client_dial(NULL);
    return 0;
}

HF_CMD_HANDLER(dial_mem)
{
    int index = 1;
    esp_hf_client_dial_memory(index);
    return 0;
}


HF_CMD_HANDLER(start_vr)
{
    // printf("Start voice recognition\n");
    esp_hf_client_start_voice_recognition();
    return 0;
}

HF_CMD_HANDLER(stop_vr)
{
    // printf("Stop voice recognition\n");
    esp_hf_client_stop_voice_recognition();
    return 0;
}


// HF_CMD_HANDLER(query_call)
// {
//     // printf("Query current call status\n");
//     esp_hf_client_query_current_calls();
//     return 0;
// }

// HF_CMD_HANDLER(retrieve_subscriber)
// {
//     // printf("Retrieve subscriber information\n");
//     esp_hf_client_retrieve_subscriber_info();
//     return 0;
// }

// HF_CMD_HANDLER(request_last_voice_tag)
// {
//     // printf("Request last voice tag\n");
//     esp_hf_client_request_last_voice_tag_number();
//     return 0;
// }



// typedef enum {
//     ESP_HF_BTRH_CMD_HOLD = 0,          /*!< put the incoming call on hold */
//     ESP_HF_BTRH_CMD_ACCEPT = 1,        /*!< accept a held incoming call */
//     ESP_HF_BTRH_CMD_REJECT = 2,        /*!< reject a held incoming call */
// } esp_hf_btrh_cmd_t;

HF_CMD_HANDLER(btrh)
{
    int btrh = ESP_HF_BTRH_CMD_HOLD;

    // printf("respond and hold command: %d\n", btrh);
    esp_hf_client_send_btrh_cmd(btrh);
    return 0;
}






