#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <linux/fiemap.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/timerfd.h>
#include <unistd.h>

#include "sd-messages.h"
#include "btrfs-util.h"
#include "bus-error.h"
#include "exec-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "log.h"
#include "main-func.h"
#include "parse-util.h"
#include "pretty-print.h"
#include "sleep-config.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
#include "missing_input.h"
#include "hashmap.h"
#include "log.h"

#define KEY_PRIVACY 752
#define MAX_INPUT_FD 8

#define ULONG_BITS (sizeof(unsigned long)*8)
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x)  ((x)%BITS_PER_LONG)
#define BIT(x)  (1UL<<OFF(x))
#define LONG(x) ((x)/BITS_PER_LONG)
#define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)

static bool bitset_get(const unsigned long *bits, unsigned i) {
    return (bits[i / ULONG_BITS] >> (i % ULONG_BITS)) & 1UL;
}

// static void bitset_put(unsigned long *bits, unsigned i) {
//     bits[i / ULONG_BITS] |= (unsigned long) 1 << (i % ULONG_BITS);
// }

typedef enum {
    STATE_UNKOWN=-1,
    STATE_KEEP=0,
    STATE_CHANGE,
} MKEY_STATE;

typedef enum {
    INPUT_KEY_PRIVACY = 1 << 0,
    INPUT_SW_LID = 1 << 1, //内核换input设备上报， 此节点废弃
} INPUT_KEY;

#define GPATH "/sys/devices/"
#define STATE_NAME "privacy_switch_state"
#define CONTROL_NAME "privacy_switch_control"

#define ALL_DEV_NUM 4
static char contrl_dev[4][8] = {"WiFi", "Camera", "Mic", "BT"};

typedef enum {
    CONTRL_WIFI = 0,
    CONTRL_CAMERA,
    CONTRL_MIC,
    CONTRL_BT,
    CONTRL_UNKOWN,
} CONTRL_DEV;

//解析enable， 查看具体BIOS中配置的设备， 控制1 不控制0
#define SUPPORT_DEVICE(x, i) ((x) & (1<<i))

//返回开关状态 开是1 关是0
#define SWITCH_ALL_STATE(y) ((y) & 0x80)
#define DEV_STATE(y,i) ((y) & (1<<i))

//记录那个Input设备有隐私开关关心的值
struct button {
    int fd;
    int flag;
    char devpath[512];
};

struct Manager {
    sd_event *event;
    sd_bus *bus;
    sd_event_source *io_event_source;
    struct button button[MAX_INPUT_FD];
    int32_t button_num;
    int32_t key_switch; //拨键开关位置 拨上1开启 拨下0关闭
    int32_t devlist[7];//bit1:wifi  bit2:camera bit3:mic bit4:bt bit5:unkown bit6:unkown bit7 unkown
    int32_t devstat[7];
    char devstate_property[256];
    int32_t current_physical_switch_control;
    int32_t current_physical_switch_state;
    char state_path[512];
    char control_path[512];
    usec_t timestamp;
};

typedef struct Manager Manager;

static Manager* manager_unref(Manager *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);

static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata);

static int method_get_privacykey(sd_bus_message *message,
        void *userdata, sd_bus_error *error)
{
    _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    Manager *m = userdata;
    int r;
    char buffer[256] = {0};
    int i;

    for (i=0; i<ALL_DEV_NUM; i++) {
        if (m->devlist[i]) {
            sprintf(&buffer[strlen(buffer)], "%s:%d,", contrl_dev[i], m->devlist[i]);
        }
    }
    if (strlen(buffer) > 1) {
        if (buffer[strlen(buffer) - 1] == ',')
            buffer[strlen(buffer) - 1] = '\0';
    }

    log_debug("privacy support %s, devstat:%s", buffer, m->devstate_property);

    r = sd_bus_message_new_method_return(message, &reply);

    r = sd_bus_message_append(reply, "s", m->devstate_property);
    if (r < 0)
        return r;
    r = sd_bus_send(NULL, reply, NULL);

    return r;
}

static int method_get_switchkey(sd_bus_message *message,
        void *userdata, sd_bus_error *error)
{
    _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    Manager *m = userdata;
    int r;

    log_debug("key_switch:%d", m->key_switch);

    r = sd_bus_message_new_method_return(message, &reply);

    r = sd_bus_message_append(reply, "b", m->key_switch?false:true);
    if (r < 0)
        return r;
    r = sd_bus_send(NULL, reply, NULL);

    return r;
}

