package com.thinkbiganalytics.nifi.rest.model.flow;

/*-
 * #%L
 * thinkbig-nifi-flow-visitor-model
 * %%
 * Copyright (C) 2017 ThinkBig Analytics
 * %%
 * 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%
 */

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

import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * JSON doesnt like circular dependencies. NifiFlowProcessor has a graph of objects that point up and down the tree. All attempts to deserialize the object with Jackson annotations on the
 * NifiFlowProcessor failed For Example (@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")) Reference https://github.com/FasterXML/jackson-databind/issues/687 To
 * get around this ids are stored instead of the entire object in the graph. This class will reattach the real objects back so you can then traverse the graph.
 */
public class NifiFlowDeserializer {

    private static final Logger log = LoggerFactory.getLogger(NifiFlowDeserializer.class);

    /**
     * Construct the graph by reattaching the source/dest by looking at the ids
     *
     * @param group the graph of the flow to deserialize and connect and convert the id references to objects
     */
    public static void constructGraph(NifiFlowProcessGroup group) {
        if (group != null) {
            log.info("constructing graph for {} ({})", group.getName(), group.getId());
            //reattach the ids back to the graph of objects
            Map<String, NifiFlowProcessor> processorMap = group.getProcessorMap();

            processorMap.values().forEach(processor -> {
                Set<NifiFlowProcessor> sources = processor.getSourceIds().stream().map(sourceId -> processorMap.get(sourceId)).collect(Collectors.toSet());
                Set<NifiFlowProcessor> destinations = processor.getDestinationIds().stream().map(destId -> processorMap.get(destId)).collect(Collectors.toSet());

                processor.setSources(sources);
                processor.setDestinations(destinations);
                processor.setProcessGroup(group);
            });

            //reattach to the group
            group.setStartingProcessors(group.getStartingProcessors().stream().map(processor -> processorMap.get(processor.getId())).collect(Collectors.toSet()));

            group.setEndingProcessors(
                group.getEndingProcessors().values().stream().map(processor -> processorMap.get(processor.getId())).collect(Collectors.toMap(processor -> processor.getId(), processor -> processor)));


        }
    }

    /**
     * Remove any circular graph dependencies and replace it with id references
     * These can be reconstructed later via the {@link this#constructGraph(NifiFlowProcessGroup)}
     *
     * @param group the graph of the flow to serialize
     */
    public static void prepareForSerialization(NifiFlowProcessGroup group) {
        //ensure the objects are cleared of references
        Map<String, NifiFlowProcessor> processorMap = group.getProcessorMap();

        processorMap.values().forEach(processor -> {
            processor.setSources(null);
            processor.setDestinations(null);
            processor.setProcessGroup(null);
        });

    }


}
