/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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.apache.ivyde.eclipse.resolvevisualizer.model;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.apache.ivy.core.module.id.ModuleRevisionId;

/**
 * Assists in the further separation of concerns between the view and the Ivy resolve report. The view looks at the
 * IvyNode in a unique way that can lead to expensive operations if we do not achieve this separation.
 */
public class IvyNodeElement {
    private ModuleRevisionId moduleRevisionId;
    private boolean evicted = false;
    private int depth = Integer.MAX_VALUE / 10;
    private final Collection<IvyNodeElement> dependencies = new HashSet<>();
    private final Collection<IvyNodeElement> callers = new HashSet<>();
    private Collection<IvyNodeElement> conflicts = new HashSet<>();
    private int hash;

    /**
     * The caller configurations that caused this node to be reached in the resolution, grouped by caller.
     */
    private final Map<IvyNodeElement, String[]> callerConfigurationMap = new HashMap<>();

    /**
     * We try to avoid building the list of this nodes deep dependencies by storing them in this cache by depth level.
     */
    private IvyNodeElement[] deepDependencyCache;

    public boolean equals(Object obj) {
        if (obj instanceof IvyNodeElement) {
            IvyNodeElement elem = (IvyNodeElement) obj;
            if (elem.getOrganization().equals(getOrganization()) && elem.getName().equals(getName())
                    && elem.getRevision().equals(getRevision())) {
                return true;
            }
        }
        return false;
    }

    public int hashCode() {
        if (hash == 0) {
            // CheckStyle:MagicNumber| OFF
            hash = 31;
            hash = hash * 13 + getModuleRevisionId().hashCode();
            hash = hash * 13 + Arrays.hashCode(getDeepDependencies());
            hash = hash * 13 + Arrays.hashCode(getConflicts());
            hash = hash * 13 + Arrays.hashCode(getCallers());
            // CheckStyle:MagicNumber| ON
        }
        return hash;
    }

    public IvyNodeElement[] getDependencies() {
        return dependencies.toArray(new IvyNodeElement[dependencies.size()]);
    }

    /**
     * Recursive dependency retrieval.
     *
     * @return The array of nodes that represents a node's immediate and transitive dependencies down to an arbitrary
     *         depth.
     */
    public IvyNodeElement[] getDeepDependencies() {
        if (deepDependencyCache == null) {
            deepDependencyCache = getDeepDependencies(this).toArray(new IvyNodeElement[] {});
        }
        return deepDependencyCache;
    }

    /**
     * Recursive dependency retrieval
     *
     * @param node IvyNodeElement
     * @return Collection&lt;IvyNodeElement&gt;
     */
    private Collection<IvyNodeElement> getDeepDependencies(IvyNodeElement node) {
        Collection<IvyNodeElement> deepDependencies = new HashSet<>();
        deepDependencies.add(node);

        for (IvyNodeElement directDependency : node.getDependencies()) {
            deepDependencies.addAll(getDeepDependencies(directDependency));
        }

        return deepDependencies;
    }

    /**
     * @param caller IvyNodeElement
     * @return An array of configurations by which this module was resolved
     */
    public String[] getCallerConfigurations(IvyNodeElement caller) {
        return callerConfigurationMap.get(caller);
    }

    public void setCallerConfigurations(IvyNodeElement caller, String[] configurations) {
        callerConfigurationMap.put(caller, configurations);
    }

    public String getOrganization() {
        return moduleRevisionId.getOrganisation();
    }

    public String getName() {
        return moduleRevisionId.getName();
    }

    public String getRevision() {
        return moduleRevisionId.getRevision();
    }

    public boolean isEvicted() {
        return evicted;
    }

    public void setEvicted(boolean evicted) {
        this.evicted = evicted;
    }

    public int getDepth() {
        return depth;
    }

    /**
     * Set this node's depth and recursively update the node's children to relative to the new value.
     *
     * @param depth int
     */
    public void setDepth(int depth) {
        this.depth = depth;
        for (IvyNodeElement dependency : dependencies) {
            dependency.setDepth(depth + 1);
        }
    }

    public IvyNodeElement[] getConflicts() {
        return conflicts.toArray(new IvyNodeElement[conflicts.size()]);
    }

    public void setConflicts(Collection<IvyNodeElement> conflicts) {
        this.conflicts = conflicts;
    }

    public ModuleRevisionId getModuleRevisionId() {
        return moduleRevisionId;
    }

    public void setModuleRevisionId(ModuleRevisionId moduleRevisionId) {
        this.moduleRevisionId = moduleRevisionId;
    }

    public void addCaller(IvyNodeElement caller) {
        callers.add(caller);
        caller.dependencies.add(this);
    }

    public IvyNodeElement[] getCallers() {
        return callers.toArray(new IvyNodeElement[callers.size()]);
    }
}

@@ -2236,20 +2236,23 @@
         
-if (
+return 
 elem.get
@@ -2395,54 +2395,9 @@
 n())
-) %7B%0A                return true;%0A            %7D
+;
 %0A   

Simplify logic