static int property_get_name(
        sd_bus *bus,
        const char *path,
        const char *interface,
        const char *property,
        sd_bus_message *reply,
        void *userdata,
        sd_bus_error *error) {

    Manager *m = userdata;

    assert(bus);
    assert(reply);
    assert(m);

    return sd_bus_message_append(reply, "s", m->devstate_property);
}

const sd_bus_vtable manager_vtable[] = {
    SD_BUS_VTABLE_START(0),
    SD_BUS_PROPERTY("PrivacyKeyState", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
    SD_BUS_METHOD("GetPrivacyKeyState", NULL, "s", method_get_privacykey, SD_BUS_VTABLE_UNPRIVILEGED),
    SD_BUS_METHOD("GetSwitchState", NULL, "b", method_get_switchkey, SD_BUS_VTABLE_UNPRIVILEGED),
    SD_BUS_SIGNAL("privacykey", "b", 0),
    SD_BUS_VTABLE_END
};

static int button_suitable(int fd) {
    unsigned long types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1]; //EV_MSC=0x04 EV_SW=0x05
    int flag = 0;

    assert(fd >= 0);

    if (ioctl(fd, EVIOCGBIT(EV_SYN, sizeof types), types) < 0)
        return -errno;

    if (bitset_get(types, EV_KEY)) {
        unsigned long keys[KEY_PRIVACY/ULONG_BITS+1];
        if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof keys), keys) < 0)
            return -errno;
        if (bitset_get(keys, KEY_PRIVACY)) {
            flag |= INPUT_KEY_PRIVACY;
        }
    }

    return flag;
}

/*
 * GreatWall资料
 * --------------------------------------
 *
 * 一、长城笔记本侧边魔术键需求 (T321测试)
 * ---------
 * 1.1 OS 读取设备状态：
 * wifi   状态 BIT0: 0-enable  1-disable
 * camera 状态 BIT1: 0-enable  1-disable
 * mic    状态 BIT2: 0-enable  1-disable
 * 开关状态    BIT7：0-物理开  1-物理关
 * 物理关(全部不可用) 功能全部失效
 * 物理开(全部可用) 拨动开关可以配置功能是否物理失效或生效
 *   拨上：wifi打开 BT打开 camera打开 mic打开  可以连接蓝牙和网络， 可以使用摄像头
 *   拨下：wifi关闭 BT关闭 camera关闭 mic关闭  系统上看不到wifi BT设备，看不到摄像头设备/dev/video
 *
 * 当前EC版本：T321FEE18T07_Privacy_switch版本的EC， 反馈Mic开关功能异常
 *
 * OS规则
 *   因拨扭开关在S3/S5 下会被波动，所以OS在S3/S5 启动阶段需要主动从ECRAM
 * 0x4A中查询状态，依据状态做相应的设置：
 * S0状态下用户波动隐私开关时，EC会发送0xE0 Q-event 通知系统，系统再从ECRAM
 * 0x4A中查询状态，并根据状态做相应的设置
 *
 * PS：0x4A的bit7为拨扭开关的物理状态，假如BIT0-BIT2与BIT7状态不一致， 那说明
 *     波动开关不对该设备做控制，该设备EC会持续汇报enable状态
 *
 * 1.2 隐私开关测试， 查看sys接口值：
 *  "/sys/devices/platform/PHYT0007:00/PHYT000B:00/privacy_switch_control"
 *  "/sys/devices/platform/PHYT0007:00/PHYT000B:00/privacy_switch_state"
 *
 * 全部打开
 *   拨上：
 *      privacy_switch_control=7
 *      physical_switch_state=0 bit7:0 bit0(WiFi):0 bit1(camera):0 bit2(mic):0
 *   拨下：
 *      privacy_switch_control=7
 *      physical_switch_state=131 （0x83）bit7:1  bit0(WiFi):1 bit1(camera):1 bit2(mic):0 应该是1 可能是因为mic功能没实现
 *
 * 全部关闭 没有按键事件上报
 *   拨上：
 *      privacy_switch_control=0
 *      physical_switch_state=3 bit7:0 bit0(WiFi):1 bit1(camera):1 bit2(mic):0
 *   拨下：
 *      privacy_switch_control=0
 *      physical_switch_state=131 (0x83) bit7:1  bit0(WiFi):1 bit1(camera):1 bit2(mic):0 应该是1 可能是因为mic功能没实现
 *
 * 只配置wifi开，camera关 mic关
 *  拨上:
 *     privacy_switch_control=1
 *     physical_switch_state=2 bit7:0 bit0(WiFi):0 bit1(camera):1 bit2(mic):0 应该是1 可能是因为mic功能没实现
 *  拨下:
 *     privacy_switch_control=1
 *     physical_switch_state=131
 *
 *  总结physical_switch_enable节点展示那些开关功能生效，physical_switch_state节点展示当前的开关状态
 */

