/*
 * 
 * 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.gcc;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Vector;

import net.sf.antcontrib.cpptasks.CUtil;
import net.sf.antcontrib.cpptasks.compiler.CaptureStreamHandler;
/**
 * A add-in class for Gcc processors
 * 
 *  
 */
public class GccProcessor {
    //   the results from gcc -dumpmachine
    private static String machine;
    private static String[] specs;
    //   the results from gcc -dumpversion
    private static String version;
    private static int addLibraryPatterns(String[] libnames, StringBuffer buf,
            String prefix, String extension, String[] patterns, int offset) {
        for (int i = 0; i < libnames.length; i++) {
            buf.setLength(0);
            buf.append(prefix);
            buf.append(libnames[i]);
            buf.append(extension);
            patterns[offset + i] = buf.toString();
        }
        return offset + libnames.length;
    }
    /**
     * Converts absolute Cygwin file or directory names to the corresponding
     * Win32 name.
     * 
     * @param names
     *            array of names, some elements may be null, will be changed in
     *            place.
     */
    public static void convertCygwinFilenames(String[] names) {
        if (names == null) {
            throw new NullPointerException("names");
        }
        File gccDir = CUtil.getExecutableLocation("gcc.exe");
        if (gccDir != null) {
            String prefix = gccDir.getAbsolutePath() + "/..";
            StringBuffer buf = new StringBuffer();
            for (int i = 0; i < names.length; i++) {
                String name = names[i];
                if (name != null && name.length() > 1 && name.charAt(0) == '/') {
                    buf.setLength(0);
                    buf.append(prefix);
                    buf.append(name);
                    names[i] = buf.toString();
                }
            }
        }
    }
    public static String[] getLibraryPatterns(String[] libnames) {
        StringBuffer buf = new StringBuffer();
        String[] patterns = new String[libnames.length * 2];
        int offset = addLibraryPatterns(libnames, buf, "lib", ".a", patterns, 0);
        if (isHPUX()) {
            offset = addLibraryPatterns(libnames, buf, "lib", ".sl", patterns,
                    offset);
        } else {
            offset = addLibraryPatterns(libnames, buf, "lib", ".so", patterns,
                    offset);
        }
        return patterns;
    }
    public static String getMachine() {
        if (machine == null) {
            String[] args = new String[]{"gcc", "-dumpmachine"};
            String[] cmdout = CaptureStreamHandler.run(args);
            if (cmdout.length == 0) {
                machine = "nomachine";
            } else {
                machine = cmdout[0];
            }
        }
        return machine;
    }
    public static String[] getOutputFileSwitch(String letter, String outputFile) {
        StringBuffer buf = new StringBuffer();
        if (outputFile.indexOf(' ') >= 0) {
            buf.append('"');
            buf.append(outputFile.replace('\\', '/'));
            buf.append('"');
        } else {
            buf.append(outputFile.replace('\\', '/'));
        }
        String[] retval = new String[]{letter, buf.toString()};
        return retval;
    }
    /**
     * Returns the contents of the gcc specs file.
     * 
     * The implementation locates gcc.exe in the executable path and then
     * builds a relative path name from the results of -dumpmachine and
     * -dumpversion. Attempts to use gcc -dumpspecs to provide this information
     * resulted in stalling on the Execute.run
     * 
     * @return contents of the specs file
     */
    public static String[] getSpecs() {
        if (specs == null) {
            File gccParent = CUtil.getExecutableLocation("gcc.exe");
            if (gccParent != null) {
                //
                //  build a relative path like
                //    ../lib/gcc-lib/i686-pc-cygwin/2.95.3-5/specs
                //
                StringBuffer buf = new StringBuffer("../lib/gcc-lib/");
                buf.append(getMachine());
                buf.append('/');
                buf.append(getVersion());
                buf.append("/specs");
                //
                //  resolve it relative to the location of gcc.exe
                //
                String relativePath = buf.toString();
                File specsFile = new File(gccParent, relativePath);
                //
                //  found the specs file
                //
                try {
                    //
                    //  read the lines in the file
                    //
                    BufferedReader reader = new BufferedReader(new FileReader(
                            specsFile));
                    Vector lines = new Vector(100);
                    String line = reader.readLine();
                    while (line != null) {
                        lines.addElement(line);
                        line = reader.readLine();
                    }
                    specs = new String[lines.size()];
                    lines.copyInto(specs);
                } catch (IOException ex) {
                }
            }
        }
        if (specs == null) {
            specs = new String[0];
        }
        return specs;
    }
    public static String getVersion() {
        if (version == null) {
            String[] args = new String[]{"gcc", "-dumpversion"};
            String[] cmdout = CaptureStreamHandler.run(args);
            if (cmdout.length == 0) {
                version = "noversion";
            } else {
                version = cmdout[0];
            }
        }
        return version;
    }
    public static boolean isCaseSensitive() {
        return true;
    }
    /**
     * Determines if task is running with cygwin
     * 
     * @return true if cygwin was detected
     */
    public static boolean isCygwin() {
        return getMachine().indexOf("cygwin") > 0;
    }
    private static boolean isHPUX() {
        String osname = System.getProperty("os.name").toLowerCase();
        if (osname.indexOf("hp") >= 0 && osname.indexOf("ux") >= 0) {
            return true;
        }
        return false;
    }
    /**
     * 
     * Parses the results of the specs file for a specific processor and
     * options
     * 
     * @param specsContent
     *            Contents of specs file as returned from getSpecs
     * @param specSectionStart
     *            start of spec section, for example "*cpp:"
     * @param options
     *            command line switches such as "-istart"
     */
    public static String[][] parseSpecs(String[] specsContent,
            String specSectionStart, String[] options) {
        if (specsContent == null) {
            throw new NullPointerException("specsContent");
        }
        if (specSectionStart == null) {
            throw new NullPointerException("specSectionStart");
        }
        if (options == null) {
            throw new NullPointerException("option");
        }
        String[][] optionValues = new String[options.length][];
        StringBuffer optionValue = new StringBuffer(40);
        for (int i = 0; i < specsContent.length; i++) {
            String specLine = specsContent[i];
            //
            //   if start of section then start paying attention
            //
            if (specLine.startsWith(specSectionStart)) {
                Vector[] optionVectors = new Vector[options.length];
                for (int j = 0; j < options.length; j++) {
                    optionVectors[j] = new Vector(10);
                }
                //
                //  go to next line and examine contents
                //     and repeat until end of file
                //
                for (i++; i < specsContent.length; i++) {
                    specLine = specsContent[i];
                    for (int j = 0; j < options.length; j++) {
                        int optionStart = specLine.indexOf(options[j]);
                        while (optionStart >= 0) {
                            optionValue.setLength(0);
                            //
                            //   walk rest of line looking for first non
                            // whitespace
                            //       and then next space
                            boolean hasNonBlank = false;
                            int k = optionStart + options[j].length();
                            for (; k < specLine.length(); k++) {
                                //
                                //  either a blank or a "}" (close of
                                // conditional)
                                //    section will end the path
                                //
                                if (specLine.charAt(k) == ' '
                                        || specLine.charAt(k) == '}') {
                                    if (hasNonBlank) {
                                        break;
                                    }
                                } else {
                                    hasNonBlank = true;
                                    optionValue.append(specLine.charAt(k));
                                }
                            }
                            //
                            //  transition back to whitespace
                            //     value is over, add it to vector
                            if (hasNonBlank) {
                                optionVectors[j].addElement(optionValue
                                        .toString());
                            }
                            //
                            //  find next occurance on line
                            //
                            optionStart = specLine.indexOf(options[j], k);
                        }
                    }
                }
                //
                //   copy vectors over to option arrays
                //
                for (int j = 0; j < options.length; j++) {
                    optionValues[j] = new String[optionVectors[j].size()];
                    optionVectors[j].copyInto(optionValues[j]);
                }
            }
        }
        //
        //   fill in any missing option values with
        //      a zero-length string array
        for (int i = 0; i < optionValues.length; i++) {
            String[] zeroLenArray = new String[0];
            if (optionValues[i] == null) {
                optionValues[i] = zeroLenArray;
            }
        }
        return optionValues;
    }
    private GccProcessor() {
    }
}
