package com.chy.borrow.util;


import com.chy.borrow.util.config.CsvConfig;
import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author:
 * @description: 提供csv文件的操作方法
 * @date Create in 2018/12/24 15:01
 * @modified By:
 */
public class CSVUtil {


    /**
     * 读取csc文件
     *
     * @param filePath      文件路径
     * @param clazz         指定类型
     * @param csvConfig     配置信息
     * @return              读取结果
     */

    public static <T> List<T> readFile(String filePath, Class<T> clazz, CsvConfig csvConfig) throws Exception {
        if (null == filePath || null == clazz) {
            return null;
        }
        List<T> result = new ArrayList<>(10);
        checkCsvConfig(csvConfig);
        CsvReader reader = new CsvReader(filePath, csvConfig.getSeparator(), Charset.forName(csvConfig.getCharset()));

        //获取类方法
        Map<Field, Method> methods = new HashMap<>(11);
        Field[] fields = clazz.getDeclaredFields();
        if (fields == null) {
//            log.error("========未获取到类{}的属性值，请确认类{}是否传入正确========", clazz.getName(), clazz.getName());
            return null;
        }
        for (int i = 0; i < fields.length; i++) {
            String methodName = "set" + fields[i].getName().substring(0, 1).toUpperCase() + fields[i].getName().substring(1);
            Method method = clazz.getMethod(methodName, fields[i].getType());
            if (method == null) {
//                log.info("====类{}中没有属性{}的set方法，请确定是否实现====", clazz.getName(), fields[i].getName());
                continue;
            }
            methods.put(fields[i], method);
        }
        //跳过头文件
        reader.readHeaders();
        String[] headers = reader.getHeaders();
        if(null == headers || headers.length <=0){
//            log.error("========文件< {} >缺少数据头，请增加数据头到文件========",filePath);
            return null;
        }
        while (reader.readRecord()) {
            T obj = (T)clazz.newInstance();
            Set<Field> keys = methods.keySet();
            for (Field key : keys) {
                key.setAccessible(true);
                String value = reader.get(key.getName());
                if(StringUtils.isBlank(value)){
                    continue;
                }
                switch (key.getType().getSimpleName()) {
                    case "Integer":
                        methods.get(key).invoke(obj,Integer.valueOf(value));
                        break;
                    case "String":
                        methods.get(key).invoke(obj,value);
                        break;
                    case "Boolean":
                        methods.get(key).invoke(obj,Boolean.valueOf(value));
                        break;
                    case "Long":
                        methods.get(key).invoke(obj,Long.valueOf(value));
                        break;
                    case "Float":
                        methods.get(key).invoke(obj,Float.valueOf(value));
                        break;
                    case "Double":
                        methods.get(key).invoke(obj,Double.valueOf(value));
                        break;
                    case "Date":
                        try {
                            methods.get(key).invoke(obj,new SimpleDateFormat(csvConfig.getDateFormat()).parse(value));
                            break;
                        } catch (ParseException e) {
//                            log.info("====日期转换失败,使用的日期格式为：{},与给定的日期数据格式不匹配,请重新指定日期转换格式====",csvConfig.getDateFormat());
//                            log.info("====错误原因：{}",e);
                            throw new RuntimeException(e);
                        }
                    default:
                        methods.get(key).invoke(obj,value);
                        break;
                }
            }
            result.add(obj);
        }
        reader.close();
        return result;
    }

    public static List<Map> readFileToMap(String filePath, CsvConfig csvConfig) throws Exception {
        if (null == filePath ) {
            return null;
        }
        List<Map> result = new ArrayList<>(10);
        checkCsvConfig(csvConfig);
        CsvReader reader = new CsvReader(filePath, csvConfig.getSeparator(), Charset.forName(csvConfig.getCharset()));

        //获取类方法
        Map<Field, Method> methods = new HashMap<>(11);
        //跳过头文件
        reader.readHeaders();
        String[] headers = reader.getHeaders();
        if(null == headers || headers.length <=0){
//            log.error("========文件< {} >缺少数据头，请增加数据头到文件========",filePath);
            return null;
        }
        while (reader.readRecord()) {
            Map obj = new HashMap();
            String[] keys = reader.getHeaders();
            for (String key : keys) {
                String value = reader.get(key);
                if(StringUtils.isBlank(value)){
                    continue;
                }
                obj.put(key,value);
            }
            result.add(obj);
        }
        reader.close();
        return result;
    }

    /**
     * 读取csc文件
     * 默认编码格式是本地编码格式
     * @param filePath      文件路径
     * @param separator     分隔符
     * @param t             指定类型
     * @return              读取结果
     */
    public static <T> List<T> readFile(String filePath, char separator, Class<T> t) throws Exception {
        return readFile(filePath,t,initCsvConfig());
    }
    /**
     * 读取csc文件
     * 默认编码格式是本地编码格式
     * @param filePath      文件路径
     * @param t             指定类型
     * @return              读取结果
     */
    public static <T> List<T> readFile(String filePath, Class<T> t) throws Exception {
        return readFile(filePath,t,initCsvConfig());
    }

