package edu.hm.hafner.analysis.parser;

import edu.hm.hafner.analysis.Issue;
import edu.hm.hafner.analysis.IssueBuilder;
import edu.hm.hafner.analysis.LookaheadParser;
import edu.hm.hafner.util.LookaheadStream;

import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Parser for Mentor Graphics Modelsim/Questa Simulator.
 *
 * @author Derrick Gibelyou
 */
public class MentorParser extends LookaheadParser {

    /**
     * Matches the beginning of a Modelsim/Questa message "** [priority] : [The remainder of the message]".
     */
    private static final String MSG_REGEX = "\\*\\*\\s+(?<priority>\\w+):\\s+(?<message>.*)";

    /**
     * The first capture group captures the message type such as "vlog-###" or "vsim-###".
     * The second group matches an optional filename in the form of filename.sv(line-number).
     * The third group matches the rest of the message.
     */
    private static final Pattern VSIM_PATTERN = Pattern.compile(
            "\\((?<category>v\\w+-\\d+)\\)(?: (?<filename>\\S*)\\((?<line>\\d+)\\):)? (?<message>.*)");

    /**
     * The first capture group matches the filename and line number.
     * The second capture group matches the warning type such as "vlog-###" or "vsim-###".
     * The third group matches the rest of the message.
     */
    private static final Pattern VLOG_PATTERN = Pattern.compile(
            "at (?<filename>\\S*)\\((?<line>\\d+)\\): \\((?<category>v\\w+-\\d+)\\) (?<message>.*)");
    
    /**
     * The first capture group matches the timestamp for the message on the previous line.
     * The iteration is ignored.
     * \w* matches a word such as "module" or "protected".
     * The next capture group captures the path of the module.
     * The next capture groups capture the File name and Line number.
     */
    private static final Pattern TIME_FILE_PATTERN = Pattern.compile(
            "# {4}Time: (?<simtime>\\d* \\ws)(?: {2}Iteration: \\d+)? {2}\\w*: (?<module>.\\S*)(?: File: (?<filename>\\S+)(?: Line: (?<line>\\d+))?)?.*");

    
    /**
     * Construct a parser for MentorGraphics Modelsim/Questa logs.
     */
    public MentorParser() {
        super(MSG_REGEX);
    }

    @Override
    protected Optional<Issue> createIssue(final Matcher matcher, final LookaheadStream lookahead,
                                          final IssueBuilder builder) {
        clearBuilder(builder);

        builder.guessSeverity(matcher.group("priority"));

        String message = matcher.group("message");
        if (message.contains("while parsing file")) {
            parseVlogMessage(lookahead, builder, message);
        } else {
            parseVsimMessage(lookahead, builder, message);
        }

        return builder.buildOptional();
    }

    private void parseVlogMessage(LookaheadStream lookahead, IssueBuilder builder, String message) {
        while (!lookahead.peekNext().startsWith("# ** at ")) {
            lookahead.next();
        }

        // If we can refine the message, the do.
        // Else just return what we got passed.
        Matcher vlog = VLOG_PATTERN.matcher(lookahead.next().substring(5));
        if (vlog.matches()) {
            builder.setFileName(vlog.group("filename"));
            builder.setLineStart(vlog.group("line"));
            builder.setCategory(vlog.group("category"));
            message = vlog.group("message");
        }
        builder.setDescription("");
        builder.setMessage(message);
    }

    private void parseVsimMessage(LookaheadStream lookahead, IssueBuilder builder, String message) {
        builder.setDescription(parseSimTime(lookahead, builder));

        // If we can refine the message, the do.
        // Else just return what we got passed.
        Matcher vsim = VSIM_PATTERN.matcher(message);
        if (vsim.matches()) {
            builder.setCategory(vsim.group("category"));
            builder.setFileName(vsim.group("filename"));
            builder.setLineStart(vsim.group("line"));
            message = vsim.group("message");
        }
        builder.setMessage(message);
    }

    private String parseSimTime(LookaheadStream lookahead, IssueBuilder builder) {
        StringBuilder description = new StringBuilder();
        String timeLine = "";
        while (lookahead.hasNext() && !lookahead.peekNext().contains("# **")) {
            timeLine = lookahead.next();
            if (timeLine.startsWith("#    Time:")) {
                break;
            }
            description.append("<br>");
            description.append(timeLine);
        }
        Matcher tf = TIME_FILE_PATTERN.matcher(timeLine);
        if (tf.matches()) {
            builder.setModuleName(tf.group("module"));
            builder.setFileName(tf.group("filename"));
            builder.setLineStart(tf.group("line"));
        }
        return description.toString();
    }

    private void clearBuilder(IssueBuilder builder) {
        builder.setModuleName(null);
        builder.setFileName(null);
        builder.setLineStart(null);
        builder.setCategory(null);
    }

    @Override
    protected boolean isLineInteresting(String line) {
        return line.startsWith("# **");
    }

}

@@ -447,16 +447,69 @@
 rser %7B%0A%0A
+    public static final long serialVersionUID = 1L;%0A%0A
     /**%0A
@@ -2705,16 +2705,24 @@
        %7D
+%0A       
  else %7B%0A
@@ -2864,32 +2864,38 @@
 arseVlogMessage(
+final 
 LookaheadStream 
@@ -2896,32 +2896,38 @@
 tream lookahead,
+ final
  IssueBuilder bu
@@ -2924,32 +2924,38 @@
 Builder builder,
+ final
  String message)
@@ -3052,32 +3052,71 @@
 xt();%0A        %7D%0A
+        String parsedMessage = message;
 %0A        // If w
@@ -3475,33 +3475,39 @@
 ));%0A            
-m
+parsedM
 essage = vlog.gr
@@ -3587,33 +3587,39 @@
 lder.setMessage(
-m
+parsedM
 essage);%0A    %7D%0A%0A
@@ -3652,16 +3652,22 @@
 Message(
+final 
 Lookahea
@@ -3676,32 +3676,38 @@
 tream lookahead,
+ final
  IssueBuilder bu
@@ -3712,16 +3712,22 @@
 builder,
+ final
  String 
@@ -3800,24 +3800,64 @@
 builder));%0A%0A
+        String parsedMessage = message;%0A
         // I
@@ -4205,17 +4205,23 @@
         
-m
+parsedM
 essage =
@@ -4281,17 +4281,23 @@
 Message(
-m
+parsedM
 essage);
@@ -4336,16 +4336,22 @@
 SimTime(
+final 
 Lookahea
@@ -4368,16 +4368,22 @@
 okahead,
+ final
  IssueBu
@@ -5123,16 +5123,22 @@
 Builder(
+final 
 IssueBui
@@ -5357,16 +5357,22 @@
 resting(
+final 
 String l

Make parameters final, add serial UID