# -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
# ex: set expandtab softtabstop=4 shiftwidth=4:
#
# Copyright (C) 2009,2010,2011,2012,2013  Contributor
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Pub/sub mechanism for status messages."""


from threading import Lock
from collections import deque
from logging import DEBUG

from twisted.internet import reactor

from aquilon.python_patches import load_uuid_quickly

uuid = load_uuid_quickly()  # pylint: disable=C0103


# Some requests can generate many debug messages.  After this limit is
# passed older records will be replaced with None.
# This is somewhat arbitrary as a number.  If each log message was a
# string of 100 bytes then 10000 would be about 1 MB.  However the log
# record object has quite a few more fields (source file, line, function
# name, and potentially exception info) which can bloat this quite a bit.
MAX_DEBUG_MESSAGES_PER_REQUEST = 10000


class RequestStatus(object):
    """Store status information for each incoming request.

    Each request will get one of these objects to write status
    information into.  Other commands (currently only show_request)
    can attach StatusSubscriber instances that will be called whenever
    new status info is available.  The info is stored as LogRecord
    objects from the python logging module.

    The initial implementation of this class used Condition objects to
    send notifications about available messages.  This caused two
    problems.  First and most important, deadlocks.  This code needs
    to work from threads that are themselves using lock mechanisms.
    Second, speed.  Anything beyond the simple Lock mechanism is
    implemented in python (that is, not natively).  Performance is
    horrible.

    The current implementation uses callbacks to reduce the number
    of threads touching this object.  Since there could be multiple
    threads we still need to lock operations that change internal
    state.  The lock in use is the primitive Lock provided by python.
    It is the only lock construct implemented natively in cPython
    (as of 2.5) - the rest are built on top of it.

    """

    def __init__(self, auditid, requestid=None):
        """Should only be created by the StatusCatalog."""
        self.auditid = str(auditid) if auditid is not None else None
        self.requestid = requestid
        self.user = ""
        self.command = ""
        self.args = []
        self.description = ""
        self.subscriber_descriptions = []
        self.records = []
        self.debug_fifo = deque()
        self.is_finished = False