/*
 *
 * Copyright 2002-2004 The Ant-Contrib project
 *
 *  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 net.sf.antcontrib.cpptasks.borland;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import net.sf.antcontrib.cpptasks.CCTask;
import net.sf.antcontrib.cpptasks.TargetMatcher;
import net.sf.antcontrib.cpptasks.VersionInfo;
import net.sf.antcontrib.cpptasks.compiler.CommandLineLinker;
import net.sf.antcontrib.cpptasks.compiler.CommandLineLinkerConfiguration;
import net.sf.antcontrib.cpptasks.compiler.LinkType;
import net.sf.antcontrib.cpptasks.compiler.Linker;
import net.sf.antcontrib.cpptasks.platforms.WindowsPlatform;
import net.sf.antcontrib.cpptasks.types.LibraryTypeEnum;

/**
 * Adapter for the Borland(r) ilink32 linker
 *
 * @author Curt Arnold
 */
public final class BorlandLinker extends CommandLineLinker {
    private static final BorlandLinker dllLinker = new BorlandLinker(".dll");
    private static final BorlandLinker instance = new BorlandLinker(".exe");
    public static BorlandLinker getInstance() {
        return instance;
    }
    private BorlandLinker(String outputSuffix) {
        super("ilink32", "-r", new String[]{".obj", ".lib", ".res"},
                new String[]{".map", ".pdb", ".lnk"}, outputSuffix, false, null);
    }
    protected void addBase(long base, Vector args) {
        if (base >= 0) {
            String baseAddr = Long.toHexString(base);
            args.addElement("-b:" + baseAddr);
        }
    }
    protected void addFixed(Boolean fixed, Vector args) {
    }
    protected void addImpliedArgs(boolean debug, LinkType linkType, Vector args) {
        if (linkType.isExecutable()) {
            if (linkType.isSubsystemConsole()) {
                args.addElement("/ap");
            } else {
                if (linkType.isSubsystemGUI()) {
                    args.addElement("/Tpe");
                }
            }
        }
        if (linkType.isSharedLibrary()) {
            args.addElement("/Tpd");
            args.addElement("/Gi");
        }
        if (debug) {
          args.addElement("-v");
        }
    }
    protected void addIncremental(boolean incremental, Vector args) {
    }
    protected void addMap(boolean map, Vector args) {
        if (!map) {
            args.addElement("-x");
        }
    }
    protected void addStack(int stack, Vector args) {
        if (stack >= 0) {
            String stackStr = Integer.toHexString(stack);
            args.addElement("-S:" + stackStr);
        }
    }
    /* (non-Javadoc)
     * @see net.sf.antcontrib.cpptasks.compiler.CommandLineLinker#addEntry(int, java.util.Vector)
     */
    protected void addEntry(String entry, Vector args) {
    }

