import grpc, logging
from common.tools.client.RetryDecorator import retry, delay_exponential
from common.tools.grpc.Tools import grpc_message_to_json_string
from interdomain.proto.context_pb2 import AuthenticationResult, Slice, SliceId, SliceStatus, TeraFlowController
from interdomain.proto.interdomain_pb2_grpc import InterdomainServiceStub

LOGGER = logging.getLogger(__name__)
MAX_RETRIES = 15
DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect')

class InterdomainClient:
    def __init__(self, address, port):
        self.endpoint = '{:s}:{:s}'.format(str(address), str(port))
        LOGGER.debug('Creating channel to {:s}...'.format(self.endpoint))
        self.channel = None
        self.stub = None
        self.connect()
        LOGGER.debug('Channel created')

    def connect(self):
        self.channel = grpc.insecure_channel(self.endpoint)
        self.stub = InterdomainServiceStub(self.channel)

    def close(self):
        if self.channel is not None: self.channel.close()
        self.channel = None
        self.stub = None

    @RETRY_DECORATOR
    def RequestSlice(self, request : Slice) -> SliceId:
        LOGGER.debug('RequestSlice request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.RequestSlice(request)
        LOGGER.debug('RequestSlice result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    @RETRY_DECORATOR
    def Authenticate(self, request : TeraFlowController) -> AuthenticationResult:
        LOGGER.debug('Authenticate request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.Authenticate(request)
        LOGGER.debug('Authenticate result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    @RETRY_DECORATOR
    def LookUpSlice(self, request : Slice) -> SliceId:
        LOGGER.debug('LookUpSlice request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.LookUpSlice(request)
        LOGGER.debug('LookUpSlice result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    @RETRY_DECORATOR
    def OrderSliceFromCatalog(self, request : Slice) -> SliceStatus:
        LOGGER.debug('OrderSliceFromCatalog request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.OrderSliceFromCatalog(request)
        LOGGER.debug('OrderSliceFromCatalog result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    @RETRY_DECORATOR
    def CreateSliceAndAddToCatalog(self, request : Slice) -> SliceStatus:
        LOGGER.debug('CreateSliceAndAddToCatalog request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.CreateSliceAndAddToCatalog(request)
        LOGGER.debug('CreateSliceAndAddToCatalog result: {:s}'.format(grpc_message_to_json_string(response)))
        return response
