/*
 * Copyright 2017 - 2020 Acosix GmbH
 *
 * 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 de.acosix.alfresco.simplecontentstores.repo.store.file;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;

import org.alfresco.repo.content.AbstractContentWriter;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.content.filestore.FileContentStore;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Instances of this class provide the ability to write content to regular files in a file system.
 *
 * This class duplicates {@link org.alfresco.repo.content.filestore.FileContentWriter} simply due to issues with package method visibility
 * that prevent re-use.
 *
 * @author Axel Faust
 */
public class FileContentWriterImpl extends AbstractContentWriter
{

    private static final Logger LOGGER = LoggerFactory.getLogger(FileContentWriterImpl.class);

    protected final File file;

    protected boolean allowRandomAccess;

    /**
     * Constructor that builds a URL based on the absolute path of the file.
     *
     * @param file
     *            the file for writing. This will most likely be directly
     *            related to the content URL.
     */
    public FileContentWriterImpl(final File file)
    {
        this(file, null);
    }

    /**
     * Constructor that builds a URL based on the absolute path of the file.
     *
     * @param file
     *            the file for writing. This will most likely be directly
     *            related to the content URL.
     * @param existingContentReader
     *            a reader of a previous version of this content
     */
    public FileContentWriterImpl(final File file, final ContentReader existingContentReader)
    {
        this(file, FileContentStore.STORE_PROTOCOL + ContentStore.PROTOCOL_DELIMITER + file.getAbsolutePath(), existingContentReader);
    }

    /**
     * Constructor that explicitly sets the URL that the reader represents.
     *
     * @param file
     *            the file for writing. This will most likely be directly
     *            related to the content URL.
     * @param url
     *            the relative url that the reader represents
     * @param existingContentReader
     *            a reader of a previous version of this content
     */
    public FileContentWriterImpl(final File file, final String url, final ContentReader existingContentReader)
    {
        super(url, existingContentReader);

        this.file = file;
        this.allowRandomAccess = true;
    }

    /**
     * @return Returns the file that this writer accesses
     */
    public File getFile()
    {
        return this.file;
    }

    /**
     * @return Returns the size of the underlying file or
     */
    @Override
    public long getSize()
    {
        if (this.file == null)
        {
            return 0L;
        }
        else if (!this.file.exists())
        {
            return 0L;
        }
        else
        {
            return this.file.length();
        }
    }

    /**
     * Sets the enablement flag for random access.
     *
     * @param allow
     *            {@code true} if radnom access should be enabled
     */
    public void setAllowRandomAccess(final boolean allow)
    {
        this.allowRandomAccess = allow;
    }

    /**
     *
     * {@inheritDoc}
     */
    @Override
    protected ContentReader createReader() throws ContentIOException
    {
        /*
         * The URL of the write is known from the start and this method contract states
         * that no consideration needs to be taken w.r.t. the stream state.
         */
        final FileContentReaderImpl reader = new FileContentReaderImpl(this.file, this.getContentUrl());
        reader.setAllowRandomAccess(this.allowRandomAccess);
        return reader;
    }

    /**
     *
     * {@inheritDoc}
     */
    @Override
    protected WritableByteChannel getDirectWritableChannel() throws ContentIOException
    {
        try
        {
            if (this.file.exists() && this.file.length() > 0)
            {
                throw new IOException("File exists - overwriting not allowed");
            }

            WritableByteChannel channel = null;
            if (this.allowRandomAccess)
            {
                @SuppressWarnings("resource")
                final RandomAccessFile randomAccessFile = new RandomAccessFile(this.file, "rw");  // will create it
                channel = randomAccessFile.getChannel();
            }
            else
            {
                final OutputStream os = new FileOutputStream(this.file);
                channel = Channels.newChannel(os);
            }

            LOGGER.debug("Opened write channel to file: \n\tfile: {}\n\trandom-access: {}", this.file, this.allowRandomAccess);
            return channel;
        }
        catch (final Throwable e)
        {
            throw new ContentIOException("Failed to open file channel: " + this, e);
        }
    }
}