package org.klomp.snark.dht;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;

/**
 *  Retrieve / Store the local DHT in a file
 *
 *  @since 0.9.2
 */
abstract class PersistDHT {

    private static final long MAX_AGE = 60*60*1000;

    /**
     *  @param backupFile may be null
     *  @since 0.9.6
     */
    public static synchronized void loadDHT(KRPC krpc, File file, File backupFile) {
        if (file.exists())
            loadDHT(krpc, file);
        else if (backupFile != null)
            loadDHT(krpc, backupFile);
    }

    public static synchronized void loadDHT(KRPC krpc, File file) {
        Log log = I2PAppContext.getGlobalContext().logManager().getLog(PersistDHT.class);
        int count = 0;
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(
            		new FileInputStream(file), "ISO-8859-1"));
            String line = null;
            while ( (line = br.readLine()) != null) {
                if (line.startsWith("#"))
                    continue;
                try {
                    krpc.heardAbout(new NodeInfo(line));
                    count++;
                    // TODO limit number? this will flush the router's SDS caches
                } catch (IllegalArgumentException iae) {
                    if (log.shouldLog(Log.WARN))
                        log.warn("Error reading DHT entry", iae);
                } catch (DataFormatException dfe) {
                    if (log.shouldLog(Log.WARN))
                        log.warn("Error reading DHT entry", dfe);
                }
            }
        } catch (IOException ioe) {
            if (log.shouldLog(Log.WARN) && file.exists())
                log.warn("Error reading the DHT File", ioe);
        } finally {
            if (br != null) try { br.close(); } catch (IOException ioe) {}
        }
        if (log.shouldLog(Log.INFO))
            log.info("Loaded " + count + " nodes from " + file);
    }

    /**
     *  @param saveAll if true, don't check last seen time
     */
    public static synchronized void saveDHT(DHTNodes nodes, boolean saveAll, File file) {
        if (nodes.size() <= 0)
            return;
        Log log = I2PAppContext.getGlobalContext().logManager().getLog(PersistDHT.class);
        int count = 0;
        long maxAge = saveAll ? 0 : I2PAppContext.getGlobalContext().clock().now() - MAX_AGE;
        PrintWriter out = null;
        try {
            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "ISO-8859-1")));
            out.println("# DHT nodes, format is NID:Hash:Destination:port");
            for (NodeInfo ni : nodes.values()) {
                 if (ni.lastSeen() < maxAge)
                     continue;
                 // DHTNodes shouldn't contain us, if that changes check here
                 out.println(ni.toPersistentString());
                 count++;
            }
            if (out.checkError())
                throw new IOException("Failed write to " + file);
        } catch (IOException ioe) {
            if (log.shouldLog(Log.WARN))
                log.warn("Error writing the DHT File", ioe);
        } finally {
            if (out != null) out.close();
        }
        if (log.shouldLog(Log.INFO))
            log.info("Stored " + count + " nodes to " + file);
    }
}
