/*
 * Copyright (c) 2019 Livio, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of the Livio Inc. nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.smartdevicelink.managers.lifecycle;

import android.util.Log;

import com.smartdevicelink.marshal.JsonRPCMarshaller;
import com.smartdevicelink.protocol.ProtocolMessage;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.RPCStruct;
import com.smartdevicelink.util.DebugTool;
import com.smartdevicelink.util.Version;

import java.lang.reflect.InvocationTargetException;
import java.util.Hashtable;
import java.util.Set;

/**
 * Converts a protocol message into an RPC.
 * Built to reduce boiler plate code for each RPC added.
 */
public class RpcConverter {

    private static final String TAG = "RpcConverter";

    private static final String RPC_PACKAGE             = "com.smartdevicelink.proxy.rpc.";
    private static final String RESPONSE_KEY            = "Response";
    private static final String GENERIC_RESPONSE_STRING = FunctionID.GENERIC_RESPONSE.toString();

    /**
     * Extracts the RPC out of the payload of a given protocol message
     * @param message protocolMessage that has the RPC in the payload
     * @param protocolVersion RPC spec version that should be used to create RPC
     * @return the extracted RPC
     */
    public static RPCMessage extractRpc(ProtocolMessage message, Version protocolVersion){
        Hashtable<String, Object> tempTable = convertProtocolMessage(message, protocolVersion);
        if(tempTable != null){
            try{
                return convertTableToRpc(tempTable);
            }catch (Exception e){
                DebugTool.logError("Error converting RPC",e);
            }
        }
        return null;
    }

    static Hashtable<String, Object> convertProtocolMessage(ProtocolMessage message, Version protocolVersion){
        Hashtable<String, Object> hash = new Hashtable<>();
        if (protocolVersion!= null && protocolVersion.getMajor() > 1) {

            Hashtable<String, Object> hashTemp = new Hashtable<>();
            hashTemp.put(RPCMessage.KEY_CORRELATION_ID, message.getCorrID());
            if (message.getJsonSize() > 0) {
                final Hashtable<String, Object> mhash = JsonRPCMarshaller.unmarshall(message.getData());
                if (mhash != null) {
                    hashTemp.put(RPCMessage.KEY_PARAMETERS, mhash);
                }
            }

            String functionName = FunctionID.getFunctionName(message.getFunctionID());

            if (functionName != null) {
                hashTemp.put(RPCMessage.KEY_FUNCTION_NAME, functionName);
            } else {
                DebugTool.logWarning("Dispatch Incoming Message - function name is null unknown RPC.  FunctionId: " + message.getFunctionID());
                return null;
            }

            if (message.getRPCType() == 0x00) {
                hash.put(RPCMessage.KEY_REQUEST, hashTemp);
            } else if (message.getRPCType() == 0x01) {
                hash.put(RPCMessage.KEY_RESPONSE, hashTemp);
            } else if (message.getRPCType() == 0x02) {
                hash.put(RPCMessage.KEY_NOTIFICATION, hashTemp);
            }

            if (message.getBulkData() != null) hash.put(RPCStruct.KEY_BULK_DATA, message.getBulkData());
            if (message.getPayloadProtected()) hash.put(RPCStruct.KEY_PROTECTED, true);

            return hash;
        } else {
            return JsonRPCMarshaller.unmarshall(message.getData());
        }
    }


    public static RPCMessage convertTableToRpc(Hashtable<String,Object> rpcHashTable){

        Hashtable<String,Object> params;
        if(rpcHashTable.containsKey((RPCMessage.KEY_RESPONSE))){
            params = (Hashtable)rpcHashTable.get((RPCMessage.KEY_RESPONSE));
        }else if(rpcHashTable.containsKey((RPCMessage.KEY_NOTIFICATION))){
            params = (Hashtable)rpcHashTable.get((RPCMessage.KEY_NOTIFICATION));
        }else if(rpcHashTable.containsKey((RPCMessage.KEY_REQUEST))){
            params = (Hashtable)rpcHashTable.get((RPCMessage.KEY_REQUEST));
        }else{
            DebugTool.logError(TAG + " Corrupted RPC table.");
            return null;
        }

        if(DebugTool.isDebugEnabled()) {
            if (params != null) {
                Set<String> keySet = params.keySet();
                for (String key : keySet) {
                    Log.i(TAG, key + "  -  " + params.get(key));
                }
            }
        }

        if(params.containsKey(RPCMessage.KEY_FUNCTION_NAME)){
            StringBuilder rpcClassName = new StringBuilder();
            String functionName = (String)params.get(RPCMessage.KEY_FUNCTION_NAME);
            rpcClassName.append(RPC_PACKAGE);
            rpcClassName.append (functionName);

            if(rpcHashTable.containsKey(RPCMessage.KEY_RESPONSE)
                    && !GENERIC_RESPONSE_STRING.equals(functionName)){
                rpcClassName.append(RESPONSE_KEY);
            }

            DebugTool.logInfo(TAG + " Attempting to create " + rpcClassName.toString());
            try {
                Class rpcClass = Class.forName(rpcClassName.toString());
                if(rpcClass != null){
                    java.lang.reflect.Constructor rpcConstructor =  rpcClass.getConstructor(Hashtable.class);
                    if(rpcConstructor != null){
                        return (RPCMessage)rpcConstructor.newInstance(rpcHashTable);
                    }
                }
            } catch (ClassNotFoundException e) {
            } catch (NoSuchMethodException e) {
            } catch (IllegalAccessException e) {
            } catch (InstantiationException e) {
            } catch (InvocationTargetException e) {
            } catch (ClassCastException e){

            }
        }else{
            DebugTool.logError(TAG + " Unable to parse into RPC");
        }

        return null;
    }


}


@@ -6164,24 +6164,171 @@
 TION_NAME);%0A
+            if(FunctionID.SHOW_CONSTANT_TBT.toString().equals(functionName)) %7B%0A                    functionName = %22ShowConstantTbt%22;%0A            %7D%0A
             
@@ -7104,32 +7104,39 @@
                %7D
+ else %7B
 %0A            %7D c
@@ -7136,190 +7136,121 @@
     
-%7D catch (ClassNotFoundException e) %7B%0A            %7D catch (NoSuchMethodException e) %7B%0A            %7D catch (IllegalAccessException e) %7B%0A            %7D catch (InstantiationException e) %7B
+        DebugTool.logError(TAG + %22 Java class cannot be found for %22 + rpcClassName.toString());%0A                %7D
 %0A   
@@ -7267,32 +7267,16 @@
  catch (
-InvocationTarget
 Exceptio
@@ -7298,40 +7298,76 @@
     
-%7D catch (ClassCastException e)%7B%0A
+    DebugTool.logError(%22RPCConverter was unable to process RPC%22, e);
 %0A   

Fix tbt name mismatch in RPCConverter (#1018)