/** 
 * 
 * Copyright 2004 Protique Ltd
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 * 
 **/

package org.activemq.io.util;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList;

/**
 * A DataContainer handles file persistence for a DiskBoundedQueue
 * The DataContainer is a temporary data structure, that is only
 * designed to exist for the lifetime of the application
 * 
 * @version $Revision: 1.1.1.1 $
 */
class DataContainer {
    private CopyOnWriteArrayList dataBlocks = new CopyOnWriteArrayList();
    private FileDataBlock writeBlock;
    private FileDataBlock readBlock;
    private File dir;
    private long length;
    private int size;
    private String name;
    private int maxBlockSize;
    private int sequence;
    private static final String SUFFIX = ".fdb";
    private static final Log log = LogFactory.getLog(DataContainer.class);

    /**
     * Constructor for the data container
     * 
     * @param dir directory where to create the data blocks
     * @param name for the data block names
     * @param maxBlockSize maximum size (in bytes) of the data blocks
     * @throws IOException
     */
    DataContainer(File dir, String name, int maxBlockSize) throws IOException {
        this.dir = dir;
        this.name = name;
        this.maxBlockSize = maxBlockSize;
    }

    /**
     * Delete all previous files of the same suffix in the directory
     */
    void deleteAll() {
        FileFilter filter = new FileFilter() {
            public boolean accept(File file) {
                return (file.getName().endsWith(SUFFIX) && file.getName().startsWith(name));
            }
        };
        File[] files = dir.listFiles(filter);
        if (files != null) {
            for (int i = 0;i < files.length;i++) {
                files[i].delete();
            }
        }
    }
    

   
    /**
     * @return true if this DataContainer is empty
     */
    public synchronized boolean isEmpty() {
        return size == 0;
    }

    /**
     * @return the length (in bytes) of unread data in the DataContainer
     */
    public long length() {
        return length;
    }

    /**
     * @return the number of data entries unread
     */
    public int size() {
        return size;
    }

    /**
     * write a block of data into the Container
     * 
     * @param data
     * @throws IOException
     */
    public synchronized void write(byte[] data) throws IOException {
        if (writeBlock == null) {
            writeBlock = createDataBlock(sequence++);
            dataBlocks.add(writeBlock);
            readBlock = writeBlock;
        }
        else if (!writeBlock.isEnoughSpace(data)) {
            writeBlock = createDataBlock(sequence++);
            dataBlocks.add(writeBlock);
        }
        length += data.length;
        size++;
        writeBlock.write(data);
    }

    /**
     * read a block of data from the container
     * 
     * @return next data entry to read
     * @throws IOException
     */
    public byte[] read() throws IOException {
        byte[] result = null;
        if (readBlock != null) {
            result = readBlock.read();
            if (result == null) {
                if (readBlock != writeBlock) {
                    readBlock.close();
                    dataBlocks.remove(readBlock);
                    readBlock = (FileDataBlock) dataBlocks.get(0);
                }
            }
            else {
                length -= result.length;
                size--;
            }
        }
        return result;
    }

    /**
     * close the DataContainer and corresponding FileDataBlocks
     * 
     * @throws IOException
     */
    public void close() throws IOException {
        for (int i = 0;i < dataBlocks.size();i++) {
            FileDataBlock db = (FileDataBlock) dataBlocks.get(i);
            db.close();
        }
        dataBlocks.clear();
        readBlock = null;
        writeBlock = null;
        size = 0;
        length = 0l;
    }

    /**
     * create a FileDataBlock
     * 
     * @param sequence
     * @return a new FileDataBlock
     * @throws IOException
     */
    private FileDataBlock createDataBlock(int sequence) throws IOException {
        String fileName = name + "_" + sequence + SUFFIX;
        if (!dir.exists()){
            log.info("making directory for temporary spooled data: " + dir);
            dir.mkdirs();
        }
        File file = File.createTempFile(name, SUFFIX, dir);
        file.deleteOnExit();
        return new FileDataBlock(file, maxBlockSize);
    }
}
