# Copyright (c) 2015 Uber Technologies, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

#
# Autogenerated by Thrift Compiler (0.9.2)
#
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
#
#  options string: py:tornado,dynamic,slots,dynexc=VCRThriftError,dynbase=VCRThriftBase,dynimport=from tchannel.testing.vcr.thrift import *
#

from thrift.Thrift import TType, TMessageType, TException, TApplicationException
from ttypes import *
from thrift.Thrift import TProcessor
from tchannel.testing.vcr.thrift import *
from tornado import gen
from tornado import concurrent
from thrift.transport import TTransport

class Iface(object):
  """
  The VCRProxy service is responsible for forwarding requests to the remote
  server, recording the interactions, and replaying responses for known
  requests.
  """
  def send(self, request):
    """
    Send the given request through the system.

    If the request is known, replay its response. Otherwise, forward it to
    the remote server and return the remote server's response.

    Parameters:
     - request
    """
    pass


class Client(Iface):
  """
  The VCRProxy service is responsible for forwarding requests to the remote
  server, recording the interactions, and replaying responses for known
  requests.
  """
  def __init__(self, transport, iprot_factory, oprot_factory=None):
    self._transport = transport
    self._iprot_factory = iprot_factory
    self._oprot_factory = (oprot_factory if oprot_factory is not None
                           else iprot_factory)
    self._seqid = 0
    self._reqs = {}
    self._transport.io_loop.spawn_callback(self._start_receiving)

  @gen.engine
  def _start_receiving(self):
    while True:
      try:
        frame = yield self._transport.readFrame()
      except TTransport.TTransportException as e:
        for future in self._reqs.itervalues():
          future.set_exception(e)
        self._reqs = {}
        return
      tr = TTransport.TMemoryBuffer(frame)
      iprot = self._iprot_factory.getProtocol(tr)
      (fname, mtype, rseqid) = iprot.readMessageBegin()
      future = self._reqs.pop(rseqid, None)
      if not future:
        # future has already been discarded
        continue
      method = getattr(self, 'recv_' + fname)
      try:
        result = method(iprot, mtype, rseqid)
      except Exception as e:
        future.set_exception(e)
      else:
        future.set_result(result)

  def send(self, request):
    """
    Send the given request through the system.

    If the request is known, replay its response. Otherwise, forward it to
    the remote server and return the remote server's response.

    Parameters:
     - request
    """
    self._seqid += 1
    future = self._reqs[self._seqid] = concurrent.Future()
    self.send_send(request)
    return future

  def send_send(self, request):
    oprot = self._oprot_factory.getProtocol(self._transport)
    oprot.writeMessageBegin('send', TMessageType.CALL, self._seqid)
    args = send_args()
    args.request = request
    args.write(oprot)
    oprot.writeMessageEnd()
    oprot.trans.flush()

  def recv_send(self, iprot, mtype, rseqid):
    if mtype == TMessageType.EXCEPTION:
      x = TApplicationException()
      x.read(iprot)
      iprot.readMessageEnd()
      raise x
    result = send_result()
    result.read(iprot)
    iprot.readMessageEnd()
    if result.success is not None:
      return result.success
    if result.cannotRecord is not None:
      raise result.cannotRecord
    if result.remoteServiceError is not None:
      raise result.remoteServiceError
    if result.serviceError is not None:
      raise result.serviceError
    if result.noPeersAvailable is not None:
      raise result.noPeersAvailable
    raise TApplicationException(TApplicationException.MISSING_RESULT, "send failed: unknown result");


class Processor(Iface, TProcessor):
  def __init__(self, handler):
    self._handler = handler
    self._processMap = {}
    self._processMap["send"] = Processor.process_send

  def process(self, iprot, oprot):
    (name, type, seqid) = iprot.readMessageBegin()
    if name not in self._processMap:
      iprot.skip(TType.STRUCT)
      iprot.readMessageEnd()
      x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name))
      oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid)
      x.write(oprot)
      oprot.writeMessageEnd()
      oprot.trans.flush()
      return
    else:
      return self._processMap[name](self, seqid, iprot, oprot)

  @gen.coroutine
  def process_send(self, seqid, iprot, oprot):
    args = send_args()
    args.read(iprot)
    iprot.readMessageEnd()
    result = send_result()
    try:
      result.success = yield gen.maybe_future(self._handler.send(args.request))
    except CannotRecordInteractionsError, cannotRecord:
      result.cannotRecord = cannotRecord
    except RemoteServiceError, remoteServiceError:
      result.remoteServiceError = remoteServiceError
    except VCRServiceError, serviceError:
      result.serviceError = serviceError
    except NoPeersAvailableError, noPeersAvailable:
      result.noPeersAvailable = noPeersAvailable
    oprot.writeMessageBegin("send", TMessageType.REPLY, seqid)
    result.write(oprot)
    oprot.writeMessageEnd()
    oprot.trans.flush()


# HELPER FUNCTIONS AND STRUCTURES

class send_args(VCRThriftBase):
  """
  Attributes:
   - request
  """

  __slots__ = [ 
    'request',
   ]

  thrift_spec = (
    None, # 0
    (1, TType.STRUCT, 'request', (Request, Request.thrift_spec), None, ), # 1
  )

  def __init__(self, request=None,):
    self.request = request

  def __hash__(self):
    value = 17
    value = (value * 31) ^ hash(self.request)
    return value


class send_result(VCRThriftBase):
  """
  Attributes:
   - success
   - cannotRecord: Thrown if the request was unrecognized and the record mode for the
  current cassette disallows recording new interactions.
   - remoteServiceError
   - serviceError
   - noPeersAvailable
  """

  __slots__ = [ 
    'success',
    'cannotRecord',
    'remoteServiceError',
    'serviceError',
    'noPeersAvailable',
   ]

  thrift_spec = (
    (0, TType.STRUCT, 'success', (Response, Response.thrift_spec), None, ), # 0
    (1, TType.STRUCT, 'cannotRecord', (CannotRecordInteractionsError, CannotRecordInteractionsError.thrift_spec), None, ), # 1
    (2, TType.STRUCT, 'remoteServiceError', (RemoteServiceError, RemoteServiceError.thrift_spec), None, ), # 2
    (3, TType.STRUCT, 'serviceError', (VCRServiceError, VCRServiceError.thrift_spec), None, ), # 3
    (4, TType.STRUCT, 'noPeersAvailable', (NoPeersAvailableError, NoPeersAvailableError.thrift_spec), None, ), # 4
  )

  def __init__(self, success=None, cannotRecord=None, remoteServiceError=None, serviceError=None, noPeersAvailable=None,):
    self.success = success
    self.cannotRecord = cannotRecord
    self.remoteServiceError = remoteServiceError
    self.serviceError = serviceError
    self.noPeersAvailable = noPeersAvailable

  def __hash__(self):
    value = 17
    value = (value * 31) ^ hash(self.success)
    value = (value * 31) ^ hash(self.cannotRecord)
    value = (value * 31) ^ hash(self.remoteServiceError)
    value = (value * 31) ^ hash(self.serviceError)
    value = (value * 31) ^ hash(self.noPeersAvailable)
    return value

