###############################################################################
##
##  Copyright (C) 2013 Tavendo GmbH
##
##  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.
##
###############################################################################


import zope
from zope.interface import Interface, Attribute


class IWebSocketChannel(Interface):
   """
   A WebSocket channel is a bidirectional, full-duplex, ordered, reliable message channel
   over a WebSocket connection as specified in RFC6455.

   This interface defines a message-based API to WebSocket plus auxiliary hooks
   and methods.
   """

   def onConnect(requestOrResponse):
      """
      Callback fired during WebSocket opening handshake when a client connects (with
      request from client) or when server connection established (with response from
      server).

      :param requestOrResponse: Connection request or response.
      :type requestOrResponse: Instance of :class:`autobahn.websocket.protocol.ConnectionRequest`
                               or :class:`autobahn.websocket.protocol.ConnectionResponse`.
      """

   def onOpen():
      """
      Callback fired when the initial WebSocket opening handshake was completed.
      You now can send and receive WebSocket messages.
      """

   def sendMessage(payload, isBinary = False, fragmentSize = None, sync = False, doNotCompress = False):
      """
      Send a WebSocket message.

      You can send text or binary messages, and optionally specifiy a payload fragment size.
      When the latter is given, the payload will be split up into WebSocket frames each with
      payload length `<= fragmentSize`.

      :param payload: The message payload.
      :type payload: bytes
      :param isBinary: `True` iff payload is binary, else the payload must be UTF-8 encoded text.
      :type isBinary: bool
      :param fragmentSize: Fragment message into WebSocket fragments of this size.
      :type fragmentSize: int
      :param sync: Iff `True`, try to force data onto the wire immediately. Note: do NOT use
                   this normally unless you know what you are doing. Performance likely will
                   suffer significantly. This feature is mainly here for use by Autobahn|Testsuite.
      :type sync: bool
      :param doNotCompress: Iff `True`, never compress this message. This only applies to
                            Hybi-Mode and only when WebSocket compression has been negotiated on
                            the WebSocket connection. Use when you know the payload 
                            uncompressible (e.g. encrypted or already compressed).
      :type doNotCompress: bool
      """

   def onMessage(payload, isBinary):
      """
      Callback fired when a complete WebSocket message was received.

      :param payload: Message payload (UTF-8 encoded text or binary). Can also be empty when
                      the WebSocket message contained no payload.
      :type payload: bytes
      :param isBinary: `True` iff payload is binary, else the payload is UTF-8 encoded text.
      :type isBinary: bool
      """

   def sendClose(code = None, reason = None):
      """
      Starts a WebSocket closing handshake tearing down the WebSocket connection.

      :param code: An optional close status code (`1000` for normal close or `3000-4999` for
                   application specific close).
      :type code: int
      :param reason: An optional close reason (a string that when present, a status
                     code MUST also be present).
      :type reason: str
      """

   def onClose(wasClean, code, reason):
      """
      Callback fired when the WebSocket connection has been closed (WebSocket closing
      handshake has been finished or the connection was closed uncleanly).

      :param wasClean: True, iff the WebSocket connection was closed cleanly.
      :type wasClean: bool
      :param code: None or close status code (as sent by the WebSocket peer).
      :type code: int
      :param reason: None or close reason (as sent by the WebSocket peer).
      :type reason: str
      """

   def sendPreparedMessage(preparedMsg):
      """
      Send a message that was previously prepared with :func:`autobahn.websocket.protocol.WebSocketFactory.prepareMessage`.

      :param prepareMessage: A previsouly prepared message.
      :type prepareMessage: Instance of :class:`autobahn.websocket.protocol.PreparedMessage`.
      """

   def sendPing(payload = None):
      """
      Send a WebSocket ping to the peer.

      A peer is expected to pong back the payload a soon as "practical". When more than
      one ping is outstanding at a peer, the peer may elect to respond only to the last ping.

      :param payload: An (optional) arbitrary payload of length `<126` octets.
      :type payload: bytes
      """

   def onPing(payload):
      """
      Callback fired when a WebSocket ping was received. A default implementation responds
      by sending a WebSocket pong.

      :param payload: Payload of ping (when there was any). Can be arbitrary, up to `125` octets.
      :type payload: bytes
      """

   def sendPong(payload = None):
      """
      Send a WebSocket pong to the peer.

      A WebSocket pong may be sent unsolicited. This serves as a unidirectional heartbeat.
      A response to an unsolicited pong is "not expected".

      :param payload: An (optional) arbitrary payload of length < 126 octets.
      :type payload: bytes
      """

   def onPong(payload):
      """
      Callback fired when a WebSocket pong was received. A default implementation does nothing.

      :param payload: Payload of pong (when there was any). Can be arbitrary, up to 125 octets.
      :type payload: bytes
      """