    public String getCommandFileSwitch(String commandFile) {
        return "@" + commandFile;
    }
    public String getIdentifier() {
        return "Borland Linker";
    }
    public File[] getLibraryPath() {
        return BorlandProcessor.getEnvironmentPath("ilink32", 'L',
                new String[]{"..\\lib"});
    }
    public String[] getLibraryPatterns(String[] libnames, LibraryTypeEnum libType) {
        return BorlandProcessor.getLibraryPatterns(libnames, libType);
    }
    public Linker getLinker(LinkType type) {
        if (type.isStaticLibrary()) {
            return BorlandLibrarian.getInstance();
        }
        if (type.isSharedLibrary()) {
            return dllLinker;
        }
        return instance;
    }
    public int getMaximumCommandLength() {
        return 1024;
    }
    public String[] getOutputFileSwitch(String outFile) {
        return BorlandProcessor.getOutputFileSwitch(outFile);
    }
    protected String getStartupObject(LinkType linkType) {
        if (linkType.isSharedLibrary()) {
            return "c0d32.obj";
        }
        if (linkType.isSubsystemGUI()) {
            return "c0w32.obj";
        }
        if (linkType.isSubsystemConsole()) {
            return "c0x32.obj";
        }
        return null;
    }
    public boolean isCaseSensitive() {
        return BorlandProcessor.isCaseSensitive();
    }
    /**
     * Prepares argument list for exec command.
     *
     * @param outputDir linker output directory
     * @param outputName linker output name
     * @param sourceFiles
     *            linker input files (.obj, .o, .res)
     * @param config
     *            linker configuration
     * @return arguments for runTask
     */
    protected String[] prepareArguments(
                    CCTask task,
                    String outputDir,
                        String outputName,
            String[] sourceFiles,
                        CommandLineLinkerConfiguration config) {
        String[] preargs = config.getPreArguments();
        String[] endargs = config.getEndArguments();
        Vector execArgs = new Vector(preargs.length + endargs.length + 10
                + sourceFiles.length);
        execArgs.addElement(this.getCommand());
        for (int i = 0; i < preargs.length; i++) {
            execArgs.addElement(preargs[i]);
        }
        for (int i = 0; i < endargs.length; i++) {
            execArgs.addElement(endargs[i]);
        }
        //
        //  see if the input files have any known startup obj files
        //
        String startup = null;
        for (int i = 0; i < sourceFiles.length; i++) {
            String filename = new File(sourceFiles[i]).getName().toLowerCase();
            if (startup != null && filename.substring(0, 2).equals("c0")
                    && filename.substring(3, 5).equals("32")
                    && filename.substring(filename.length() - 4).equals(".obj")) {
                startup = sourceFiles[i];
            }
        }
        //
        //  c0w32.obj, c0x32.obj or c0d32.obj depending on
        //        link type
        if (startup == null) {
            startup = config.getStartupObject();
        }
        execArgs.addElement(startup);
        Vector resFiles = new Vector();
        Vector libFiles = new Vector();
        String defFile = null;
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < sourceFiles.length; i++) {
            String last4 = sourceFiles[i]
                    .substring(sourceFiles[i].length() - 4).toLowerCase();
            if (last4.equals(".def")) {
                defFile = quoteFilename(buf, sourceFiles[i]);
            } else {
                if (last4.equals(".res")) {
                    resFiles.addElement(quoteFilename(buf, sourceFiles[i]));
                } else {
                    if (last4.equals(".lib")) {
                        libFiles.addElement(quoteFilename(buf, sourceFiles[i]));
                    } else {
                        execArgs.addElement(quoteFilename(buf, sourceFiles[i]));
                    }
                }
            }
        }
        //
        //   output file name
        //
        String outputFileName = new File(outputDir, outputName).toString();
        execArgs.addElement("," + quoteFilename(buf, outputFileName));
        if (config.getMap()) {
            int lastPeriod = outputFileName.lastIndexOf('.');
            String mapName;
            if (lastPeriod < outputFileName.length() - 4) {
                mapName = outputFileName + ".map";
            } else {
                mapName = outputFileName.substring(0, lastPeriod) + ".map";
            }
            execArgs.addElement("," + quoteFilename(buf, mapName) + ",");
        } else {
            execArgs.addElement(",,");
        }
        //
        //   add all the libraries
        //
        Enumeration libEnum = libFiles.elements();
        boolean hasImport32 = false;
        boolean hasCw32 = false;
        while (libEnum.hasMoreElements()) {
            String libName = (String) libEnum.nextElement();
            if (libName.equalsIgnoreCase("import32.lib")) {
                    hasImport32 = true;
            }
            if (libName.equalsIgnoreCase("cw32.lib")) {
                    hasImport32 = true;
            }
            execArgs.addElement(quoteFilename(buf, libName));
        }
        if (!hasCw32) {
                execArgs.addElement(quoteFilename(buf, "cw32.lib"));
        }
        if (!hasImport32) {
                execArgs.addElement(quoteFilename(buf, "import32.lib"));
        }
        if (defFile == null) {
            execArgs.addElement(",,");
        } else {
            execArgs.addElement("," + quoteFilename(buf, defFile) + ",");
        }
        Enumeration resEnum = resFiles.elements();
        while (resEnum.hasMoreElements()) {
            String resName = (String) resEnum.nextElement();
            execArgs.addElement(quoteFilename(buf, resName));
        }
        String[] execArguments = new String[execArgs.size()];
        execArgs.copyInto(execArguments);
        return execArguments;
    }
    /**
     * Prepares argument list to execute the linker using a response file.
     *
     * @param outputFile
     *            linker output file
     * @param args
     *            output of prepareArguments
     * @return arguments for runTask
     */
    protected String[] prepareResponseFile(File outputFile, String[] args)
            throws IOException {
            String cmdargs[] = BorlandProcessor.prepareResponseFile(outputFile, args, " + \n");
        cmdargs[cmdargs.length - 1] = getCommandFileSwitch(cmdargs[cmdargs.length -1]);
        return cmdargs;
    }

    /**
     * Adds source or object files to the bidded fileset to
     * support version information.
     *
     * @param versionInfo version information
     * @param linkType link type
     * @param isDebug true if debug build
     * @param outputFile name of generated executable
     * @param objDir directory for generated files
     * @param matcher bidded fileset
     */
        public void addVersionFiles(final VersionInfo versionInfo,
                        final LinkType linkType,
                        final File outputFile,
                        final boolean isDebug,
                        final File objDir,
                        final TargetMatcher matcher) throws IOException {
                WindowsPlatform.addVersionFiles(versionInfo, linkType, outputFile, isDebug, objDir, matcher);
        }

}
