/*************************************************************************
    > File Name: rktools.cpp
    > Author: jkand.huang
    > Mail: jkand.huang@rock-chips.com
    > Created Time: Fri 17 May 2019 07:30:44 PM CST
 ************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "log.h"
#include "rktools.h"
#include "download.h"
extern "C" {
    #include "../mtdutils/mtdutils.h"
}


#define LOCAL_VERSION_PATH "/etc/version"
#define DOWNLOAD_VERSION_PATH "/tmp/version"

bool getVersionFromfile(const char * filepath,char *version, int maxLength) {
    if (version == NULL || filepath == NULL) {
        LOGE("getLocalVersion is error, version == null.\n");
        return false;
    }
    FILE *fp = fopen(filepath, "r");
    if (fp == NULL) {
        LOGE("open %s failed, error is %s.\n", filepath, strerror(errno));
        return false;
    }

    char *line = NULL;
    size_t len = 0;
    size_t read;
    while ((read = getline(&line, &len, fp)) != -1) {
        if (read == 0 || line[0] == '#') {
            continue;
        }
        char *pline = strstr(line, "RK_VERSION");
        if (pline != NULL && (pline = strstr(pline, "=")) != NULL) {
            pline++; //过滤掉等于号
            //过滤掉空格
            while (*pline == ' ') {
                pline++;
            }
            int pline_len = strlen(pline) - 1;
            int version_len = (pline_len > maxLength ? maxLength:pline_len);
            memcpy(version, pline, version_len);
            LOGI("version = %s.\n", version);
            break;
        }
    }
    free(line);
    fclose(fp);
    return true;
}

//下载服务器版本号文件
bool getRemoteVersion(char *url, char *version, int maxLength) {
    if (url == NULL) {
        LOGE("getRemoteVersion url is null.\n");
        return false;
    }

    if (download_file(url, DOWNLOAD_VERSION_PATH) == -1){
        LOGE("getRemoteVersion failed, url is %s.\n", url);
        return false;
    }

    return getVersionFromfile(DOWNLOAD_VERSION_PATH, version, maxLength);
}

//获取本地版本号
bool getLocalVersion(char *version, int maxLength) {
    return getVersionFromfile(LOCAL_VERSION_PATH, version, maxLength);
}

//判断是MTD还是block 设备
bool isMtdDevice() {
    char param[2048];
    int fd, ret;
    char *s = NULL;
    fd = open("/proc/cmdline", O_RDONLY);
    ret = read(fd, (char*)param, 2048);
    close(fd);
    s = strstr(param,"storagemedia");
    if(s == NULL){
        LOGI("no found storagemedia in cmdline, default is not MTD.\n");
        return false;
    }else{
        s = strstr(s, "=");
        if (s == NULL) {
            LOGI("no found storagemedia in cmdline, default is not MTD.\n");
            return false;
        }

        s++;
        while (*s == ' ') {
            s++;
        }

        if (strncmp(s, "mtd", 3) == 0 ) {
            LOGI("Now is MTD.\n");
            return true;
        } else if (strncmp(s, "sd", 2) == 0) {
            LOGI("Now is SD.\n");
            if ( !access(MTD_PATH, F_OK) ) {
                fd = open(MTD_PATH, O_RDONLY);
                ret = read(fd, (char*)param, 2048);
                close(fd);

                s = strstr(param,"mtd");
                if(s == NULL){
                    LOGI("no found mtd.\n");
                    return false;
                }
                LOGI("Now is MTD.\n");
                return true;
            }
        }
    }
    LOGI("Current device is not MTD");
    return false;
}

/**
 * 从cmdline 获取从哪里引导
 * 返回值:
 *     0: a分区
 *     1: b分区
 *    -1: recovery 模式
 */