class IWebSocketChannelFrameApi(IWebSocketChannel):
   """
   Frame-based API to a WebSocket channel.
   """

   def onMessageBegin(isBinary):
      """
      Callback fired when receiving of a new WebSocket message has begun.

      :param isBinary: `True` iff payload is binary, else the payload is UTF-8 encoded text.
      :type isBinary: bool
      """

   def onMessageFrame(payload):
      """
      Callback fired when a complete WebSocket message frame for a previously begun
      WebSocket message has been received.

      :param payload: Message frame payload (a list of chunks received).
      :type payload: list of bytes
      """

   def onMessageEnd():
      """
      Callback fired when a WebSocket message has been completely received (the last
      WebSocket frame for that message has been received).
      """

   def beginMessage(isBinary = False, doNotCompress = False):
      """
      Begin sending a new WebSocket message.

      :param isBinary: `True` iff payload is binary, else the payload must be UTF-8 encoded text.
      :type isBinary: bool
      :param doNotCompress: Iff `True`, never compress this message. This only applies to
                            Hybi-Mode and only when WebSocket compression has been negotiated on
                            the WebSocket connection. Use when you know the payload 
                            uncompressible (e.g. encrypted or already compressed).
      :type doNotCompress: bool
      """

   def sendMessageFrame(payload, sync = False):
      """
      When a message has been previously begun, send a complete message frame in one go.

      :param payload: The message frame payload. When sending a text message, the payload must
                      be UTF-8 encoded already.
      :type payload: bytes
      :param sync: Iff `True`, try to force data onto the wire immediately. Note: do NOT use
                   this normally unless you know what you are doing. Performance likely will
                   suffer significantly. This feature is mainly here for use by Autobahn|Testsuite.
      :type sync: bool
      """

   def endMessage():
      """
      End a message previously begun message. No more frames may be sent (for that message).
      You have to begin a new message before sending again.
      """



class IWebSocketChannelStreamingApi(IWebSocketChannelFrameApi):
   """
   Streaming API to a WebSocket channel.
   """

   def onMessageFrameBegin(length):
      """
      Callback fired when receiving a new message frame has begun.
      A default implementation will prepare to buffer message frame data.

      :param length: Payload length of message frame which is subsequently received.
      :type length: int
      """

   def onMessageFrameData(payload):
      """
      Callback fired when receiving data within a previously begun message frame.
      A default implementation will buffer data for frame.

      :param payload: Partial payload for message frame.
      :type payload: bytes
      """

   def onMessageFrameEnd():
      """
      Callback fired when a previously begun message frame has been completely received.
      A default implementation will flatten the buffered frame data and
      fire `onMessageFrame`.
      """

   def beginMessageFrame(length):
      """
      Begin sending a new message frame.

      :param length: Length of the frame which is to be started. Must be `>= 0` and `<= 2^63`.
      :type length: int
      """

   def sendMessageFrameData(payload, sync = False):
      """
      Send out data when within a message frame (message was begun, frame was begun).
      Note that the frame is automatically ended when enough data has been sent.
      In other words, there is no `endMessageFrame`, since you have begun the frame
      specifying the frame length, which implicitly defined the frame end. This is different
      from messages, which you begin *and* end explicitly , since a message can contain
      an unlimited number of frames.

      :param payload: Frame payload to send.
      :type payload: bytes
      :param sync: Iff `True`, try to force data onto the wire immediately. Note: do NOT use
                   this normally unless you know what you are doing. Performance likely will
                   suffer significantly. This feature is mainly here for use by Autobahn|Testsuite.
      :type sync: bool

      :returns: int -- When the currently sent message frame is still incomplete,
                       returns octets remaining to be sent. When the frame is complete,
                       returns `0`, when `< 0`, the amount of unconsumed data in payload
                       argument.
      """



class IWampDealer(Interface):
   """
   """

   def register(self, endpoint, obj):
      """
      """

   def registerMethod(self, endpoint, obj, method):
      """
      """

   def registerProcedure(self, endpoint, procedure):
      """
      """

   def unregister(self, endpoint):
      """
      """


class IWampBroker(Interface):
   """
   """

   def register(self, topic, prefix = False, publish = True, subscribe = True):
      """
      """

   def unregister(self, topic):
      """
      """


class IWampPublishOptions(Interface):

   excludeMe = Attribute("Exclude me, the publisher, from receiving the event (even though I may be subscribed).")



class IWampSession(Interface):
   """
   """

   def call(self, *args):
      """
      """

   def subscribe(self, topic, handler):
      """
      """

   def unsubscribe(self, topic, handler = None):
      """
      """

   def publish(self, topic, event,
               excludeMe = None,
               exclude = None,
               eligible = None,
               discloseMe = None):
      """
      """

   def setDealer(self, dealer = None):
      """
      """

   def setBroker(self, broker = None):
      """
      """