static int get_privacykey_status(Manager* m)
{
    int r;
    int i;
    int key_status = STATE_UNKOWN;
    _cleanup_free_ char *value = NULL;
    int privacykey_enable = -1, privacykey_state = -1;

    r = read_one_line_file(m->control_path, &value);
    if (r>=0) {
        r = safe_atoi(value, &privacykey_enable);
        if (r < 0)
            return log_error_errno(r, "Failed to read %s, value:%s: %m", m->control_path, value);
    } else if (r < 0) {
        return log_error_errno(r, "Failed to read %s: %m", m->control_path);
    }

    r = read_one_line_file(m->state_path, &value);
    if (r>=0) {
        r = safe_atoi(value, &privacykey_state);
        if (r < 0)
            return log_error_errno(r, "Failed to read %s, value:%s : %m", m->state_path, value);
    } else if (r < 0) {
        return log_error_errno(r, "Failed to read %s: %m", m->state_path);
    }

    log_info("privacykey_enable:%d, privacykey_state:%d", privacykey_enable, privacykey_state);

    if (m->current_physical_switch_control == -1) {
        //1:设备可配置 0:设备关闭
        m->current_physical_switch_control = privacykey_enable;
        for (i=0; i<ALL_DEV_NUM; i++) {
            m->devlist[i] = SUPPORT_DEVICE(privacykey_enable, i)?1:0;
            log_debug("%s:%d", contrl_dev[i], m->devlist[i]);
        }
    } else {
        if (m->current_physical_switch_control != privacykey_enable) {
            log_info("!!! privacy key enable status abnormal!!!");
        }
        m->current_physical_switch_control = privacykey_enable;
    }

    log_info("m->current_physical_switch_state:%d, privacykey_state:%d", m->current_physical_switch_state, privacykey_state);
    if (m->current_physical_switch_state != privacykey_state) {
        key_status = STATE_CHANGE;
        m->current_physical_switch_state = privacykey_state;
        //返回 1:设备可配置 0:设备关闭
        if (SWITCH_ALL_STATE(privacykey_state) == 0) { //设备开
            log_info("privacy device open, device can config");
            m->key_switch = 1;
        } else {
            log_info("privacy device close, all device can not config");
            m->key_switch = 0;
        }
        char buffer[256] = {0};
        char tmp[64] = {0};
        int start = 0;
        for (i=0; i<ALL_DEV_NUM; i++) {
            memset(tmp, 0, sizeof(tmp));
            if (m->devlist[i]) {
                m->devstat[i] = DEV_STATE(privacykey_state, i)?1:0;
                log_debug("dev[%d]:%s, stat:%d", i, contrl_dev[i], m->devstat[i]);
                if (start == 0) {
                    sprintf(tmp, "%s:%d", contrl_dev[i], m->devstat[i]);
                    start = 1;
                } else {
                    sprintf(tmp, ",%s:%d", contrl_dev[i], m->devstat[i]);
                }
                strcat(buffer, tmp);
            }
        }
        sprintf(m->devstate_property, "%s", buffer);
        log_info("devstate_property: %s", buffer);
    }

    log_debug("key_status:%d", key_status);
    return key_status;
}

static int match_prepare_for_sleep(sd_bus_message* message,
        void* userdata, sd_bus_error* error)
{
    int sleep, r;
    int key_status = STATE_UNKOWN;
    Manager* m = (Manager*)userdata;

    r = sd_bus_message_read(message, "b", &sleep);
    if (r < 0) {
        log_info("Failed to parse signal: \n");
        return 0;
    }

    if (sleep == false) {
        log_debug("post sleep"); //长城固件在唤醒后
        key_status = get_privacykey_status(m);
        log_debug("key_status:%d", key_status);
        if (key_status == STATE_CHANGE) {
            sd_bus_emit_signal(m->bus,
                    "/org/freedesktop/OEMPrivacyKey",
                    "org.freedesktop.OEMPrivacyKey.Manager",
                    "privacykey",
                    "b",
                    m->key_switch?true:false);
        }
    }

    return 0;
}

