/*
 * 
 * Copyright 2002-2005 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.Vector;
import org.apache.tools.ant.BuildException;

import net.sf.antcontrib.cpptasks.CUtil;
import net.sf.antcontrib.cpptasks.compiler.CommandLineLinker;
import net.sf.antcontrib.cpptasks.compiler.LinkType;
import net.sf.antcontrib.cpptasks.compiler.Linker;
import net.sf.antcontrib.cpptasks.CCTask;
import net.sf.antcontrib.cpptasks.compiler.CommandLineLinkerConfiguration;
import net.sf.antcontrib.cpptasks.types.LibraryTypeEnum;

/**
 * Adapter for the Borland(r) tlib Librarian
 * 
 * @author Curt Arnold
 */
public class BorlandLibrarian extends CommandLineLinker {
    private static final BorlandLibrarian instance = new BorlandLibrarian();
    public static BorlandLibrarian getInstance() {
        return instance;
    }
    private BorlandLibrarian() {
        super("tlib", "--version", new String[]{".obj"}, new String[0], ".lib", false,
                null);
    }
    protected void addBase(long base, Vector args) {
    }
    protected void addFixed(Boolean fixed, Vector args) {
    }
    protected void addImpliedArgs(boolean debug, LinkType linkType, Vector args) {
    }
    protected void addIncremental(boolean incremental, Vector args) {
    }
    protected void addMap(boolean map, Vector args) {
    }
    protected void addStack(int stack, Vector args) {
    }
    /* (non-Javadoc)
     * @see net.sf.antcontrib.cpptasks.compiler.CommandLineLinker#addEntry(int, java.util.Vector)
     */
    protected void addEntry(String entry, Vector args) {
    }
    
    protected String getCommandFileSwitch(String cmdFile) {
        //
        //  tlib requires quotes around paths containing -
        //     ilink32 doesn't like them
        StringBuffer buf = new StringBuffer("@");
        BorlandProcessor.quoteFile(buf, cmdFile);
        return buf.toString();
    }
    public File[] getLibraryPath() {
        return CUtil.getPathFromEnvironment("LIB", ";");
    }
    public String[] getLibraryPatterns(String[] libnames, LibraryTypeEnum libType) {
        return BorlandProcessor.getLibraryPatterns(libnames, libType);
    }
    public Linker getLinker(LinkType type) {
        return BorlandLinker.getInstance().getLinker(type);
    }
    public int getMaximumCommandLength() {
        return 1024;
    }
    public String[] getOutputFileSwitch(String outFile) {
        return BorlandProcessor.getOutputFileSwitch(outFile);
    }
    public boolean isCaseSensitive() {
        return BorlandProcessor.isCaseSensitive();
    }
    /**
     * Gets identifier for the linker.
     * 
     * TLIB will lockup when attempting to get version
     * information.  Since the Librarian version isn't critical
     * just return a stock response.
     */
    public String getIdentifier() {
    	return "TLIB 4.5 Copyright (c) 1987, 1999 Inprise Corporation";
    }
    
    /**
     * 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();
        StringBuffer buf = new StringBuffer();
        Vector execArgs = new Vector(preargs.length + endargs.length + 10
                + sourceFiles.length);
        
        execArgs.addElement(this.getCommand());
        String outputFileName = new File(outputDir, outputName).toString();
        execArgs.addElement(quoteFilename(buf, outputFileName));

        for (int i = 0; i < preargs.length; i++) {
            execArgs.addElement(preargs[i]);
        }

        //
        //   add a place-holder for page size
        //
        int pageSizeIndex = execArgs.size();
        execArgs.addElement(null);
        
        int objBytes = 0;
        
        for (int i = 0; i < sourceFiles.length; i++) {
            String last4 = sourceFiles[i]
                    .substring(sourceFiles[i].length() - 4).toLowerCase();
            if (last4.equals(".def")) {
            } else {
                if (last4.equals(".res")) {
                } else {
                    if (last4.equals(".lib")) {
                    } else {
                        execArgs.addElement("+" + quoteFilename(buf, sourceFiles[i]));
                        objBytes += new File(sourceFiles[i]).length();
                    }
                }
            }
        }
        
        for (int i = 0; i < endargs.length; i++) {
            execArgs.addElement(endargs[i]);
        }
        
        String[] execArguments = new String[execArgs.size()];
        execArgs.copyInto(execArguments);

    	int minPageSize = objBytes >> 16;
    	int pageSize = 0;
    	for(int i = 4; i <= 15; i++) {
    		pageSize = 1 << i;
    		if (pageSize > minPageSize) break;
    	}
        execArguments[pageSizeIndex] = "/P" + Integer.toString(pageSize);
        
        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;
    }
    
    /**
     * Builds a library
     *
     */
    public void link(CCTask task,
                     File outputFile,
                     String[] sourceFiles,
                     CommandLineLinkerConfiguration config)
                     throws BuildException
    {
    	//
    	//  delete any existing library
    	outputFile.delete();
    	//
    	//  build a new library
    	super.link(task, outputFile, sourceFiles, config);
    }
    
    /**
     *   Encloses problematic file names within quotes.
     *   @param buf string buffer
     *   @param filename source file name
     *   @return filename potentially enclosed in quotes.
     */
    protected String quoteFilename(StringBuffer buf,String filename) {
      buf.setLength(0);
      BorlandProcessor.quoteFile(buf, filename);
      return buf.toString();
    }

}
