#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# ==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE
# ------------------------------------------------------------------------------

# tag::includeManual[]
"""
NAME:
    ValidateContentFormat.py

DESCRIPTION:
    This trigger is intended for file content validation as part of change-content trigger

    Works for YAML, XML - only checks files matching required file extensions - see below.

    To install, add a line to your Perforce triggers table like the following:

        validate-yaml change-content //....yaml "python /p4/common/bin/triggers/ValidateContentFormat.py -p %serverport% -u perforce --yaml %change% "
        validate-xml  change-content //....xml  "python /p4/common/bin/triggers/ValidateContentFormat.py -p %serverport% -u perforce --xml %change% "

    or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER):

        validate-yaml change-content //....yaml "python /p4/common/bin/triggers/ValidateContentFormat.py --yaml %change% "
        validate-xml  change-content //....xml  "python /p4/common/bin/triggers/ValidateContentFormat.py --xml %change% "

    You may need to provide the full path to python executable, or edit the path to the trigger.
    Also, don't forget to make the file executable.
"""
# end::includeManual[]

# Python 2.7/3.3 compatibility.
from __future__ import print_function

import sys
import re
import os
import P4Triggers
import P4
import yaml
import tempfile
from lxml import etree

def yaml_validator(fname):
    try:
        with open(fname, 'r') as f:
            content = yaml.load(f)
        return ""
    except Exception as e:
        return str(e)

def xml_validator(fname):
    try:
        with open(fname, 'r') as f:
            doc = etree.parse(f)
        return ""
    except Exception as e:
        return str(e)

# Regex extensions to search for in filenames
file_extensions = {"yaml": ["\.yaml$", "\.yml$"],
                   "xml": ["\.xml$"]}
# Format validator
file_validator = {"yaml": yaml_validator,
                  "xml": xml_validator}

class ValidateContentFormat(P4Triggers.P4Trigger):
    """See module doc string for details"""

    def __init__(self, *args, **kwargs):
        P4Triggers.P4Trigger.__init__(self, **kwargs)
        self.parse_args(__doc__, args)

    def add_parse_args(self, parser):
        """Specific args for this trigger - also calls super class to add common trigger args"""
        parser.add_argument('--yaml', default=False, action='store_true',
                            help="Trigger validates YAML files")
        parser.add_argument('--xml', default=False, action='store_true',
                            help="Trigger validates XML files")
        parser.add_argument('change', help="Change to process - %%change%% argument from triggers entry.")
        super(ValidateContentFormat, self).add_parse_args(parser)

    def run(self):
        """Runs trigger"""
        error_count = 0
        try:
            self.logger.debug("ValidateContent trigger firing")
            self.setupP4()
            self.p4.connect()

            exts = ""
            if self.options.yaml:
                exts = file_extensions["yaml"]
                validator = file_validator["yaml"]
            elif self.options.xml:
                exts = file_extensions["xml"]
                validator = file_validator["xml"]

            change = self.getChange(self.options.change)
            for df in change.files:
                valid_ext = False
                fname = df.depotFile.lower()
                for ext in exts:
                    if re.search(ext, fname):
                        valid_ext = True
                        break
                if not valid_ext:
                    continue
                name_parts = df.depotFile.split("/")
                tfile = os.path.join(tempfile.gettempdir(), name_parts[-1])
                self.logger.debug("Validating %s as local: %s" % (df.depotFile, tfile))
                self.p4.run_print("-o", tfile, "%s@=%s" % (df.depotFile, self.options.change))
                result = validator(tfile)
                self.logger.debug("Validated %s - result: %s" % (tfile, result))
                if result:
                    error_count += 1
                    msg = "Invalid format for %s\n%s" % (df.depotFile, result)
                    self.logger.warning(msg)
                    print(msg)
            if error_count:
                print("%d files failed" % error_count)
            return error_count
        except Exception:
            return self.reportException()

if __name__ == '__main__':
    """ Main Program"""
    trigger = ValidateContentFormat(*sys.argv[1:])
    sys.exit(trigger.run())
