/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package org.apache.james.imapserver;

import java.io.*;
import java.util.*;

/**
 * Reference: RFC 2060
 * @author <a href="mailto:s@rapi.com">Stephan Schiessling</a>
 * @version 0.1 on 15 Aug 2002
 */
public class FileStoreHighestUID implements HighestUID {

    /**
     * The number of increases to allow before persisting to
     * disk.
     */
    private static final int WRITE_STEP = 3;

    /**
     * The current value of the highest UID
     */
    private int highestUID;

    /**
     * The next value when the highest UID will be persisted to disk.
     */
    private int whenToWrite;

    /**
     * The file where the highest UID is persisted.
     */
    private File file;

    /**
     * Random number generator used to generate new starter
     * ids for each mailbox.
     */
    private static final Random theRandom = new Random();

    /**
     * File constructor.  Will default the UID to 0 if the file doesn't
     * exist.  May be removed shortly.
     *
     * @param f the file where the UID is to be stored.
     */
    public FileStoreHighestUID(File f) {
        this(f, false);
    }

    /**
     * Constructor that allows the user to specify whether they want
     * the starting UID to be randomly calculated if no file containing
     * the UID can be found.
     *
     * @param f the File where the UID supremum will be stored.
     * @param computeRandomStarterId whether a random UID should be computed
     *                               if the file does not exist.  If false,
     *                               UIDs will start with 0.
     */
    public FileStoreHighestUID(File f, boolean computeRandomStarterId) {
        file = f;
        int starterUID = 0;
        if (computeRandomStarterId) {
            // TODO : Encapsulate this logic
            starterUID = theRandom.nextInt((int)((Integer.MAX_VALUE - 1) / 2));
        }
        highestUID = starterUID;

        if (file.exists()) {
            ObjectInputStream is = null;
            try {
                is = new ObjectInputStream(new FileInputStream(file));
                Integer i = (Integer) is.readObject();
                highestUID = i.intValue();
                is.close();
                is = null;
            } catch (Exception ex) {
                // log here
                ex.printStackTrace();
                if (is != null) {
                    try {
                        is.close();
                    } catch (Exception ignored) {}
                }
                throw new RuntimeException("Could not load highestUID!");
            }
            // maybe james was stopped without writing correct highestUID, therefore add
            // STEP_HIGHEST_UID, to ensure uniqeness of highestUID.
            highestUID += WRITE_STEP;
        }
        write();
        System.out.println("Initialized highestUID="+highestUID);
    }

    /**
     * Retrieves the current value for the highestUID.
     *
     * @return the current value for the highestUID
     */
    public synchronized int get() {
        return highestUID;
    }
  
    /**
     * Increases the value of the highestUID by 1.  Will
     * write the value to disk if the total increment since
     * the last write is WRITE_STEP
     */
    public synchronized void increase() {
        highestUID++;
        if (highestUID >= whenToWrite) {
            // save this highestUID
            // make it persistent
            write();
        }
    }

    /**
     * Writes the highest UID to disk.
     */
    private void write() {
        ObjectOutputStream os = null;
        try {
            os = new ObjectOutputStream( new FileOutputStream(file));
            os.writeObject(new Integer(highestUID));
            os.close();
            os = null;
            // Increment whenToWrite only after we've successfully written to disk.
            whenToWrite = highestUID+WRITE_STEP;
        } catch (Exception ex) {
            // log here
            ex.printStackTrace();
            if (os != null) {
                try {
                    os.close();
                } catch (Exception ignored) {}
            }
            throw new RuntimeException("Failed to save highestUID!");
        }
    }
}

