/*-
 * ~~~~~~licensing~~~~~~
 * application-impl
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.platform.integration.applications.impl.project.item;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import ru.entaxy.esb.resources.EntaxyResource;
import ru.entaxy.platform.base.objects.EntaxyObject.OBJECT_RELATION;
import ru.entaxy.platform.integration.applications.EntaxyApplication;
import ru.entaxy.platform.integration.applications.impl.project.ApplicationProjectItemImpl;
import ru.entaxy.platform.objects.runtime.EntaxyRuntimeObject;
import ru.entaxy.platform.objects.runtime.EntaxyRuntimeObjectResource;
import ru.entaxy.platform.objects.runtime.EntaxyRuntimeRelation;

public class ItemFactory {

    static public final ItemFactory INSTANCE = new ItemFactory();

    static public final List<String> CONFIG_TYPES = Arrays.asList(new String[] {"entaxy.runtime.config"});

    private ItemFactory() {

    }

    public ApplicationProjectItemImpl createItem(EntaxyRuntimeObject runtimeObject,
            Map<String, ApplicationProjectItemImpl> currentStorage,
            final Map<String, ApplicationProjectItemImpl> linkedItems) {
        return createItem(runtimeObject, currentStorage, linkedItems, false, false);
    }
    
    public ApplicationProjectItemImpl createItem(EntaxyRuntimeObject runtimeObject,
            Map<String, ApplicationProjectItemImpl> currentStorage,
            final Map<String, ApplicationProjectItemImpl> linkedItems, boolean skipDependencies, boolean skipISR) {

        String fullId = runtimeObject.getObjectFullId();
        if (currentStorage.containsKey(fullId))
            return currentStorage.get(fullId);

        if (linkedItems.containsKey(fullId))
            return linkedItems.get(fullId);

        boolean isConfig = false;
        for (String type : CONFIG_TYPES)
            if (runtimeObject.getType().startsWith(type))
                isConfig = true;

        ApplicationProjectItemImpl result;
        if (isConfig) {
            result = ConfigItemImpl.create(runtimeObject);
        } else {
            result = ObjectItemImpl.create(runtimeObject);
        }

        if (!skipDependencies) {
            processDependencies(result, runtimeObject, currentStorage, linkedItems, skipISR, false);
        }

        for (ApplicationProjectItemImpl item : linkedItems.values())
            if (!currentStorage.containsKey(item.getId()))
                item.setRequired(true);

        return result;
    }

    public void processDependencies(ApplicationProjectItemImpl targetItem, EntaxyRuntimeObject runtimeObject,
            Map<String, ApplicationProjectItemImpl> currentStorage,
            final Map<String, ApplicationProjectItemImpl> linkedItems) {
        processDependencies(targetItem, runtimeObject, currentStorage, linkedItems, false, true);
    }
    
    public void processDependencies(ApplicationProjectItemImpl targetItem, EntaxyRuntimeObject runtimeObject,
            Map<String, ApplicationProjectItemImpl> currentStorage,
            final Map<String, ApplicationProjectItemImpl> linkedItems, boolean skipISR, boolean skipComposition) {
       
        List<EntaxyRuntimeRelation> relations;
        if (!skipISR) {
            // process object relations: incoming aggregations and compositions
            relations = runtimeObject.getIncomiingSubordRelations();
            for (EntaxyRuntimeRelation rel : relations) {
                EntaxyRuntimeObject object = rel.getMain();
                ApplicationProjectItemImpl item = createItem(object, currentStorage, linkedItems, false, false);
                targetItem.addRequired(item);
                item.updateScope(EntaxyApplication.ITEM_SCOPE.convert(rel.getScope()));
                if (!currentStorage.containsKey(item.getId()))
                    item.setRequired(true);
                if (!linkedItems.containsKey(item.getId()))
                    linkedItems.put(item.getId(), item);
            }
        }

        // process object relations: outgoing dependencies and uses
        relations = runtimeObject.getRelations(OBJECT_RELATION.DEPENDENCY);
        relations.addAll(runtimeObject.getRelations(OBJECT_RELATION.USE));
        for (EntaxyRuntimeRelation rel : relations) {
            EntaxyRuntimeObject object = rel.getMain();
            ApplicationProjectItemImpl item = createItem(object, currentStorage, linkedItems);
            item.updateScope(EntaxyApplication.ITEM_SCOPE.convert(rel.getScope()));
            targetItem.addRequired(item);
            if (!currentStorage.containsKey(item.getId()))
                item.setRequired(true);
            if (!linkedItems.containsKey(item.getId()))
                linkedItems.put(item.getId(), item);
        }
        
        if (!skipComposition) {
            //add default route as mandatory
            relations = runtimeObject.getRelations(OBJECT_RELATION.COMPOSITION);
            for (EntaxyRuntimeRelation rel : relations) {
                EntaxyRuntimeObject object = rel.getTarget();
                ApplicationProjectItemImpl item = createItem(object, currentStorage, linkedItems, false, true);
                item.updateScope(EntaxyApplication.ITEM_SCOPE.convert(rel.getScope()));
                item.addRequired(targetItem);
                if (!currentStorage.containsKey(item.getId()))
                    item.setRequired(true);
                if (!linkedItems.containsKey(item.getId()))
                    linkedItems.put(item.getId(), item);
            }
        }

        // process object resources
        List<EntaxyRuntimeObjectResource> resources = runtimeObject.getResources();
        for (EntaxyRuntimeObjectResource resource : resources) {
            ApplicationProjectItemImpl item = createItem(resource, currentStorage);
            item.updateScope(EntaxyApplication.ITEM_SCOPE.convert(resource.getScope()));
            targetItem.addRequired(item);
            if (!currentStorage.containsKey(item.getId()))
                item.setRequired(true);
            if (!linkedItems.containsKey(item.getId()))
                linkedItems.put(item.getId(), item);
        }
    }

    public ApplicationProjectItemImpl createItem(EntaxyRuntimeObjectResource objectResource,
            Map<String, ApplicationProjectItemImpl> currentStorage) {
        return new ResourceItemImpl(objectResource);
    }

    public ApplicationProjectItemImpl createItem(EntaxyResource entaxyResource,
            Map<String, ApplicationProjectItemImpl> currentStorage) {
        return new ResourceItemImpl(entaxyResource);
    }

}
