package net.hasor.rsf.address;
import net.hasor.rsf.RsfEnvironment;
import net.hasor.rsf.RsfSettings;
import net.hasor.rsf.domain.RsfConstants;
import net.hasor.rsf.utils.IOUtils;
import net.hasor.utils.MatchUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 服务地址的辅助工具,负责读写本地地址本缓存。
 * @version : 2014年9月12日
 * @author 赵永春 (zyc@hasor.net)
 */
public class DiskCacheAddressPool extends AddressPool {
    protected final Logger        logger     = LoggerFactory.getLogger(getClass());
    private final   AtomicBoolean inited     = new AtomicBoolean(false);
    private final   Thread        timer;
    private final   File          snapshotHome;
    private final   File          indexFile;
    private         boolean       exitThread = false;

    public DiskCacheAddressPool(final RsfEnvironment rsfEnvironment) {
        super(rsfEnvironment);
        File rsfHome = new File(rsfEnvironment.evalString("%RSF_DATA_HOME%/"));
        this.snapshotHome = new File(rsfHome, RsfConstants.SnapshotPath);
        this.indexFile = new File(snapshotHome, RsfConstants.SnapshotIndex);
        //
        this.timer = new Thread(new Runnable() {
            public void run() {
                if (rsfEnvironment.getSettings().islocalDiskCache()) {
                    doWork();
                }
            }
        });
        this.timer.setContextClassLoader(rsfEnvironment.getClassLoader());
        this.timer.setName("RSF-DiskCacheAddressPool-Timer");
        this.timer.setDaemon(true);
    }

    /** 启动定时器,定时进行地址本的磁盘缓存。*/
    public void startTimer() {
        if (this.inited.compareAndSet(false, true)) {
            this.logger.info("startTimer address snapshot Thread[{}].", timer.getName());
            this.exitThread = false;
            this.timer.start();
        }
    }

    /** 停止定时器,停止定时进行地址本的磁盘缓存。*/
    public void shutdownTimer() {
        if (this.inited.compareAndSet(true, false)) {
            this.logger.info("shutdownTimer address snapshot Thread[{}].", timer.getName());
            this.exitThread = true;
        }
    }

    private void doWork() {
        this.exitThread = false;
        RsfSettings rsfSettings = this.getRsfEnvironment().getSettings();
        long refreshCacheTime = rsfSettings.getRefreshCacheTime();
        long diskCacheTimeInterval = rsfSettings.getDiskCacheTimeInterval();
        long nextCheckSavePoint = 0;
        //
        if (diskCacheTimeInterval <= 0) {
            diskCacheTimeInterval = RsfConstants.OneHourTime;
        }
        //
        this.logger.info("AddressPool - Timer -> start, refreshCacheTime = {}.", refreshCacheTime);
        while (!this.exitThread) {
            //1.启动时做一次清理
            clearCacheData();
            try {
                Thread.sleep(refreshCacheTime);
            } catch (InterruptedException e) {
                /**/
            }
            //2.将数据保存到缓存文件
            this.logger.info("AddressPool - refreshCache. at = {} , refreshCacheTime = {}.", nowTime(), refreshCacheTime);
            this.refreshAddressCache();
            if (rsfSettings.islocalDiskCache() && nextCheckSavePoint < System.currentTimeMillis()) {
                nextCheckSavePoint = System.currentTimeMillis() + diskCacheTimeInterval;/*每小时保存一次地址本快照。*/
                try {
                    storeConfig();
                } catch (IOException e) {
                    this.logger.error("saveAddress error {} -> {}", e.getMessage(), e);
                }
            }
        }
        this.logger.info("AddressPool - Timer -> stop.");
    }

    /**清理缓存的地址数据*/
    public void clearCacheData() {
        String[] fileNames = this.snapshotHome.list((dir, name) -> {
            return MatchUtils.wildToRegex("address-[0-9]{8}-[0-9]{6}.zip", name, MatchUtils.MatchTypeEnum.Regex);
        });
        List<String> sortList = (fileNames == null) ? new ArrayList<String>(0) : Arrays.asList(fileNames);
        Collections.sort(sortList);
        //
        long nowTime = System.currentTimeMillis() - RsfConstants.SevenDaysTime;//数据自动清理 7 天之前的数据
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-HHmmss");
        for (String itemName : sortList) {
            try {
                String dateTimeStr = itemName.substring(RsfConstants.AddrPoolStoreName.length(), itemName.length() - ".zip".length());
                Date dateTime = format.parse(dateTimeStr);
                if (dateTime.getTime() < nowTime) {
                    new File(this.snapshotHome, itemName).delete();
                }
            } catch (Exception e) { /**/ }
        }
    }
    //

    /**保存地址列表到zip流中(每小时保存一次)，当遇到保存的文件已存在时会重新生成新的文件名。*/
    public synchronized void storeConfig() throws IOException {
        File writeFile = null;
        while (writeFile == null || writeFile.exists()) {
            writeFile = new File(this.snapshotHome, RsfConstants.AddrPoolStoreName + nowTime() + ".zip");
        }
        this.logger.info("rsf - saveAddress to snapshot file({}) ->{}", writeFile);
        FileOutputStream fos = null;
        FileWriter fw = null;
        try {
            boolean mkdirResult = writeFile.getParentFile().mkdirs();
            if (mkdirResult || writeFile.getParentFile().exists()) {
                fos = new FileOutputStream(writeFile, false);
                fos.getFD().sync();//独占文件
                //
                this.storeConfig(fos);
                //
                fos.flush();
                fos.close();
                //
                fw = new FileWriter(this.indexFile, false);
                this.logger.info("rsf - update snapshot index -> " + this.indexFile.getAbsolutePath());
                fw.write(writeFile.getName());
                fw.flush();
                fw.close();
            }
        } catch (IOException e) {
            this.logger.error("rsf - saveAddress " + e.getClass().getSimpleName() + " :" + e.getMessage(), e);
            throw e;
        } finally {
            if (fos != null) {
                fos.close();
            }
            if (fw != null) {
                fw.close();
            }
        }
    }

    /**从保存的地址本中恢复数据。*/
    public synchronized void restoreConfig() {
        //1.校验
        if (!this.indexFile.exists()) {
            this.logger.info("address snapshot index file, undefined.");
            return;
        }
        if (!this.indexFile.canRead()) {
            this.logger.error("address snapshot index file, can not read.");
            return;
        }
        //2.确定要读取的文件。
        File readFile = null;
        try {
            FileReader reader = new FileReader(this.indexFile);
            List<String> bodyList = IOUtils.readLines(reader);
            String index = bodyList.isEmpty() ? "" : bodyList.get(0);
            readFile = new File(this.snapshotHome, index);
            if ("".equals(index) || !readFile.exists()) {
                this.logger.error("address snapshot file is not exist.", readFile);
                return;
            }
        } catch (Throwable e) {
            this.logger.error("read the snapshot file name error :" + e.getMessage(), e);
            return;
        }
        //
        //3.恢复数据数据
        FileInputStream inStream = null;
        try {
            inStream = new FileInputStream(readFile);
            this.restoreConfig(inStream);
            inStream.close();
        } catch (IOException e) {
            this.logger.error("read the snapshot file name error :" + e.getMessage(), e);
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (IOException e1) {
                    this.logger.error(e1.getMessage(), e1);
                }
            }
        }
    }

    private static String nowTime() {
        return new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date());
    }
}