int getCurrentSlot(){
    char cmdline[CMDLINE_LENGTH];
    int fd = open("/proc/cmdline", O_RDONLY);
    read(fd, (char*)cmdline, CMDLINE_LENGTH);
    close(fd);
    char *slot = strstr(cmdline, "android_slotsufix");
    if(slot == NULL) slot = strstr(cmdline, "androidboot.slot_suffix");
    if(slot != NULL){
        slot = strstr(slot, "=");
        if(slot != NULL && *(++slot) == '_'){
            slot += 1;
            LOGI("Current Mode is '%c' system.\n", (*slot == 'a')? 'A':'B');
            if((*slot) == 'a'){
                return 0;
            }else if((*slot) == 'b'){
                return 1;
            }
        }
    }
    LOGI("Current Mode is recovery.\n");
    return -1;
}

void getFlashPoint(char *path) {
    char *emmc_point = getenv(EMMC_POINT_NAME);
        LOGI("test Current device is emmc : %s.\n", emmc_point);
    if ( !access(emmc_point, F_OK) ) {
        LOGI("Current device is emmc : %s.\n", emmc_point);
        strcpy(path, emmc_point);
    } else if (strncmp("emmc", getenv("storagemedia"), 4) == 0) {
        LOGI("Current device is emmc : /dev/mmcblk0.\n");
        strcpy(path, "/dev/mmcblk0");
    } else {
        LOGI("Current device is nand : %s.\n", NAND_DRIVER_DEV_LBA);
        strcpy(path, NAND_DRIVER_DEV_LBA);
    }
}
/*
 * 获得flash 的大小,和块数
 */
int getFlashSize(char *path, long long* flash_size, long long* block_num) {

    LOGI("[%s:%d]\n", __func__, __LINE__);

    off64_t total_size_64 = 0;
    if (isMtdDevice()) {
        size_t erase_size;
        size_t total_size;
        mtd_scan_partitions();
        const MtdPartition *part = mtd_find_partition_by_name("rk-nand");
        if ( part == NULL ) {
            part = mtd_find_partition_by_name("spi-nand0");
        }
        if (part == NULL || mtd_partition_info(part, &total_size, &erase_size, NULL)) {
            LOGE("Can't find rk-nand or spi-nand0\n");
            return -1;
        }
        total_size = total_size - (erase_size * 4);
        total_size_64 = total_size;
    } else {
        char flash_name[20];
        getFlashPoint(flash_name);
        int fd_dest = open(flash_name, O_RDWR|O_LARGEFILE);
        if (fd_dest < 0) {
            LOGE("Can't open %s\n", flash_name);
            return -2;
        }
        if ((total_size_64 = lseek64(fd_dest, 0, SEEK_END)) == -1) {
            LOGE("getFlashInfo lseek64 failed.\n");
            close(fd_dest);
            return -2;
        }
        lseek64(fd_dest, 0, SEEK_SET);
        close(fd_dest);
    }
    if ( flash_size ) {
        *flash_size = total_size_64 / 1024; //Kib
        LOGI("[%s:%d] flash size [%lld] \n", __func__, __LINE__, *flash_size);
    }
    if ( block_num ) {
        *block_num = (total_size_64 / 1024) * 2;
        LOGI("[%s:%d]  block num [%lld]\n", __func__, __LINE__, *block_num);
    }

    return 0;
}

int getFlashInfo (size_t *total_size, size_t *block_size, size_t *page_size)
{
    if (isMtdDevice()) {
        if (mtd_get_flash_info(total_size, block_size, page_size) != 0) {
            LOGE("%s-%d: get mtd info error\n", __func__, __LINE__);
            return -1;
        }
        return 0;
    } else {
        LOGI("[%s:%d]\n", __func__, __LINE__);
        if (total_size) {
            LOGE("%s-%d: get flash total size error. NOT support now.\n", __func__, __LINE__);
            return -1;
        }
        if (block_size) *block_size = 512*1024;
        if (page_size) *page_size = 2*1024;
        return 0;
    }
}