/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2012, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.patching.runner;

import static org.jboss.as.patching.IoUtils.NO_CONTENT;
import static org.jboss.as.patching.IoUtils.copy;
import static org.jboss.as.patching.IoUtils.safeClose;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import org.jboss.as.patching.IoUtils;
import org.jboss.as.patching.logging.PatchLogger;
import org.jboss.as.patching.metadata.ContentModification;
import org.jboss.as.patching.metadata.MiscContentItem;

/**
 * Base {@linkplain PatchingTask} for misc file updates.
 *
 * @author Emanuel Muckenhuber
 */
abstract class AbstractFileTask extends AbstractPatchingTask<MiscContentItem> {

    final File target; // the target file
    final File backup; // the backup file

    protected AbstractFileTask(PatchingTaskDescription description, File target, File backup) {
        super(description, MiscContentItem.class);
        this.target = target;
        this.backup = backup;
    }

    /**
     * Create the rollback item.
     *
     * @param original the original content modification
     * @param item the new misc content item
     * @param targetHash the new target hash
     * @return the rollback modification item
     */
    abstract ContentModification createRollbackEntry(ContentModification original, MiscContentItem item, byte[] targetHash);

    @Override
    byte[] backup(PatchingTaskContext context) throws IOException {
        if(target.isFile()) {
            // Backup the original in the history directory
            final byte[] backupHash = IoUtils.copy(target, backup);
            return backupHash;
        } else if (contentItem.isDirectory() && target.isDirectory()) {
            // Completely ignore the apply step if the directory already exists
            // This will basically skip the creation of the rollback item
            setIgnoreApply();
        }
        return NO_CONTENT;
    }

    @Override
    byte[] apply(PatchingTaskContext context, PatchContentLoader loader) throws IOException {
        final MiscContentItem item = contentItem;
        if(item.isDirectory()) {
            if(! target.mkdirs() && ! target.isDirectory()) {
                throw PatchLogger.ROOT_LOGGER.cannotCreateDirectory(target.getAbsolutePath());
            }
            return NO_CONTENT;
        } else {
            if (!target.exists()) {
                createParentDirectories(target, item.getPath(), item.getPath().length, context);
            }
            final InputStream is = loader.openContentStream(item);
            try {
                // Replace the file
                return copy(is, target);
            } finally {
                safeClose(is);
            }
        }
    }

    @Override
    ContentModification createRollbackEntry(ContentModification original, byte[] targetHash, byte[] itemHash) {
        final MiscContentItem item = new MiscContentItem(contentItem.getName(), contentItem.getPath(), itemHash, contentItem.isDirectory(), contentItem.isAffectsRuntime());
        return createRollbackEntry(original, item, targetHash);
    }

    static void createParentDirectories(final File target, String[] path, int depth, final PatchingTaskContext context) throws IOException {
        if (depth > 0) {
            final File parent = target.getParentFile();
            if (! parent.exists()) {
                createParentDirectories(parent, path, depth - 1, context);
            }
            if(! parent.mkdir() && ! parent.isDirectory()) {
                throw PatchLogger.ROOT_LOGGER.cannotCreateDirectory(target.getAbsolutePath());
            }
            // TODO record changes
//            final String[] newPath = Arrays.copyOf(path, depth - 1);
//            final MiscContentItem item = new MiscContentItem(parent.getName(), newPath, NO_CONTENT);
//            context.recordChange(new ContentModification(item, NO_CONTENT, ModificationType.ADD), new ContentModification(item, NO_CONTENT, ModificationType.REMOVE));
        }
    }

}