static Manager* manager_unref(Manager *m)
{
    assert(m);

    for (int i=0; i<MAX_INPUT_FD; i++) {
        if (m->button[i].fd != -1 || m->button[i].fd >= 0)
            close(m->button[i].fd);
    }

    sd_event_source_unref(m->io_event_source);

    return mfree(m);
}

static int manager_connect_bus(Manager *m) {
    int r;

    assert(m);
    assert(!m->bus);

    r = bus_open_system_watch_bind_with_description(&m->bus, "oem privacy key");
    if (r < 0)
        return log_error_errno(r, "Failed to connect to system bus: %m");

    r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/OEMPrivacyKey", "org.freedesktop.OEMPrivacyKey.Manager", manager_vtable, m);
    if (r < 0)
        return log_error_errno(r, "Failed to add manager object vtable: %m");

    r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.OEMPrivacyKey", 0, NULL, NULL);
    if (r < 0)
        return log_error_errno(r, "Failed to request name: %m");

    r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL);
    if (r < 0)
        return log_error_errno(r, "Failed to attach bus to event loop: %m");

    r = sd_bus_match_signal_async(
            m->bus,
            NULL,
            "org.freedesktop.login1",
            "/org/freedesktop/login1",
            "org.freedesktop.login1.Manager",
            "PrepareForSleep",
            match_prepare_for_sleep, NULL, m);

    if (r < 0) {
        log_info("Failed to add match\n");
        return r;
    }

    return 0;
}

static int manager_run(Manager *m) {
    int r;

    assert(m);

    for (;;) {
        r = sd_event_get_state(m->event);
        if (r < 0)
            return r;
        if (r == SD_EVENT_FINISHED)
            return 0;

        r = sd_event_run(m->event, (uint64_t) -1);
        if (r < 0)
            return r;
    }
}

static int manager_new(Manager **ret) {
    _cleanup_(manager_unrefp) Manager *m = NULL;
    int r;

    assert(ret);

    m = new(Manager, 1);
    if (!m)
        return -ENOMEM;
    *m = (Manager) {
        .current_physical_switch_control = -1,
            .current_physical_switch_state = -1,
            .button_num = 0,
            .timestamp = 0,
    };
    memset(m->devlist, 0, sizeof(m->devlist));
    memset(m->devstat, 0, sizeof(m->devstat));
    memset(m->devstate_property, 0, sizeof(m->devstate_property));

    memset(m->button, -1, sizeof(struct button));
    r = sd_event_default(&m->event);
    if (r < 0)
        return r;

    *ret = TAKE_PTR(m);
    return 0;
}

static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata)
{
    Manager *m = userdata;
    struct input_event ev;
    ssize_t l;
    int need_send_signal = 0;
    int key_status = -1;
    struct timeval tv;

    assert(s);
    assert(m);

    l = read(fd, &ev, sizeof(ev));
    if (l < 0)
        return errno != EAGAIN ? -errno : 0;
    if ((size_t) l < sizeof(ev))
        return -EIO;

    if (ev.type == EV_KEY && ev.value > 0) {
        if (ev.code == KEY_PRIVACY) {
            log_debug("KEY_PRIVACY key, send %s %d\n", "privacykey", ev.value);
            need_send_signal = 1;
        }
    } else if (ev.type == EV_KEY && ev.value == 0) {
        if (ev.code == KEY_PRIVACY) {
            log_debug("KEY_PRIVACY key, send %s %d\n", "privacykey", ev.value?0:1);
            need_send_signal = 1;
        }
    }

    if (need_send_signal) {
        gettimeofday(&tv, NULL);
        log_debug("now usecond: %ld, old: m->timestamp:%lu\n", tv.tv_sec*1000*1000 + tv.tv_usec, m->timestamp);
        key_status = get_privacykey_status(m);
        if (key_status == STATE_CHANGE || (tv.tv_sec*1000*1000 + tv.tv_usec - m->timestamp) > 10*1000) {
            sd_bus_emit_signal(m->bus,
                    "/org/freedesktop/OEMPrivacyKey",
                    "org.freedesktop.OEMPrivacyKey.Manager",
                    "privacykey",
                    "b",
                    m->key_switch?true:false);
        }
        m->timestamp = tv.tv_sec*1000*1000 + tv.tv_usec;
    }

    return 0;
}

