package com.atlassian.bitbucket.scm;

import com.atlassian.plugin.StateAware;
import com.atlassian.plugin.module.ModuleFactory;

/**
 * {@code ModuleDescriptor} which interprets {@code &lt;scm/&gt;} tags in {@code atlassian-plugin.xml}.
 * <code><pre>
 * &lt;scm id="scm" class="com.example.scm.CustomScm" weight="100"/&gt;
 * </pre></code>
 * Because this module descriptor uses the {@code ModuleFactory} to create the {@link Scm} instance, the "class" for the
 * {@link Scm} may also reference a bean:
 * <code><pre>
 * &lt;component id="customScm" class="com.example.scm.CustomScm"/&gt;
 * &lt;scm id="scm" class="bean:customScm"/&gt;
 * </pre></code>
 * Using the "bean:" approach allows plugins to declare their custom {@link Scm} implementations as public components,
 * allowing other plugins to {@code component-import} them and use them.
 * <p>
 * This module descriptor caches the {@link Scm} instance it creates, and returns the same instance on subsequent calls
 * until the module is disabled or uninstalled. As a result, as documented on the {@link Scm} interface, implementations
 * are <i>required</i> to be thread-safe, because the same instance will be used by multiple threads.
 * <p>
 * SCMs are {@link #getWeight() weighted}, which is used to control the order they are returned in when retrieving
 * {@link ScmService#getAvailable() available SCMs}. The higher the weight, the <i>later</i> in the set they will
 * appear. Additionally, if multiple SCMs are registered with the same {@link Scm#getId() ID}, the one with the lowest
 * weight will be used.
 */
public class ScmModuleDescriptor extends BaseWeightedModuleDescriptor<Scm> {

    public static final String XML_ELEMENT_NAME = "scm";

    private volatile Scm module;

    public ScmModuleDescriptor(ModuleFactory moduleFactory) {
        super(moduleFactory, 1000);
    }

    /**
     * Clears the cached {@link Scm}.
     */
    @Override
    public void disabled() {
        if (module instanceof StateAware) {
            ((StateAware) module).disabled();
        }
        module = null;

        super.disabled();
    }

    /**
     * Retrieves the {@link Scm} instance for this descriptor.
     * <p>
     * The first time this method is called after an SCM plugin is installed or enabled, the {@link Scm} will be
     * retrieved from the {@code ModuleFactory} and cached. Subsequent calls will retrieve the same instance.
     *
     * @return the {@link Scm} instance
     */
    @Override
    public Scm getModule() {
        if (module == null) {
            module = moduleFactory.createModule(moduleClassName, this);
        }
        return module;
    }
}
