import os.path
import re

from buildbot.plugins import steps, util
from buildbot.process import buildstep
from buildbot.status.results import SUCCESS
import yaml

import environments as envs


SERVO_REPO = "https://github.com/servo/servo"


class ServoFactory(util.BuildFactory):
    """
    Build factory which checks out the servo repo as the first build step.
    """

    def __init__(self, build_steps):
        """
        Takes a list of Buildbot steps.
        Prefer using DynamicServoFactory to using this class directly.
        """
        all_steps = [
            steps.Git(repourl=SERVO_REPO, mode="full", method="clobber"),
        ] + build_steps
        # util.BuildFactory is an old-style class so we cannot use super()
        # but must hardcode the superclass here
        util.BuildFactory.__init__(self, all_steps)


class BadConfigurationStep(buildstep.BuildStep):
    """
    Step which immediately fails the build due to a bad configuration.
    """

    haltOnFailure = True
    flunkOnFailure = True

    def __init__(self, exception):
        self.exception = exception

    def run(self):
        raise Exception("Bad configuration, unable to convert to steps" +
                        str(self.exception))


class DynamicServoFactory(ServoFactory):
    """
    Smart factory which takes a list of shell commands from a yaml file
    and creates the appropriate Buildbot Steps. Uses heuristics to infer
    Step type, if there are any logfiles, etc.
    """

    def __init__(self, builder_name, environment):
        self.environment = environment
        try:
            config_dir = os.path.dirname(os.path.realpath(__file__))
            yaml_path = os.path.join(config_dir, 'steps.yml')
            with open(yaml_path) as steps_file:
                builder_steps = yaml.safe_load(steps_file)
            commands = builder_steps[builder_name]
            dynamic_steps = [self.make_step(command) for command in commands]
        except Exception as e:  # Bad step configuration, fail build
            print(str(e))
            dynamic_steps = [BadConfigurationStep(e)]

        # TODO: windows compatibility (use a custom script for this?)
        pkill_step = [steps.ShellCommand(command=["pkill", "-x", "servo"],
                                         decodeRC={0: SUCCESS, 1: SUCCESS})]

        # util.BuildFactory is an old-style class so we cannot use super()
        # but must hardcode the superclass here
        ServoFactory.__init__(self, pkill_step + dynamic_steps)

    def make_step(self, command):
        step_kwargs = {}
        step_kwargs['env'] = self.environment

        command = command.split(' ')
        step_kwargs['command'] = command

        step_class = steps.ShellCommand
        args = iter(command)
        for arg in args:
            # Change Step class to capture warnings as needed
            # (steps.Compile and steps.Test catch warnings)
            if arg == './mach':
                mach_arg = next(args)
                if re.match('build(-.*)?', mach_arg):
                    step_class = steps.Compile
                elif re.match('test-.*', mach_arg):
                    step_class = steps.Test

            # Capture any logfiles
            elif re.match('--log-.*', arg):
                logfile = next(args)
                if 'logfiles' not in step_kwargs:
                    step_kwargs['logfiles'] = {}
                step_kwargs['logfiles'][logfile] = logfile

        return step_class(**step_kwargs)


doc = ServoFactory([
    # This is not dynamic because a) we need to pass the logEnviron kwarg
    # and b) changes to the documentation build are already encapsulated
    # in the upload_docs.sh script; any further changes should go through
    # the saltfs repo to avoid leaking the token.
    steps.ShellCommand(command=["etc/ci/upload_docs.sh"],
                       env=envs.doc,
                       # important not to leak token
                       logEnviron=False),
])

windows = ServoFactory([
    # TODO: convert this to use DynamicServoFactory
    # We need to run each command in a bash login shell, which breaks the
    # heuristics used by DynamicServoFactory.make_step
    steps.Compile(command=["bash", "-l", "-c", "./mach build -d -v"],
                  env=envs.build_windows),
    steps.Compile(command=["bash", "-l", "-c", "./mach test-unit"],
                  env=envs.build_windows),
    # TODO: run lockfile_changed.sh and manifest_changed.sh scripts
])

@@ -3988,16 +3988,109 @@
 e),%0A%5D)%0A%0A
+%0Adef make_cd_string(command):%0A    return %22cd /c/buildbot/slave/windows/build ; %22 + command%0A%0A%0A
 windows 
@@ -4325,33 +4325,75 @@
 sh%22, %22-l%22, %22-c%22,
- 
+%0A                           make_cd_string(
 %22./mach build -d
@@ -4396,16 +4396,17 @@
 d -d -v%22
+)
 %5D,%0A     
@@ -4489,17 +4489,59 @@
 %22, %22-c%22,
- 
+%0A                           make_cd_string(
 %22./mach 
@@ -4550,16 +4550,17 @@
 st-unit%22
+)
 %5D,%0A     

Fix building on Windows