/*-
 * #%L
 * %%
 * Copyright (C) 2011 - 2018 BMW Car IT 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.
 * #L%
 */
package io.joynr.proxy;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;

import io.joynr.dispatcher.rpc.annotation.StatelessCallbackCorrelation;
import io.joynr.exceptions.JoynrIllegalStateException;
import io.joynr.exceptions.JoynrRuntimeException;
import io.joynr.messaging.MessagingPropertyKeys;

@Singleton
public class DefaultStatelessAsyncIdCalculatorImpl implements StatelessAsyncIdCalculator {

    private static final Logger logger = LoggerFactory.getLogger(DefaultStatelessAsyncIdCalculatorImpl.class);

    private final String channelId;
    private final Random random = new Random();
    private final Map<String, String> participantIdMap = new ConcurrentHashMap<>();

    @Inject
    public DefaultStatelessAsyncIdCalculatorImpl(@Named(MessagingPropertyKeys.CHANNELID) String channelId) {
        this.channelId = channelId;
    }

    @Override
    public String calculateParticipantId(String interfaceName, StatelessAsyncCallback statelessAsyncCallback) {
        String statelessCallbackId = calculateStatelessCallbackId(interfaceName, statelessAsyncCallback);
        String fullParticipantId = channelId + CHANNEL_SEPARATOR + statelessCallbackId;
        try {
            String uuid = UUID.nameUUIDFromBytes(fullParticipantId.getBytes("UTF-8")).toString();
            participantIdMap.putIfAbsent(uuid, statelessCallbackId);
            return uuid;
        } catch (UnsupportedEncodingException e) {
            throw new JoynrRuntimeException("Platform does not support UTF-8", e);
        }
    }

    @Override
    public String calculateStatelessCallbackId(String interfaceName, StatelessAsyncCallback statelessAsyncCallback) {
        return interfaceName + USE_CASE_SEPARATOR + statelessAsyncCallback.getUseCase();
    }

    @Override
    public String calculateStatelessCallbackMethodId(Method method) {
        StatelessCallbackCorrelation callbackCorrelation = method.getAnnotation(StatelessCallbackCorrelation.class);
        if (callbackCorrelation == null) {
            logger.error("Method {} on {} is missing StatelessCallbackCorrelation. Unable to generate callback method ID.",
                         method,
                         method.getDeclaringClass().getName());
            throw new JoynrRuntimeException("No StatelessCallbackCorrelation found on method " + method);
        }
        return callbackCorrelation.value();
    }

    @Override
    public String calculateStatelessCallbackRequestReplyId(Method method) {
        String requestReplyId = String.valueOf(random.nextLong());
        String methodId = calculateStatelessCallbackMethodId(method);
        return requestReplyId + REQUEST_REPLY_ID_SEPARATOR + methodId;
    }

    @Override
    public String extractMethodIdFromRequestReplyId(String requestReplyId) {
        if (requestReplyId == null || requestReplyId.trim().isEmpty()
                || !requestReplyId.contains(REQUEST_REPLY_ID_SEPARATOR)) {
            throw new JoynrIllegalStateException("Unable to extract method ID from invalid request/reply ID: "
                    + requestReplyId);
        }
        int index = requestReplyId.indexOf(REQUEST_REPLY_ID_SEPARATOR);
        return requestReplyId.substring(index + REQUEST_REPLY_ID_SEPARATOR.length());
    }

    @Override
    public String fromParticipantUuid(String statelessParticipantIdUuid) {
        String statelessCallbackId = null;
        try {
            statelessCallbackId = Optional.ofNullable(participantIdMap.get(statelessParticipantIdUuid))
                                          .orElseThrow(() -> new JoynrIllegalStateException("Unknown stateless participant ID UUID: "
                                                  + statelessParticipantIdUuid));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return statelessCallbackId;
    }

}

@@ -1752,16 +1752,71 @@
 ANNELID)
+%0A                                                 final
  String 
@@ -1926,16 +1926,22 @@
 ipantId(
+final 
 String i
@@ -1945,32 +1945,38 @@
 g interfaceName,
+ final
  StatelessAsyncC
@@ -2009,32 +2009,38 @@
 back) %7B%0A        
+final 
 String stateless
@@ -2125,24 +2125,30 @@
 k);%0A        
+final 
 String fullP
@@ -2237,24 +2237,30 @@
             
+final 
 String uuid 
@@ -2444,16 +2444,22 @@
  catch (
+final 
 Unsuppor
@@ -2645,16 +2645,22 @@
 lbackId(
+final 
 String i
@@ -2672,16 +2672,22 @@
 aceName,
+ final
  Statele
@@ -2891,16 +2891,22 @@
 ethodId(
+final 
 Method m
@@ -2922,16 +2922,22 @@
         
+final 
 Stateles
@@ -3541,16 +3541,22 @@
 ReplyId(
+final 
 Method m
@@ -3568,24 +3568,30 @@
 ) %7B%0A        
+final 
 String reque
@@ -3641,24 +3641,30 @@
 ));%0A        
+final 
 String metho
@@ -3857,16 +3857,22 @@
 ReplyId(
+final 
 String r
@@ -4201,16 +4201,22 @@
         
+final 
 int inde
@@ -4416,16 +4416,22 @@
 antUuid(
+final 
 String s
@@ -4471,68 +4471,20 @@
     
+final 
 String
- statelessCallbackId = null;%0A        try %7B%0A           
  sta
@@ -4572,16 +4572,25 @@
 dUuid))%0A
+         
         
@@ -4765,16 +4765,19 @@
         
+   
 + statel
@@ -4804,99 +4804,8 @@
 ));%0A
-        %7D catch (Throwable throwable) %7B%0A            throwable.printStackTrace();%0A        %7D%0A
     

Revert to use previous version of orElseThrow()