/*
 * Copyright 2014 Rackspace
 *
 *    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 com.rackspacecloud.blueflood.outputs.cloudfiles;

import com.rackspacecloud.blueflood.eventemitter.RollupEvent;
import com.rackspacecloud.blueflood.outputs.serializers.RollupEventSerializer;
import com.rackspacecloud.blueflood.service.Configuration;
import com.rackspacecloud.blueflood.service.CoreConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.text.SimpleDateFormat;

// Not thread-safe
public class RollupFile implements Comparable {
    private static final Logger log = LoggerFactory.getLogger(RollupFile.class);

    private final File file;
    private FileOutputStream outputStream;
    private long timestamp;
    private RollupEventSerializer serializer = new RollupEventSerializer();
    public static final FileFilter fileFilter = new RollupFileFilter();

    public RollupFile(File file) {
        this.file = file;
        this.timestamp = parseTimestamp(file.getName());
    }

    /**
     * Retrieve the timestamp associated with this file.
     *
     * @return The timestamp associated with the file.
     */
    public long getTimestamp() {
        return timestamp;
    }

    /**
     * Get the name of the buffer file.
     *
     * @return The name of the buffer file.
     */
    public String getName() {
        return file.getName();
    }

    /**
     * Get the remote file name.
     *
     * @return The path to the remote file.
     */
    public String getRemoteName() {
        java.util.Date time = new java.util.Date(timestamp / 1000000); // convert back from nanoseconds
        String str = new SimpleDateFormat("yyyyMMdd_").format(time);
        return str + Configuration.getInstance().getStringProperty(CoreConfig.SHARDS) + "_" + getName();
    }

    /**
     * Get the age of the rollup file in milliseconds.
     *
     * @return The age of the rollup file in milliseconds.
     */
    public long getAge() {
        return (System.nanoTime() - timestamp) / 1000;
    }

    /**
     * Get the size of the rollup file in bytes.
     *
     * @return The size of the rollup file in bytes.
     * @throws IOException
     */
    public long getSize() throws IOException {
        ensureOpen();
        return outputStream.getChannel().size();
    }

    /**
     * Retrieve an InputStream which reads from this rollup file.
     *
     * @return An InputStream of data from the file.
     * @throws FileNotFoundException
     */
    public InputStream asReadStream() throws FileNotFoundException {
        return new FileInputStream(file);
    }

    /**
     * Serialize a Rollup Event and append it to the file.
     *
     * @param rollup The rollup to append.
     * @throws IOException
     */
    public void append(RollupEvent rollup) throws IOException {
        ensureOpen();
        outputStream.write(serializer.toBytes(rollup));
        outputStream.write('\n');
        outputStream.flush();
    }

    public void close() throws IOException {
        if (outputStream != null) {
            outputStream.close();
            outputStream = null;
        }
    }

    public void delete() throws FileNotFoundException, IllegalAccessException {
        if (!file.delete()) {
            if (!file.exists()) {
                throw new FileNotFoundException("File does not exist");
            }
            throw new IllegalAccessException("File exists but could not be deleted. File is probably locked.");
        }
    }

    public int compareTo(Object other) {
        return new Long(getTimestamp()).compareTo(((RollupFile) other).getTimestamp());
    }


    private void ensureOpen() throws FileNotFoundException {
        if (outputStream == null) {
            log.info("opening buffer file for writing: {}", file.getName());
            outputStream = new FileOutputStream(file, true);
        }
    }

    /**
     * Parse the timestamp from a filename.
     *
     * @param fileName The file name to parse.
     * @return The timestamp contained in the file name.
     * @throws NumberFormatException
     */
    private static long parseTimestamp(String fileName) throws NumberFormatException {
        String numberPart = fileName.substring(0, fileName.length() - 5);
        return Long.parseLong(numberPart);
    }


    /**
     * Test whether a File looks like a RollupFile.
     *
     * @param file The file to test.
     * @return Whether the file looks like a RollupFile.
     */
    public static boolean isRollupFile(File file) {
        String fileName = file.getName();

        if (!file.isFile()) {
            log.info("skipping non-file: {}", fileName);
        }

        return true;
    }


    /**
     * Build a new RollupFile using the current time as the timestamp. The file won't be created until it is actually
     * written to for the first time.
     *
     * @param bufferDir The directory in which to create the file.
     * @return The new RollupFile.
     */
    public static RollupFile buildRollupFile(File bufferDir) {
        return new RollupFile(new File(bufferDir, System.nanoTime() + ".json"));
    }

    private static class RollupFileFilter implements FileFilter {
        public boolean accept(File f) {
            String fileName = f.getName();

            if (!f.isFile()) {
                return false;
            }

            if (!fileName.endsWith(".json")) {
                log.info("skipping non-JSON file: {}", fileName);
                return false;
            }

            try {
                parseTimestamp(fileName);
            } catch (NumberFormatException e) {
                log.info("skipping malformatted filename: {}", fileName);
                return false;
            }
            return true;
        }
    }
}

@@ -1278,17 +1278,21 @@
 te long 
-t
+nanoT
 imestamp
@@ -1288,24 +1288,42 @@
 noTimestamp;
+ // in nanoseconds
 %0A    private
@@ -1534,17 +1534,21 @@
    this.
-t
+nanoT
 imestamp
@@ -1613,25 +1613,29 @@
 etrieve the 
-t
+nanoT
 imestamp ass
@@ -1676,33 +1676,37 @@
   * @return The 
-t
+nanoT
 imestamp associa
@@ -1751,16 +1751,20 @@
 long get
+Nano
 Timestam
@@ -1784,17 +1784,21 @@
  return 
-t
+nanoT
 imestamp
@@ -2166,17 +2166,21 @@
 il.Date(
-t
+nanoT
 imestamp
@@ -2606,17 +2606,21 @@
 ime() - 
-t
+nanoT
 imestamp
@@ -2627,16 +2627,19 @@
 ) / 1000
+000
 ;%0A    %7D%0A
@@ -4170,16 +4170,20 @@
 Long(get
+Nano
 Timestam
@@ -4221,16 +4221,20 @@
 her).get
+Nano
 Timestam
@@ -4520,25 +4520,29 @@
 * Parse the 
-t
+nanoT
 imestamp fro
@@ -4628,17 +4628,21 @@
 urn The 
-t
+nanoT
 imestamp
@@ -5386,17 +5386,21 @@
  as the 
-t
+nanoT
 imestamp

rename timestamp to nanoTimestamp. fix math on converting ns to ms