package org.apache.maven.simian;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  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. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" and
 *    "Apache Maven" must not be used to endorse or promote products
 *    derived from this software without prior written permission. For
 *    written permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    "Apache Maven", nor may "Apache" appear in their name, without
 *    prior written permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * ====================================================================
 */

import org.apache.commons.jelly.JellyContext;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * This class represents a model of a Simian log file.
 * An instance of this class will be passed to Jelly to generate
 * an xdoc document. Finally, this xdoc file is weaved into the
 * Maven documentation, with links pointing to the JXR sources.
 *
 * @author Aslak Hellesoy
 * @version $Revision: 1.4 $
 */
public class SimianLog {
    private File logFile;
    private JellyContext jellyContext;

    private Duplication[] duplications;
    private String copyright1;
    private String copyright2;
    private int totalDuplicateLineCount;
    private int loc;
    private int fileParseCount;
    private String time;
    private int blockCount;
    private int fileWithDuplicateCount;
    private String sourceDirectory;
    private String unitTestSourceDirectory;

    public void setLogFile(File logFile) {
        this.logFile = logFile;
    }

    public void setJellyContext(JellyContext jellyContext) {
        this.jellyContext = jellyContext;
    }

    public void setSourceDirectory(String sourceDirectory) throws IOException {
        this.sourceDirectory = new File(sourceDirectory).getCanonicalPath();
    }

    public void setUnitTestSourceDirectory(String unitTestSourceDirectory) throws IOException {
        this.unitTestSourceDirectory = new File(unitTestSourceDirectory).getCanonicalPath();
    }

    public Duplication[] getDuplications() {
        return duplications;
    }

    public String getCopyright1() {
        return copyright1;
    }

    public String getCopyright2() {
        return copyright2;
    }

    private String linkify(String s) {
        StringBuffer sb = new StringBuffer();
        StringTokenizer st = new StringTokenizer(s, " ", true);
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            if(token.startsWith("http://")) {
                token = "<a href=\"" + token + "\">" + token + "</a>";
            }
            sb.append(token);
        }
        return sb.toString();
    }

    public int getTotalDuplicateLineCount() {
        return totalDuplicateLineCount;
    }

    public int getLoc() {
        return loc;
    }

    public int getFileParseCount() {
        return fileParseCount;
    }

    public String getTime() {
        return time;
    }

    public int getBlockCount() {
        return blockCount;
    }

    public int getFileWithDuplicateCount() {
        return fileWithDuplicateCount;
    }

    public void process() throws IOException {
        List duplicationList = new ArrayList();
        Duplication duplication = null;

        BufferedReader logReader = new BufferedReader( new FileReader(logFile) );
        // Skip 3 lines of header.
        copyright1 = logReader.readLine();
        copyright2 = logReader.readLine();
        String line;
        while( (line = logReader.readLine()) != null ) {
            if( line.endsWith("duplicate lines in the following files:") ) {
                // New duplication

                StringTokenizer dupeTokenizer = new StringTokenizer(line);
                // Skip "Found" token
                dupeTokenizer.nextToken();
                int dupeSize = Integer.parseInt(dupeTokenizer.nextToken());
                duplication = new Duplication(dupeSize);
                duplicationList.add(duplication);
            } else if( line.startsWith(" Between") ) {
                // New duplication area within a duplication

                StringTokenizer dupeEntryTokenizer = new StringTokenizer(line);
                // Skip "Between" and "lines" tokens.
                dupeEntryTokenizer.nextToken();
                dupeEntryTokenizer.nextToken();
                int startLine = Integer.parseInt(dupeEntryTokenizer.nextToken());
                // Skip "and".
                dupeEntryTokenizer.nextToken();
                int endLine = Integer.parseInt(dupeEntryTokenizer.nextToken());
                // Skip "in".
                dupeEntryTokenizer.nextToken();
                // filePath could have spaces, we really want all tokens
                StringBuffer filePathBuffer = new StringBuffer();
                while (dupeEntryTokenizer.hasMoreTokens()) 
                {
                    filePathBuffer.append(dupeEntryTokenizer.nextToken());
                    filePathBuffer.append(' ');
                }
                String filePath = filePathBuffer.toString();
                String pathToClass = null;
                if( filePath.startsWith(sourceDirectory) ) {
                	pathToClass = "xref/" + getPathToClass(sourceDirectory, filePath);
				} else if( filePath.startsWith(unitTestSourceDirectory) ) {
                	pathToClass = "xref-test/" + getPathToClass(unitTestSourceDirectory, filePath);
				} else {
					throw new IllegalStateException( "Couldn't find relative path for " + filePath + " in " + sourceDirectory + " or " + unitTestSourceDirectory);
				}

                DuplicationArea duplicationArea = new DuplicationArea(startLine, endLine, pathToClass,
                  getPathToClass(sourceDirectory, filePath).replace('/', '.'));
                duplication.addDuplicationArea(duplicationArea);
            } else if( line.indexOf("duplicate lines of code in") != -1 ) {
                // Summary (Line 1)
                StringTokenizer summaryTokenizer = new StringTokenizer(line);
                // Skip "Found".
                summaryTokenizer.nextToken();
                totalDuplicateLineCount = Integer.parseInt(summaryTokenizer.nextToken());
                // Skip "duplicate lines of code in".
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                blockCount = Integer.parseInt(summaryTokenizer.nextToken());
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                fileWithDuplicateCount = Integer.parseInt(summaryTokenizer.nextToken());
            } else if( line.startsWith("Processed a total of") ) {
                // Summary (Line 2)
                StringTokenizer summaryTokenizer = new StringTokenizer(line);
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                loc = Integer.parseInt(summaryTokenizer.nextToken());
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                fileParseCount = Integer.parseInt(summaryTokenizer.nextToken());
            } else if( line.startsWith("Processing time:") ) {
                StringTokenizer summaryTokenizer = new StringTokenizer(line);
                summaryTokenizer.nextToken();
                summaryTokenizer.nextToken();
                time = summaryTokenizer.nextToken();
            }
        }
        duplications = (Duplication[]) duplicationList.toArray(new Duplication[duplicationList.size()]);

        // Set ourself on the context, so Jelly can reference us as a regular bean.
        jellyContext.setVariable("simianLog", this);
    }

    private String getPathToClass(String directory, String filePath) {
        int classStart = directory.length() + 1;
        int classEnd = filePath.lastIndexOf(".java");

        String result = filePath.substring(classStart, classEnd).replace(File.separatorChar, '/');

        return result;
    }
}
