package freenet.node;

import static java.util.concurrent.TimeUnit.MINUTES;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import freenet.support.Logger;
import freenet.support.SimpleFieldSet;
import freenet.support.Ticker;
import freenet.support.io.Closer;
import freenet.support.io.FileUtil;

class Persister implements Runnable {
        private static volatile boolean logMINOR;
        static {
            Logger.registerClass(Persister.class);
        }

        static final long PERIOD = MINUTES.toMillis(15);

	Persister(Persistable t, File persistTemp, File persistTarget, Ticker ps) {
		this.persistable = t;
		this.persistTemp = persistTemp;
		this.persistTarget = persistTarget;
		this.ps = ps;
	}
	
	// Subclass must set the others later
	protected Persister(Persistable t, Ticker ps) {
		this.persistable = t;
		this.ps = ps;
	}
	
	final Persistable persistable;
	private final Ticker ps;
	File persistTemp;
	File persistTarget;
	private boolean started;
	
	void interrupt() {
		synchronized(this) {
			notifyAll();
		}
	}

	@Override
	public void run() {
		freenet.support.Logger.OSThread.logPID(this);
		try {
			persistThrottle();
		} catch (Throwable t) {
			Logger.error(this, "Caught in ThrottlePersister: "+t, t);
			System.err.println("Caught in ThrottlePersister: "+t);
			t.printStackTrace();
			System.err.println("Will restart ThrottlePersister...");
		}
		ps.queueTimedJob(this, PERIOD);
	}
	
	private void persistThrottle() {
		if (logMINOR) {
			Logger.minor(this, "Trying to persist throttles...");
		}
		SimpleFieldSet fs = persistable.persistThrottlesToFieldSet();
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(persistTemp);
			fs.writeToBigBuffer(fos);
			fos.close();
			FileUtil.renameTo(persistTemp, persistTarget);
		} catch (FileNotFoundException e) {
			Logger.error(this, "Could not store throttle data to disk: " + e, e);
		} catch (IOException e) {
			persistTemp.delete();
		} finally {
			Closer.close(fos);
		}
	}

	public SimpleFieldSet read() {
		SimpleFieldSet throttleFS = null;
		try {
			throttleFS = SimpleFieldSet.readFrom(persistTarget, false, true);
		} catch (IOException e) {
			try {
				throttleFS = SimpleFieldSet.readFrom(persistTemp, false, true);
			} catch (FileNotFoundException e1) {
				// Ignore
			} catch (IOException e1) {
				if(persistTarget.length() > 0 || persistTemp.length() > 0)
					Logger.error(this, "Could not read "+persistTarget+" ("+e+") and could not read "+persistTemp+" either ("+e1+ ')');
			}
		}
		return throttleFS;
	}

	public void start() {
		synchronized(this) {
			if(started) {
				Logger.error(this, "Already started: "+this, new Exception("debug"));
				return;
			}
			started = true;
		}
		SemiOrderedShutdownHook.get().addEarlyJob(new Thread() {
			
			public void run() {
				System.out.println("Writing "+persistTarget+" on shutdown");
				persistThrottle();
			}
			
		});
		run();
	}

}
