/*
 * Copyright 2008 ETH Zuerich, CISD
 *
 * 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 ch.systemsx.cisd.common.filesystem;

import java.io.File;

import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
import ch.systemsx.cisd.base.unix.Unix;
import ch.systemsx.cisd.common.TimingParameters;
import ch.systemsx.cisd.common.concurrent.MonitoringProxy;
import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException;
import ch.systemsx.cisd.common.exceptions.Status;

/**
 * A {@link IFileImmutableCopier} that uses a native method to create hard links.
 * 
 * @author Bernd Rinn
 */
public class FastHardLinkMaker implements IFileImmutableCopier
{

    private final static IFileImmutableCopier nativeCopier = new IFileImmutableCopier()
        {
            public Status copyFileImmutably(File source, File destinationDirectory,
                    String nameOrNull)
            {
                return copyFileImmutably(source, destinationDirectory, nameOrNull,
                        CopyModeExisting.ERROR);
            }

            public Status copyFileImmutably(File source, File destinationDirectory,
                    String nameOrNull, CopyModeExisting mode)
            {
                final File destination =
                        new File(destinationDirectory, (nameOrNull == null) ? source.getName()
                                : nameOrNull);
                if (destination.exists())
                {
                    switch (mode)
                    {
                        case OVERWRITE:
                            destination.delete();
                            break;
                        case IGNORE:
                            return Status.OK;
                        default:
                            return Status.createError("File '" + destination + "' already exists.");
                    }
                }
                try
                {
                    Unix.createHardLink(source.getAbsolutePath(), destination.getAbsolutePath());
                    return Status.OK;
                } catch (IOExceptionUnchecked ex)
                {
                    final String errorMsg = ex.getCause().getMessage();
                    if (errorMsg.endsWith("Operation not supported"))
                    {
                        try
                        {
                            FileUtilities.copyFileTo(source, destination, true);
                            return Status.OK;
                        } catch (EnvironmentFailureException ex2)
                        {
                            return Status.createError(ex2.getMessage());
                        }
                    }
                    return Status.createError(errorMsg);
                }
            }
        };

    /**
     * Returns <code>true</code>, if the native library could be initialized successfully and thus
     * this class is operational, or <code>false</code> otherwise.
     */
    public final static boolean isOperational()
    {
        return Unix.isOperational();
    }

    /**
     * Creates an {@link IFileImmutableCopier}.
     * 
     * @param timingParameters The timing parameters used to monitor and potentially retry the hard
     *            link creation.
     * @return The copier, if the native library could be initialized successfully, or
     *         <code>null</code> otherwise.
     */
    public final static IFileImmutableCopier tryCreate(final TimingParameters timingParameters)
    {
        if (Unix.isOperational() == false)
        {
            return null;
        }
        return new FastHardLinkMaker(timingParameters);

    }

    /**
     * Creates an {@link IFileImmutableCopier} with default timing parameters (uses
     * {@link TimingParameters#getDefaultParameters()}.
     * 
     * @return The copier, if the native library could be initialized successfully, or
     *         <code>null</code> otherwise.
     */
    public final static IFileImmutableCopier tryCreate()
    {
        return tryCreate(TimingParameters.getDefaultParameters());
    }

    private final IFileImmutableCopier monitoringProxy;

    private FastHardLinkMaker(final TimingParameters timingParameters)
    {
        monitoringProxy =
                MonitoringProxy.create(IFileImmutableCopier.class, nativeCopier)
                        .timing(timingParameters).get();
    }

    public Status copyFileImmutably(final File source, final File destinationDirectory,
            final String nameOrNull)
    {
        return monitoringProxy.copyFileImmutably(source, destinationDirectory, nameOrNull);
    }

    public Status copyFileImmutably(File source, File destinationDirectory, String nameOrNull,
            CopyModeExisting mode)
    {
        return monitoringProxy.copyFileImmutably(source, destinationDirectory, nameOrNull, mode);
    }

}