package org.bdware.sc.redo;

import com.google.gson.JsonElement;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.sc.engine.DesktopEngine;
import org.bdware.sc.engine.SyncMechUtil;

import java.io.*;
import java.util.Map;
import java.util.TreeMap;

public class TransRecordUtil {
    public static final int RESERVED = 20;  //最近的多少次records内存也保存，如果一个节点距离集群当前的seq相差超过这个值，就不从本地恢复，让别的节点现场dump
    public static final int DUMP_PERIOD = 50;   //每满50次记录，就记录一次全量状态，清一次trans记录
    private static final Logger LOGGER = LogManager.getLogger(TransRecordUtil.class);
    public Map<Integer, TransRecord> cacheTransRecords = new TreeMap<Integer, TransRecord>();   //TODO 认为其中records一定是seq连续的，否则可能有问题?
    //public PriorityQueue<TransRecord> transRecords = new PriorityQueue<TransRecord>();
    public TransRecord currentTransRecord;
    SyncMechUtil syncUtil;
    DesktopEngine engine;
    String fileName;

    public TransRecordUtil(DesktopEngine en, SyncMechUtil sync) {
        this.engine = en;
        syncUtil = sync;
    }

    public void setFileName(String path) {
        fileName = path;
    }

    //每次事务开始时初始化
    public void startNext(String fun, JsonElement arg, int sequence) {
        //logger.debug("TransRecordUtil 开始记录事务");
        currentTransRecord = new TransRecord(fun, arg, sequence);
    }

    public void startNext(String fun, JsonElement arg) {
        //logger.debug("TransRecordUtil 开始记录事务");
        currentTransRecord = new TransRecord(fun, arg);
    }

    //每次事务结束时记录
    public synchronized void eachFinish() {
        //logger.debug("TransRecordUtil 记录完一个事务 \n" + currentTransRecord.toString());
        cacheTransRecords.put(currentTransRecord.seq, currentTransRecord);
        if (cacheTransRecords.size() == RESERVED) {
            int temp = 0;
            for (Integer i : cacheTransRecords.keySet()) {
                temp = i;
                break;
            }
            cacheTransRecords.remove(temp);
        }

        appendTransFile(currentTransRecord);   //执行前已经定序了，所以trans其实没必要定序
    }

    //追加写入最后一个TransRecord
    public void appendTransFile(TransRecord record) {
        syncUtil.filedTrans.getAndIncrement();

        File file = new File(syncUtil.transDir + "/" + fileName);
        File parent = file.getParentFile();
        if (parent.isDirectory() && !parent.exists()) {
            parent.mkdirs();
        }
        try {
            FileWriter fw = new FileWriter(file, true);
            PrintWriter pw = new PrintWriter(fw);
            pw.println(record.toString());
            pw.flush();
            fw.flush();
            pw.close();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //自动触发检查点
        if (syncUtil.filedTrans.get() == DUMP_PERIOD) {
            LOGGER.info("自动触发检查点  DUMP_PERIOD=" + DUMP_PERIOD);
            file = new File(syncUtil.memoryDir);
            if (file.isDirectory()) {
                String[] children = file.list();
                for (int i = 0; i < children.length; i++) {
                    File temp = new File(file, children[i]);
                    temp.delete();
                }
            }
            file = new File(syncUtil.traceDir);
            if (file.isDirectory()) {
                String[] children = file.list();
                for (int i = 0; i < children.length; i++) {
                    File temp = new File(file, children[i]);
                    temp.delete();
                }
            }
            file = new File(syncUtil.transDir);
            if (file.isDirectory()) {
                String[] children = file.list();
                for (int i = 0; i < children.length; i++) {
                    File temp = new File(file, children[i]);
                    temp.delete();
                }
            }

            //sync文件删除第2,3行
            file = new File(syncUtil.syncDir + "/" + syncUtil.syncFileName);
            String firstLine = "";
            try {
                FileReader fr = new FileReader(file);
                BufferedReader br = new BufferedReader(fr);
                String line = "";
                while ((line = br.readLine()) != null) {
                    firstLine = line;
                    break;
                }
                br.close();
                fr.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            file.delete();
            file = new File(syncUtil.syncDir + "/" + syncUtil.syncFileName);

            try {
                FileWriter fw = new FileWriter(file, true);
                PrintWriter pw = new PrintWriter(fw);
                pw.println(firstLine);
                pw.flush();
                fw.flush();
                pw.close();
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            syncUtil.setStartFlag(true);
        }
    }

    //record executeContract in current trans
    public void recordExecutes(String k, String v) {
        if (currentTransRecord != null) {
            currentTransRecord.executes.put(k, v);
        } else {
            LOGGER.info("[TransRecordUtil] recordExecutes error!");
        }
    }

    public String getCachedTransRecords(int start) {
        StringBuilder str = new StringBuilder();

        //先查看有没有第1个
        if (!cacheTransRecords.containsKey(start))
            return "";

        synchronized (cacheTransRecords) {
            int temp = -1;
            int j = start - 1;//确保有从start开始的连续trans,j记录上一个i值
            for (Integer i : cacheTransRecords.keySet()) {
                if (i >= start) {
                    if (i == (j + 1)) {
                        str.append(cacheTransRecords.get(i).toString() + "\n");
                        temp = Math.max(temp, i);
                        j = i;
                    } else {
                        LOGGER.info("i=" + i + "  j=" + j + "   不连续");
                        return "";
                    }
                }
            }
            if (temp != -1)
                str.append("==>>" + temp);
        }
        return str.toString();
    }
}
