#   Copyright 2017 David Malcolm <dmalcolm@redhat.com>
#   Copyright 2017 Red Hat, Inc.
#
#   This library is free software; you can redistribute it and/or
#   modify it under the terms of the GNU Lesser General Public
#   License as published by the Free Software Foundation; either
#   version 2.1 of the License, or (at your option) any later version.
#
#   This library is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#   Lesser General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public
#   License along with this library; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
#   USA

# Parser for .csv files emitted by "splint"
#   http://splint.org/

from collections import namedtuple
import csv
import os
import re

from firehose.model import Message, Function, Point, Range, \
    File, Location, Generator, Metadata, Analysis, Issue, Sut, Trace, \
    State, Notes, CustomFields

FIELDS = ['warning', 'flag_code', 'flag_name', 'priority',
          'file', 'line', 'column',
          'warning_text', 'additional_text']
WARNING_TEXT_IDX = FIELDS.index('warning_text')

class Row(namedtuple('Row', FIELDS)):
    def to_issue(self):
        """
        Generate an Issue from this csv row.
        """
        location = Location(file=File(givenpath=self.file,
                                      abspath=None),
                            function=None, # FIXME
                            point=Point(int(self.line),
                                        int(self.column)))
        return Issue(cwe=None,
                     testid=self.flag_name,
                     location=location,
                     message=Message(self.warning_text),
                     notes=Notes(self.additional_text),
                     trace=None,
                     severity=self.priority,
                     customfields=None)

def parse_row(row):
    """
    Convert a list generated by csv.reader into a Row
    """
    # splint doesn't escape quotes that occur within the messages, which
    # can lead to extra fields:
    #   https://github.com/ravenexp/splint/issues/6
    # Workaround this by assuming that such quotes occurred in the
    # "Warning Text" field.
    # This fires for warnings 4 and 6
    #
    # This fixes the issue, but there is still some minor information loss
    # e.g. some quote characters in the text disappear, and we sometimes
    # gain a trailing quote character
    if len(row) > len(FIELDS):
        joined_text = ','.join(row[WARNING_TEXT_IDX:-1])
        row = row[:WARNING_TEXT_IDX] + [joined_text, row[-1]]
    return Row(*row)

def parse_splint_csv(path):
    """
    Parse a .csv file written by splint's "-csv FILENAME" option.
    Generate a list of Result instances.
    """
    generator = Generator(name='splint')
    metadata = Metadata(generator, None, None, None)
    analysis = Analysis(metadata, [])
    with open(path, 'r') as f:
        reader = csv.reader(f)
        for raw_row in reader:
            # Skip the initial title row
            if raw_row[0] == 'Warning':
                continue
            rowobj = parse_row(raw_row)
            analysis.results.append(rowobj.to_issue())

    return analysis

def parse_splint_stderr(stderr):
    """
    Parse the stderr from splint (as a string).
    Return a version string, or None.
    """
    lines = stderr.splitlines()
    if not lines:
        return None
    m = re.match("Splint\s+(.*)\s+---.*", lines[0])
    if m:
        return m.group(1)