    /**
     * 把缓存内容写入到csv文件
     *
     * @param filePath       文件路径
     * @param cacheContainer 缓存容器
     * @param csvConfig      配置信息
     * @return 写入是否成功
     */
    public <T> boolean writeFile(String filePath, List<T> cacheContainer, Class<T> t,CsvConfig csvConfig) {
        CsvWriter writer;
        if (StringUtils.isBlank(filePath) || CollectionUtils.isEmpty(cacheContainer) || null == t) {
            return false;
        }
        this.checkCsvConfig(csvConfig);
        writer = new CsvWriter(filePath, csvConfig.getSeparator(), Charset.forName(csvConfig.getCharset()));
        //获取实体类中的属性
        Field[] fields = t.getDeclaredFields();
        //生成数据头
        String[] headers = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            headers[i] = fields[i].getName();
        }
        //写入到文件
        try {
            writer.writeRecord(headers);
            for (T obj : cacheContainer) {
                String[] str = coverFieldsValue(obj,csvConfig);
                writer.writeRecord(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        writer.close();
        return true;
    }

    /**
     * 把缓存内容写入到csv文件
     *
     * @param filePath       文件路径
     * @param cacheContainer 缓存容器
     * @param csvConfig      配置信息
     * @return 写入是否成功
     */
    public static boolean writeFileForMap(String filePath, List<Map> cacheContainer,CsvConfig csvConfig) {
        CsvWriter writer;
        if (StringUtils.isBlank(filePath) || CollectionUtils.isEmpty(cacheContainer) ) {
            return false;
        }
        checkCsvConfig(csvConfig);
        writer = new CsvWriter(filePath, csvConfig.getSeparator(), Charset.forName(csvConfig.getCharset()));
        //获取实体类中的属性
//        Field[] fields = t.getDeclaredFields();
        Set fields = cacheContainer.get(0).keySet();
        //生成数据头
        String[] headers = new String[fields.size()];
        int i = 0;
        for(Object o : fields){
            headers[i] = o.toString();
            i++;
        }
        //写入到文件
        try {
            writer.writeRecord(headers);
            for (Map obj : cacheContainer) {
                String[] str = new String[fields.size()];
                int j = 0;
                for(Object o : fields){
                    str[j] = obj.get(o)==null?null:obj.get(o).toString();
                    j++;
                }
                writer.writeRecord(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        writer.close();
        return true;
    }

    /**
     * 把缓存内容写入到csv文件
     * 默认编码格式是本地编码格式
     * @param filePath       文件路径
     * @param cacheContainer 缓存容器
     * @return 写入是否成功
     */
    public static boolean writeFileForMap(String filePath, List<Map> cacheContainer) {
        File fileZip = new File(filePath);
        if (!fileZip.getParentFile().exists()){
            fileZip.getParentFile().mkdirs();
        }
        return writeFileForMap(filePath,cacheContainer,initCsvConfig());
    }

    /**
     * 把缓存内容写入到csv文件
     * 默认编码格式是本地编码格式
     * @param filePath       文件路径
     * @param cacheContainer 缓存容器
     * @return 写入是否成功
     */
    public <T> boolean writeFile(String filePath, List<T> cacheContainer, Class<T> t) {
        return this.writeFile(filePath,cacheContainer,t,this.initCsvConfig());
    }

    /**
     * 把传入的实例属性的值封装成字符串数组
     *
     * @param obj       实例
     * @return          字符串数组
     * @throws Exception    异常
     */
    private <T>  String[] coverFieldsValue(T obj,CsvConfig csvConfig) throws Exception {
        String[] result ;
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        if (null == fields || fields.length <= 0) {
            return null;
        }
        result = new String[fields.length];
        for (int i = 0; i < fields.length; i++) {
            new Date();
            String methodName = "get" + fields[i].getName().substring(0, 1).toUpperCase() + fields[i].getName().substring(1);
            Method method = clazz.getMethod(methodName);
            Object value = method.invoke(obj);
            if(null == value){
                continue;
            }
            if("Date".equals(fields[i].getType().getSimpleName())){
                Date date = (Date)value;
                result[i] = new SimpleDateFormat(csvConfig.getDateFormat()).format(date);
                continue;
            }
            result[i] = value.toString();
        }
        return result;
    }

    /**
     * 构造一个默认的配置实例
     * 默认编码格式为本地系统编码格式
     * @return      设有默认值的配置实例
     */
    public static CsvConfig initCsvConfig(){
        String charset = System.getProperty("file.encoding");
        return new CsvConfig(charset,"yyyy-MM-dd HH:mm:ss:SSS",',');
    }

    /**
     * 检测给予系统配置信息的完整性
     * @param csvConfig        给定的配置实例
     */
    private static void checkCsvConfig(CsvConfig csvConfig){
        if(null == csvConfig){
            csvConfig = initCsvConfig();
        }else{
            if(null == csvConfig.getCharset()){
                csvConfig.setCharset(System.getProperty("file.encoding"));
            }
            if(null == csvConfig.getDateFormat()){
                csvConfig.setDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
            }
            //没有指定分隔符
            if(0 == csvConfig.getSeparator()){
                csvConfig.setSeparator(',');
            }
        }
    }

    public static void main(String[] args) {
//        List<Map> list = new ArrayList<>();
//        Map map = new HashMap();
//        map.put("test1","111");
//        map.put("test2","222");
//        list.add(map);
//        CSVUtil.writeFileForMap("D:\\test.csv",list);
        try {
            System.out.println(CSVUtil.readFileToMap("D:\\test.csv",initCsvConfig()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
