/*
 * Copyright (c) 2020, Oracle and/or its affiliates.
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
 * endorse or promote products derived from this software without specific prior written
 * permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.oracle.truffle.llvm.parser.factories;

import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.llvm.runtime.LLVMContext;
import com.oracle.truffle.llvm.runtime.LLVMSyscallEntry;
import com.oracle.truffle.llvm.runtime.PlatformCapability;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public abstract class PlatformCapabilityBase<S extends Enum<S> & LLVMSyscallEntry> extends PlatformCapability<S> {
    public static final String LIBCXXABI_PREFIX = "libc++abi.";
    public static final String LIBCXX_PREFIX = "libc++.";
    protected final boolean loadCxxLibraries;

    public PlatformCapabilityBase(Class<S> cls, boolean loadCxxLibraries) {
        super(cls);
        this.loadCxxLibraries = loadCxxLibraries;
    }

    @Override
    public String[] getSulongDefaultLibraries() {
        if (loadCxxLibraries) {
            return new String[]{getLibsulongFilename(), getLibsulongxxFilename()};
        } else {
            return new String[]{getLibsulongFilename()};
        }
    }

    public abstract String getLibsulongxxFilename();

    public abstract String getLibsulongFilename();

    @Override
    public List<String> preprocessDependencies(LLVMContext ctx, TruffleFile file, List<String> dependencies) {
        List<String> newDeps = null;
        boolean libSulongXXAdded = false;
        // inject libsulong++ dependency
        if (file != null && ctx.isInternalLibraryFile(file)) {
            Path path = Paths.get(file.getPath());
            String remainder = ctx.getInternalLibraryPath().relativize(path).toString();
            if (remainder.startsWith(LIBCXXABI_PREFIX) || remainder.startsWith(LIBCXX_PREFIX)) {
                newDeps = new ArrayList<>(dependencies);
                newDeps.add(getLibsulongxxFilename());
                libSulongXXAdded = true;
            }
        }

        // replace absolute dependencies to libc++* to relative ones (in the llvm home)
        for (int i = 0; i < dependencies.size(); i++) {
            String dep = dependencies.get(i);
            if (dep.startsWith("/usr/lib/libc++")) {
                Path namePath = Paths.get(dep).getFileName();
                if (namePath != null) {
                    String filename = namePath.toString();
                    if (filename.startsWith("libc++.") || filename.startsWith("libc++abi.")) {
                        if (newDeps == null) {
                            newDeps = new ArrayList<>(dependencies);
                        }
                        // replace with file name
                        newDeps.set(i, filename);
                        dep = filename;
                    }
                }
            }
            if (!libSulongXXAdded && (dep.startsWith(LIBCXXABI_PREFIX) || dep.startsWith(LIBCXX_PREFIX))) {
                // inject libsulong++ dependency
                if (newDeps == null) {
                    newDeps = new ArrayList<>(dependencies);
                }
                newDeps.add(getLibsulongxxFilename());
                libSulongXXAdded = true;
            }
        }
        if (newDeps != null) {
            return newDeps;
        }
        return dependencies;
    }
}