/*******************************************************************************
 * Copyright (c) 2016 École Polytechnique de Montréal
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License 2.0 which
 * accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *******************************************************************************/

package org.eclipse.tracecompass.internal.statesystem.core.backend.historytree;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.tracecompass.internal.statesystem.core.backend.historytree.classic.HistoryTreeClassic;

/**
 * Class that contains factory methods to build different types of history trees
 *
 * @author Geneviève Bastien
 */
@NonNullByDefault
public final class HistoryTreeFactory {

    private HistoryTreeFactory() {
    }

    /**
     * Create a new State History from scratch, using a {@link HTConfig} object
     * for configuration.
     *
     * @param conf
     *            The config to use for this History Tree.
     * @return the new state history tree
     * @throws IOException
     *             If an error happens trying to open/write to the file
     *             specified in the config
     */
    public static IHistoryTree createHistoryTree(HTConfig conf) throws IOException {
        return new HistoryTreeClassic(conf);
    }

    /**
     * "Reader" factory : instantiate a SHTree from an existing tree file on
     * disk
     *
     * @param existingStateFile
     *            Path/filename of the history-file we are to open
     * @param expectedProviderVersion
     *            The expected version of the state provider
     * @return The history tree
     * @throws IOException
     *             If an error happens reading the file
     */
    public static IHistoryTree createFromFile(Path existingStateFile, int expectedProviderVersion) throws IOException {
        /*
         * Check the file exists and has a positive length. These verifications
         * will also be done in the HT's constructor.
         */
        if (!Files.isReadable(existingStateFile)) {
            throw new IOException("Selected state file does not exist or is not readable."); //$NON-NLS-1$
        }
        if (Files.size(existingStateFile) <= 0) {
            throw new IOException("Empty target file"); //$NON-NLS-1$
        }

        /* Read the magic number from the history tree file */
        ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
        try (ReadableByteChannel channel = Files.newByteChannel(existingStateFile, StandardOpenOption.READ);) {
            buffer.order(ByteOrder.LITTLE_ENDIAN);
            buffer.clear();
            channel.read(buffer);
            buffer.flip();
        }

        /*
         * Check the magic number to see which history tree class to create
         */
        int magicNumber = buffer.getInt();
        switch (magicNumber) {
        case HistoryTreeClassic.HISTORY_FILE_MAGIC_NUMBER:
            return new HistoryTreeClassic(existingStateFile.toFile(), expectedProviderVersion);
        default:
            throw new IOException("Not a known history tree file"); //$NON-NLS-1$
        }
    }
}
