###################################################################################
# LAVA QA API - This class represents a LAVA job object.
# Copyright (C) 2015, 2016 Collabora Ltd.

# 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  US
###################################################################################

import json
from xmlrpclib import Fault
from lqa_api.test import Test

class JobError(Exception):
    """This is the base exception for all Job errors."""
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code

    def __str__(self):
        return "{} {}".format(self.msg, self.code)

class Job(object):

    def __init__(self, job_id, server):
        self._id = job_id
        self._server = server
        self._status = {}
        self._details = {}
        self._bundle = {}
        self._tests = []

    def __str__(self):
        return "{} - {}".format(self._id, self.name)

    def _fetch_job_status(self):
        """Method to fetch the job status"""
        try:
            self._status = self._server.job_status(self._id)
        except Fault as e:
            raise JobError(e.faultString, e.faultCode)

    def _fetch_job_details(self):
        """Method to fetch the job details"""
        if not self._details:
            try:
                self._details = self._server.job_details(self._id)
            except Fault as e:
                raise JobError(e.faultString, e.faultCode)

    def _fetch_job_bundle(self):
        """Fetch job bundle"""
        if not self._bundle:
            try:
                bundle = self._server.get_bundle(self.sha1)
                self._bundle = json.loads(bundle['content'])
            except Fault as e:
                raise JobError(e.faultString, e.faultCode)

    def _fetch_tests(self):
        if not self._tests:
            for test_run in self.test_runs:
                self._tests.append(Test(test_run))

    def _get_job_details_prop(self, prop):
        """Get property prop from job details metadata"""
        self._fetch_job_details()
        return self._details.get(prop, None)

    # External API
    @property
    def id(self):
        return self._id

    @property
    def bundle(self):
        self._fetch_job_bundle()
        return self._bundle

    @property
    def details(self):
        self._fetch_job_details()
        return self._details

    @property
    def name(self):
        return self._get_job_details_prop('description')

    @property
    def bundle_link(self):
        return self._get_job_details_prop('_results_link')

    @property
    def priority(self):
        return { None : None,
                 0    : "low",
                 50   : "medium", 
                 100  : "high" }[self._get_job_details_prop('priority')]

    @property
    def submit_time(self):
        return self._get_job_details_prop('submit_time')

    @property
    def start_time(self):
        return self._get_job_details_prop('start_time')

    @property
    def end_time(self):
        return self._get_job_details_prop('end_time')

    @property
    def device_type(self):
        return self._get_job_details_prop('requested_device_type_id')

    @property
    def job_definition(self):
        return self._get_job_details_prop('definition')

    @property
    def hostname(self):
        dc = self._get_job_details_prop('_actual_device_cache')
        if dc:
            return dc.get('hostname', None)

    @property
    def worker_host(self):
        dc = self._get_job_details_prop('_actual_device_cache')
        if dc:
            return dc.get('worker_host_id', None)

    @property
    def failure_comment(self):
        return self._get_job_details_prop('failure_comment')

    @property
    def sha1(self):
        self._fetch_job_status()
        return self._status.get('bundle_sha1', None)

    @property
    def status(self):
        self._fetch_job_status()
        return self._status.get('job_status', None)

    @property
    def test_runs(self):
        self._fetch_job_bundle()
        return self._bundle.get('test_runs', None)

    @property
    def tests(self):
        self._fetch_tests()
        return self._tests

    def output(self, offset=0):
        return self._server.job_output(self.id, offset)

    def has_bundle(self):
        return self.bundle_link is not None

    # Test for the different job statuses
    def is_submitted(self):
        return self.status == 'Submitted'

    def is_running(self):
        return self.status == 'Running'

    def is_complete(self):
        return self.status == 'Complete'

    def is_incomplete(self):
        return self.status == 'Incomplete'

    def is_canceled(self):
        return self.status == 'Canceled'

    def is_canceling(self):
        return self.status == 'Canceling'