static int get_input_device_path(Manager *m)
{
    const char *p;
    DIR *dir;
    struct dirent *de;
    const char *dirname = "/dev/input";
    int r = 0;
    struct stat st;
    char name[256] = {0};
    int ret = -1;
    //unsigned long types[EV_KEY/ULONG_BITS+1] = {}, keys[KEY_PRIVACY/ULONG_BITS+1] = {};
    //struct input_mask mask;

    dir = opendir(dirname);
    if(dir == NULL)
        return -1;

    while((de = readdir(dir))) {
        _cleanup_close_ int fd = -1;
        if (de->d_name[0] == '.' && (de->d_name[1] == '\0' ||
                    (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;

        p = strjoina("/dev/input/", de->d_name);
        if ((stat(p, &st) == 0) && (S_ISDIR(st.st_mode))) {
            continue;
        }

        fd = open(p, O_RDONLY);
        if (fd < 0) {
            log_debug("Failed to open %s: %m", p);
            continue;
        }
        log_debug("open device:%s", p);

        r = button_suitable(fd);
        if (r<=0) {
            fd = -1;
            continue;
        } else {
            if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 0)
                log_error("can not get input name");
            if (r == INPUT_KEY_PRIVACY) {
                sprintf(m->button[m->button_num].devpath, "%s", p);
                m->button[m->button_num].flag = r;
                log_info("find device:%s support  INPUT_KEY_PRIVACY, name:%s", p, name);
                ret = 1;
                m->button_num += 1;
            }

            //bitset_put(keys, KEY_PRIVACY);
            //mask = (struct input_mask) {
            //        .type = EV_KEY,
            //        .codes_size = sizeof(keys),
            //        .codes_ptr = PTR_TO_UINT64(keys),
            //};
            //if (ioctl(fd, EVIOCSMASK, &mask) < 0)
            //        return log_warning_errno(errno, "Failed to set EV_KEY event mask on /dev/input/%s: %m", name);
        }
    }

    closedir(dir);

    return ret;
}

static void find_file(const char *path, Manager *m)
{
    DIR *dir = opendir(path);
    if (!dir) {
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char full_path[512] = {0};
        if (sprintf(full_path, "%s/%s", path, entry->d_name) == -1) {
            continue;
        }

        struct stat statbuf;
        if (lstat(full_path, &statbuf) == -1) {
            continue;
        }

        if (S_ISDIR(statbuf.st_mode)) {
            find_file(full_path, m);
        } else {
            if (!strcmp(entry->d_name, CONTROL_NAME)) {
                log_debug("%s\n", full_path);
                strcpy(m->control_path, full_path);
            }
            if (!strcmp(entry->d_name, STATE_NAME)) {
                log_debug("%s\n", full_path);
                strcpy(m->state_path, full_path);
            }
        }

    }

    closedir(dir);
}

static int run(int argc, char *argv[]) {
    _cleanup_(manager_unrefp) Manager *m = NULL;
    _cleanup_close_ int fd = -1;
    int r;
    int i = 0;

    r = manager_new(&m);
    if (r < 0)
        return log_error_errno(r, "Failed to allocate manager object: %m");

    find_file(GPATH, m);

    if (access(m->state_path,F_OK) == 0 && access(m->control_path, F_OK) == 0) {
        log_info("state:%s\ncontrol:%s", m->state_path, m->control_path);
    } else {
        log_info("not PrivacyKey device");
        return 0;
    }

    /* Connect to the bus */
    r = manager_connect_bus(m);
    if (r < 0)
        return r;

    get_privacykey_status(m);

    r = get_input_device_path(m);
    if (r < 0)
        return r;

    m->io_event_source = sd_event_source_unref(m->io_event_source);
    for (i=0; i<=m->button_num; i++) {
        if (m->button[i].flag > 0) {
            m->button[i].fd = open(m->button[i].devpath, O_RDONLY);
            if (m->button[i].fd < 0)
                return -1;
            r = sd_event_add_io(m->event, &m->io_event_source, m->button[i].fd, EPOLLIN, button_dispatch, m);
            if (r < 0)
                return log_error_errno(r, "Failed to add button event for %s: %m", m->button[i].devpath);
        }
    }

    r = manager_run(m);

    log_info("exit");
    return r;
}

DEFINE_MAIN_FUNCTION(run);
