/*-
 * ~~~~~~licensing~~~~~~
 * object-runtime-core
 * ==========
 * 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.objects.runtime.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.osgi.framework.BundleContext;

import ru.entaxy.platform.base.objects.EntaxyObject;
import ru.entaxy.platform.objects.runtime.EntaxyRuntimeObject;

public class ObjectRuntimeStorage {

    EntaxyRuntimeObjectContainerImpl ghostsContainer;

    Map<String, EntaxyRuntimeObjectContainerImpl> containers = new HashMap<>();

    Map<String, EntaxyRuntimeObjectImpl> objects = new HashMap<>();

    List<EntaxyRuntimeObject> objectsCache = new ArrayList<>();

    protected Object containersLock = new Object();

    protected BundleContext bundleContext;

    public ObjectRuntimeStorage() {
        super();
        ghostsContainer = new EntaxyRuntimeObjectContainerBundle();
    }

    public void add(EntaxyObject object) {

        if (!object.isGhost()) {

            long bundleId = object.getBundleInfo().getBundleId();
            EntaxyRuntimeObjectContainerImpl container = getCreateBundleContainer(bundleId);
            EntaxyRuntimeObjectImpl wrapped = wrap(object);
            container.add(wrapped);

        } else {

            EntaxyRuntimeObjectContainerImpl container = ghostsContainer;
            EntaxyRuntimeObjectImpl wrapped = wrap(object);
            container.add(wrapped);

        }

        rebuildCache();
    }

    public void update(EntaxyObject object) {
        add(object);
        rebuildCache();
    }

    public void remove(EntaxyObject object) {
        EntaxyRuntimeObjectImpl impl = objects.get(object.getObjectFullId());
        if (impl == null)
            return;

        impl.getContainerImpl().remove(impl);

        objects.remove(object.getObjectFullId());

        rebuildCache();
    }

    protected synchronized void rebuildCache() {
        this.objectsCache.clear();
        this.objects.values().stream().map(obj -> (EntaxyRuntimeObject) obj).forEach(obj -> this.objectsCache.add(obj));
    }

    protected EntaxyRuntimeObjectImpl wrap(EntaxyObject object) {
        if (objects.containsKey(object.getObjectFullId()))
            objects.get(object.getObjectFullId()).setWrappedObject(object);
        else
            objects.put(object.getObjectFullId(), new EntaxyRuntimeObjectImpl(object));
        return objects.get(object.getObjectFullId());
    }

    protected EntaxyRuntimeObjectContainerImpl getCreateBundleContainer(long bundleId) {
        String containerId = bundleId + "";
        synchronized (containersLock) {
            if (!containers.containsKey(containerId)) {
                EntaxyRuntimeObjectContainerImpl containerImpl = new EntaxyRuntimeObjectContainerBundle();
                containerImpl.bundle(bundleId, bundleContext);
                containers.put(containerImpl.getId(), containerImpl);
            }
        }
        return containers.get(containerId);
    }

    public BundleContext getBundleContext() {
        return bundleContext;
    }

    public void setBundleContext(BundleContext bundleContext) {
        this.bundleContext = bundleContext;
    }

}
