package org.apache.maven.simian;

/* ====================================================================
 *   Copyright 2001-2004 The Apache Software Foundation.
 *
 *   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.
 * ====================================================================
 */

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.5 $
 */
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;
    }
}
