/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * See LICENSE.txt included in this distribution for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at LICENSE.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

package org.opensolaris.opengrok.history;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import java.util.logging.Logger;
import org.opensolaris.opengrok.web.Util;

/**
 * Class representing file annotation, i.e., revision and author for the last
 * modification of each line in the file.
 */
public class Annotation {

    private final List<Line> lines = new ArrayList<Line>();
    private final Map<String, String> desc = new HashMap<String, String>();
    private int widestRevision;
    private int widestAuthor;
    private final String filename;
    static final Logger log = Logger.getLogger(Annotation.class.getName());

    public Annotation(String filename) {
        this.filename = filename;
    }

    /**
     * Gets the revision for the last change to the specified line.
     *
     * @param line line number (counting from 1)
     * @return revision string, or an empty string if there is no information
     * about the specified line
     */
    public String getRevision(int line) {
        try {
            return lines.get(line-1).revision;
        } catch (IndexOutOfBoundsException e) {
            return "";
        }
    }

    /**
     * Gets all revisions that are in use, first is the lowest one (sorted using natural order)
     *
     * @return list of all revisions the file has
     */
    public Set<String> getRevisions() {
        Set<String> ret=new HashSet<String>();
        for (Iterator<Line> it = this.lines.iterator(); it.hasNext();) {
            Line ln = it.next();
            ret.add(ln.revision);
        }
        return ret;
    }

    /**
     * Gets the author who last modified the specified line.
     *
     * @param line line number (counting from 1)
     * @return author, or an empty string if there is no information about the
     * specified line
     */
    public String getAuthor(int line) {
        try {
            return lines.get(line-1).author;
        } catch (IndexOutOfBoundsException e) {
            return "";
        }
    }

    /**
     * Gets the enabled state for the last change to the specified line.
     *
     * @param line line number (counting from 1)
     * @return true if the xref for this revision is enabled, false otherwise
     */
    public boolean isEnabled(int line) {
        try {
            return lines.get(line-1).enabled;
        } catch (IndexOutOfBoundsException e) {
            return false;
        }
    }

    /**
     * Returns the size of the file (number of lines).
     *
     * @return number of lines
     */
    public int size() {
        return lines.size();
    }

    /**
     * Returns the widest revision string in the file (used for pretty
     * printing).
     *
     * @return number of characters in the widest revision string
     */
    public int getWidestRevision() {
        return widestRevision;
    }

    /**
     * Returns the widest author name in the file (used for pretty printing).
     *
     * @return number of characters in the widest author string
     */
    public int getWidestAuthor() {
        return widestAuthor;
    }

    /**
     * Adds a line to the file.
     *
     * @param revision revision number
     * @param author author name
     */
    void addLine(String revision, String author, boolean enabled) {
        final Line line = new Line(revision, author, enabled);
        lines.add(line);
        widestRevision = Math.max(widestRevision, line.revision.length());
        widestAuthor = Math.max(widestAuthor, line.author.length());
    }

    void addDesc(String revision, String description) {
        desc.put(revision, Util.encode(description));
    }

    public String getDesc(String revision) {
        return desc.get(revision);
    }

    /** Class representing one line in the file. */
    private static class Line {
        final String revision;
        final String author;
        final boolean enabled;
        Line(String rev, String aut, boolean ena) {
            revision = (rev == null) ? "" : rev;
            author = (aut == null) ? "" : aut;
            enabled = ena;
        }
    }

    public String getFilename() {
        return filename;
    }

    //TODO below might be useless, need to test with more SCMs and different commit messages
    // to see if it will not be usefull, if title attribute of <a> loses it's breath
    public void writeTooltipMap(Writer out) throws IOException {
        out.append("<script type=\"text/javascript\">\nvar desc = new Object();\n");
        for (Entry<String, String> entry : desc.entrySet()) {
            out.append("desc['");
            out.append(entry.getKey());
            out.append("'] = \"");
            out.append(entry.getValue());
            out.append("\";\n");
        }
        out.append("</script>\n");
    }

    @Override
    public String toString() {
        StringWriter sw = new StringWriter();
        for (Line line : lines) {
            sw.append(line.revision);
            sw.append("|");
            sw.append(line.author);
            sw.append(": \n");
        }
        
        try {
            writeTooltipMap(sw);
        } catch (IOException e) {
            log.finest(e.getMessage());
        }

        return sw.toString();
    }
}
