/**
 * Copyright © 2016-2022 The Thingsboard Authors
 *
 * 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.
 */
package org.thingsboard.rule.engine.transform;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode;
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;

import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;

@Slf4j
@RuleNode(
        type = ComponentType.TRANSFORMATION,
        name = "rename keys",
        configClazz = TbRenameKeysNodeConfiguration.class,
        nodeDescription = "Renames msg data or metadata keys to the new key names selected in the key mapping.",
        nodeDetails = "If the key that is selected in the key mapping is missed in the selected msg source(data or metadata), it will be ignored." +
                " If the msg data is not a JSON object returns the incoming message as outbound message with <code>Failure</code> chain," +
                " otherwise returns transformed messages via <code>Success</code> chain",
        uiResources = {"static/rulenode/rulenode-core-config.js"},
        configDirective = "tbTransformationNodeRenameKeysConfig",
        icon = "find_replace"
)
public class TbRenameKeysNode implements TbNode {

    TbRenameKeysNodeConfiguration config;
    Map<String, String> renameKeysMapping;
    boolean fromMetadata;

    @Override
    public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
        this.config = TbNodeUtils.convert(configuration, TbRenameKeysNodeConfiguration.class);
        this.renameKeysMapping = config.getRenameKeysMapping();
        this.fromMetadata = config.isFromMetadata();
    }

    @Override
    public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException {
        TbMsgMetaData metaData = msg.getMetaData();
        String data = msg.getData();
        AtomicBoolean msgChanged = new AtomicBoolean(false);
        if (fromMetadata) {
            Map<String, String> metaDataMap = metaData.getData();
            renameKeysMapping.forEach((nameKey, newNameKey) -> {
                if (metaDataMap.containsKey(nameKey)) {
                    msgChanged.set(true);
                    metaDataMap.put(newNameKey, metaDataMap.get(nameKey));
                    metaDataMap.remove(nameKey);
                }
            });
            metaData = new TbMsgMetaData(metaDataMap);
        } else {
            JsonNode dataNode = JacksonUtil.toJsonNode(msg.getData());
            if (dataNode.isObject()) {
                ObjectNode msgData = (ObjectNode) dataNode;
                renameKeysMapping.forEach((nameKey, newNameKey) -> {
                    if (msgData.has(nameKey)) {
                        msgChanged.set(true);
                        msgData.set(newNameKey, msgData.get(nameKey));
                        msgData.remove(nameKey);
                    }
                });
                data = JacksonUtil.toString(msgData);
            }
        }
        if (msgChanged.get()) {
            ctx.tellSuccess(TbMsg.transformMsg(msg, msg.getType(), msg.getOriginator(), metaData, data));
        } else {
            ctx.tellSuccess(msg);
        }
    }

    @Override
    public void destroy() {
    }
}


@@ -1402,58 +1402,8 @@
 ion;
-%0Aimport java.util.concurrent.atomic.AtomicBoolean;
 %0A%0A@S
@@ -1836,159 +1836,9 @@
   %22 
-If the msg data is not a JSON object returns the incoming message as outbound message with %3Ccode%3EFailure%3C/code%3E chain,%22 +%0A                %22 otherwise r
+R
 etur
@@ -2777,23 +2777,17 @@
         
-AtomicB
+b
 oolean m
@@ -2802,32 +2802,13 @@
 d = 
-new AtomicBoolean(
 false
-)
 ;%0A  
@@ -2915,60 +2915,127 @@
     
-renameKeysMapping.forEach((nameKey, newNameKey) -%3E %7B
+for (Map.Entry%3CString, String%3E entry : renameKeysMapping.entrySet()) %7B%0A                String nameKey = entry.getKey();
 %0A   
@@ -3113,34 +3113,31 @@
   msgChanged
-.set(
+ = 
 true
-)
 ;%0A          
@@ -3162,26 +3162,32 @@
 Map.put(
-newNameKey
+entry.getValue()
 , metaDa
@@ -3279,34 +3279,32 @@
  %7D%0A            %7D
-);
 %0A            met
@@ -3550,60 +3550,131 @@
     
-renameKeysMapping.forEach((nameKey, newNameKey) -%3E %7B
+for (Map.Entry%3CString, String%3E entry : renameKeysMapping.entrySet()) %7B%0A                    String nameKey = entry.getKey();
 %0A   
@@ -3756,18 +3756,15 @@
 nged
-.set(
+ = 
 true
-)
 ;%0A  
@@ -3801,18 +3801,24 @@
 set(
-newNameKey
+entry.getValue()
 , ms
@@ -3926,18 +3926,16 @@
        %7D
-);
 %0A       
@@ -4031,14 +4031,8 @@
 nged
-.get()
 ) %7B%0A
@@ -4255,9 +4255,8 @@
     %7D%0A%7D%0A
-%0A

refactor code