/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/

/**
 * @author Tom Gaskins
 * @version $Id: WWIO.java 1832 2007-05-11 20:52:38Z tgaskins $
 */
package gov.nasa.worldwind;

import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.io.*;
import java.util.zip.*;

public class WWIO
{
    public static boolean saveBuffer(ByteBuffer buffer, File file) throws IOException
    {
        if (buffer == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.BufferNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        if (file == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.FileIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        FileOutputStream fos = null;
        FileChannel channel = null;
        FileLock lock;
        int numBytesWritten = 0;
        try
        {
            fos = new FileOutputStream(file);
            channel = fos.getChannel();
            lock = channel.tryLock();
            if (lock == null)
            {
                // The file is being written to, or some other process is keeping it to itself.
                // This is an okay condition, but worth noting.
                String message = WorldWind.retrieveErrMsg("WWIO.UnableToAcquireLockFor") + file.getPath();
                WorldWind.logger().log(java.util.logging.Level.FINER, message);
                return false;
            }

            for (buffer.rewind(); buffer.hasRemaining();)
            {
                numBytesWritten += channel.write(buffer);
            }

            return true;
        }
        catch (IOException e)
        {
            String message = WorldWind.retrieveErrMsg("WWIO.ErrorSavingBufferTo") + file.getPath();
            WorldWind.logger().log(java.util.logging.Level.FINE, message, e);

            if (numBytesWritten > 0) // don't leave behind incomplete files
                file.delete();

            throw e;
        }
        finally
        {
            try
            {
                if (channel != null)
                    channel.close(); // also releases the lock
                else if (fos != null)
                    fos.close();
            }
            catch (java.io.IOException e)
            {
                String message = WorldWind.retrieveErrMsg("WWIO.ErrorTryingToClose") + file.getPath();
                WorldWind.logger().log(java.util.logging.Level.FINE, message);
            }
        }
    }

    public static MappedByteBuffer mapFile(File file) throws IOException
    {
        if (file == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.FileIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        FileInputStream is = new FileInputStream(file);
        try
        {
            return is.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
        }
        finally
        {
            is.close();
        }
    }

    public static ByteBuffer readURLContentToBuffer(URL url) throws IOException
    {
        if (url == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.URLIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        InputStream is = url.openStream();
        ByteBuffer buffer;
        try
        {
            URLConnection connection = url.openConnection();
            int size = connection.getContentLength();
            buffer = ByteBuffer.allocateDirect(size);
            byte inputBuffer[] = new byte[size];
            int n;
            do
            {
                n = is.read(inputBuffer);
                if (n > 0)
                    buffer.put(inputBuffer, 0, n);
            } while (buffer.hasRemaining() && n >= 0);
            buffer.flip();
            buffer.rewind();
        }
        finally
        {
            if (is != null)
            {
                is.close();
            }
        }

        return buffer;
    }

    public static ByteBuffer readFileToBuffer(File file) throws IOException
    {
        if (file == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.FileIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        FileInputStream is = new FileInputStream(file);
        try
        {
            FileChannel fc = is.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate((int) fc.size());
            for (int count = 0; count >= 0 && buffer.hasRemaining();)
            {
                count = fc.read(buffer);
            }
            buffer.flip();
            return buffer;
        }
        finally
        {
            is.close();
        }
    }

    public static ByteBuffer readZipEntryToBuffer(File zipFile, String entryName) throws IOException
    {
        if (zipFile == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.FileIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        InputStream is = null;
        ZipEntry ze;
        try
        {
            ZipFile zf = new ZipFile(zipFile);
            if (zf.size() < 1)
            {
                String message = WorldWind.retrieveErrMsg("WWIO.ZipFileIsEmpty") + zipFile.getPath();
                WorldWind.logger().log(java.util.logging.Level.FINE, message);
                throw new java.io.IOException(message);
            }

            if (entryName != null)
            {   // Read the specified entry
                ze = zf.getEntry(entryName);
                if (ze == null)
                {
                    String message = WorldWind.retrieveErrMsg("WWIO.ZipFileEntryNIF") + entryName
                        + WorldWind.retrieveErrMsg("punctuation.space") + zipFile.getPath();
                    WorldWind.logger().log(java.util.logging.Level.FINE, message);
                    throw new IOException(message);
                }
            }
            else
            {   // Read the first entry
                ze = zf.entries().nextElement(); // get the first entry
            }

            is = zf.getInputStream(ze);
            ByteBuffer buffer = null;
            if (ze.getSize() > 0)
            {
                buffer = transferStreamToByteBuffer(is, (int) ze.getSize());
                buffer.flip();
            }
            return buffer;
        }
        finally
        {
            if (is != null)
                is.close();
        }
    }

    private static ByteBuffer transferStreamToByteBuffer(InputStream stream, int numBytes) throws IOException
    {
        if (stream == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.InputStreamIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        if (numBytes < 1)
        {
            String message = WorldWind.retrieveErrMsg("WWIO.NumberBytesTransferLessThanOne");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        int bytesRead = 0;
        int count = 0;
        byte[] bytes = new byte[numBytes];
        while (count >= 0 && (numBytes - bytesRead) > 0)
        {
            count = stream.read(bytes, bytesRead, numBytes - bytesRead);
            if (count > 0)
            {
                bytesRead += count;
            }
        }
        return ByteBuffer.wrap(bytes);
    }

    public static String levelRowColumnPath(StringBuffer prefix, String level, int row, int column, String suffix)
    {
        StringBuffer sb = prefix;
        if (sb == null)
            sb = new StringBuffer();

        if (level != null)
            sb.append(level);
        String rowString = "/" + Integer.toString(row);
        sb.append(rowString);//sb.append(f.format(row));
        sb.append(rowString);//sb.append(f.format(row));
        sb.append("_");
        sb.append(Integer.toString(column));//sb.append(f.format(column));
        if (suffix != null)
        {
            if (!suffix.startsWith("."))
                sb.append(".");
            sb.append(suffix);
        }

        return sb.toString();
    }

    public static String replaceSuffix(String in, String newSuffix)
    {
        if (in == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.InputFileNameIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        return in.substring(0, in.lastIndexOf(".")) + (newSuffix != null ? newSuffix : "");
    }

    public static File saveBufferToTempFile(ByteBuffer buffer, String suffix) throws IOException
    {
        if (buffer == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.ByteBufferIsNull");
            gov.nasa.worldwind.WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        File outputFile = java.io.File.createTempFile("WorldWind", suffix != null ? suffix : "");
        outputFile.deleteOnExit();
        buffer.rewind();
        WWIO.saveBuffer(buffer, outputFile);

        return outputFile;
    }

    public static String getSuffixForMimeType(String mimeType)
    {
        if (mimeType == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.MimeTypeIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }

        if (mimeType.equalsIgnoreCase("image/jpeg") || mimeType.equalsIgnoreCase("image/jpg"))
            return ".jpg";
        if (mimeType.equalsIgnoreCase("image/png"))
            return ".png";

        return null;
    }

    public static boolean isFileOutOfDate(URL url, long expiryTime)
    {
        if (url == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.URLIsNull");
            gov.nasa.worldwind.WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        try
        {
            // Determine whether the file can be treated like a File, e.g., a jar entry.
            URI uri = url.toURI();
            if (uri.isOpaque())
                return false; // TODO: Determine how to check the date of non-Files

            File file = new File(uri);

            return file.exists() && file.lastModified() < expiryTime;
        }
        catch (URISyntaxException e)
        {
            String message = WorldWind.retrieveErrMsg("WWIO.ExceptionValidatingFileExpiration");
            WorldWind.logger().log(java.util.logging.Level.FINE, message + url);
            return false;
        }
    }
}
