###################################################################################
# LAVA QA tool
# Copyright (C) 2015 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 time
from lqa_api.exit_codes import SUCCESS

# Default to a 3 hours timeout
WAIT_DEFAULT_TIMEOUT="3h"

class WaitQueue(list):
    """Wait queue for jobs. It is a subclass of the list object."""

    def __init__(self):
        # Default queue exit code is SUCCESS which means no job in the queue failed
        self._queue_exit_code = SUCCESS

    def addjob(self, job, add_cb=None):
        """Add job to the wait queue."""
        self.append(job)
        add_cb and add_cb(self, job)

    def removejob(self, job, remove_cb=None):
        """Remove job from the wait queue."""
        self.remove(job)
        # Update queue exit code
        self._update_exit_code(job)
        remove_cb and remove_cb(self, job)

    def has_jobs(self):
        """Check if there are pending jobs in the wait queue."""
        return len(self) > 0

    def wait(self, readable_timeout=WAIT_DEFAULT_TIMEOUT,
             wait_cb=None, remove_cb=None, timeout_cb=None):
        """Wait for the jobs on Submitted and Running state."""

        timeout = _convert_readable_timeout_to_seconds(readable_timeout)
        endtime = None
        # Negative value unset the timeout.
        if timeout >= 0:
            endtime = time.time() + timeout

        while True:
            for job in self[:]:
                if job.status in ['Submitted', 'Running']:
                    # Call the wait function (if specified).
                    wait_cb and wait_cb(self, job)
                else:
                    self.removejob(job, remove_cb)
            # Break if no more jobs to wait for.
            if not self.has_jobs(): 
                break
            # Exit with timeout of endtime.
            if endtime and time.time() > endtime:
                # Call the timeout function (if specified).
                timeout_cb and timeout_cb(self, timeout)
                break
            # Sleep for 30 seconds before polling again.
            time.sleep(30)

        # Return the queue.
        return self

    def _update_exit_code(self, job):
        """This function collates the queue exit code to the worst error code"""
        ec = job.exit_code
        if ec < self._queue_exit_code:
            self._queue_exit_code = ec

    @property
    def exit_code(self):
        return self._queue_exit_code

def _convert_readable_timeout_to_seconds(timeout):
    """
    Supported notations:
       <timeout>h = hours
       <timeout>m = minutes
       <timeout>s = seconds (default if not specified)
    """
    # 1 minute = 60 secs
    minute = 60
    hour = minute * minute
    return \
        timeout.endswith('h') and float(timeout[:-1]) * hour   or \
        timeout.endswith('m') and float(timeout[:-1]) * minute or \
        timeout.endswith('s') and float(timeout[:-1])          or \
        float(timeout)
