/*******************************************************************************
 * Copyright (C) 2021, 1C-Soft LLC and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     1C-Soft LLC - initial API and implementation
 *******************************************************************************/
package com.e1c.v8codestyle.md;

import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.COMMON_MODULE__CLIENT_MANAGED_APPLICATION;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.COMMON_MODULE__CLIENT_ORDINARY_APPLICATION;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.COMMON_MODULE__EXTERNAL_CONNECTION;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.COMMON_MODULE__GLOBAL;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.COMMON_MODULE__PRIVILEGED;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.COMMON_MODULE__RETURN_VALUES_REUSE;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.COMMON_MODULE__SERVER;
import static com._1c.g5.v8.dt.metadata.mdclass.MdClassPackage.Literals.COMMON_MODULE__SERVER_CALL;

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

import org.eclipse.emf.ecore.EStructuralFeature;

import com._1c.g5.v8.dt.metadata.mdclass.ReturnValuesReuse;
import com._1c.g5.v8.dt.metadata.mdclass.ScriptVariant;

/**
 * The Enumeration of common module types with title, suffix and feature settings.
 *
 * @author Dmitriy Marmyshev
 */
public enum CommonModuleTypes
{

    //@formatter:off
    SERVER(Messages.CommonModuleTypes_Server_module,
        new String[] { "", "" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, false,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, true,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    CLIENT(Messages.CommonModuleTypes_Client_module,
        new String[] { "Client", "Клиент" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, true,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, false,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, false,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    CLIENT_SERVER(Messages.CommonModuleTypes_Client_Server_module,
        new String[] { "ClientServer", "КлиентСервер" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, true,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, true,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    SERVER_CALL(Messages.CommonModuleTypes_Server_module_for_call_form_client,
        new String[] { "ServerCall", "ВызовСервера" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, false,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, false,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, true,
            COMMON_MODULE__EXTERNAL_CONNECTION, false,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    SERVER_CALL_CACHED(Messages.CommonModuleTypes_Server_module_for_call_form_client_with_cached_response,
        new String[] { "ServerCallCached", "ВызовСервераПовтИсп" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, false,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, false,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, true,
            COMMON_MODULE__EXTERNAL_CONNECTION, false,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DURING_SESSION)),

    CLIENT_GLOBAL(Messages.CommonModuleTypes_Client_global_module,
        new String[] { "Global", "Глобальный" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, true,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, false,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, false,
            COMMON_MODULE__GLOBAL, true,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    SERVER_GLOBAL(Messages.CommonModuleTypes_Server_global_module,
        new String[] { "ServerGlobal", "СерверГлобальный" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, false,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, true,
            COMMON_MODULE__GLOBAL, true,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    CLIENT_CACHED(Messages.CommonModuleTypes_Client_Cached_module,
        new String[] { "ClientCached", "КлиентПовтИсп" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, true,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, false,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, false,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DURING_SESSION)),

    SERVER_CACHED(Messages.CommonModuleTypes_Server_Cached_module,
        new String[] { "Cached", "ПовтИсп" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, false,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, true,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DURING_SESSION)),

    SERVER_FULL_ACCESS(Messages.CommonModuleTypes_Server_Full_access_module,
        new String[] { "FullAccess", "ПолныеПрава" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, false,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, false,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, false,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, true,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    SERVER_OVERRIDABLE(Messages.CommonModuleTypes_Server_Overridable_module,
        new String[] { "Overridable", "Переопределяемый" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, false,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, true,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    CLIENT_OVERRIDABLE(Messages.CommonModuleTypes_Client_Overridable_module,
        new String[] { "ClientOverridable", "КлиентПереопределяемый" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, true,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, false,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, false,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    SERVER_LOCALIZATION(Messages.CommonModuleTypes_Server_Localization_module,
        new String[] { "Localization", "Локализация" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, false,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, true,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, true,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE)),

    CLIENT_LOCALIZATION(Messages.CommonModuleTypes_Client_Localization_module,
        new String[] { "ClientLocalization", "КлиентЛокализация" }, //$NON-NLS-1$ //$NON-NLS-2$
        Map.of(
            COMMON_MODULE__CLIENT_MANAGED_APPLICATION, true,
            COMMON_MODULE__CLIENT_ORDINARY_APPLICATION, true,
            COMMON_MODULE__SERVER, false,
            COMMON_MODULE__SERVER_CALL, false,
            COMMON_MODULE__EXTERNAL_CONNECTION, false,
            COMMON_MODULE__GLOBAL, false,
            COMMON_MODULE__PRIVILEGED, false,
            COMMON_MODULE__RETURN_VALUES_REUSE, ReturnValuesReuse.DONT_USE));
    //@formatter:on

    private final String title;

    private final String[] nameSuffix;

    private final Map<EStructuralFeature, Object> featureValues;

    private Map<EStructuralFeature, Object> featureValuesMobileOnly;

    CommonModuleTypes(String title, String[] nameSuffix, Map<EStructuralFeature, Object> featureValues)
    {
        this.title = title;
        this.nameSuffix = nameSuffix;
        this.featureValues = featureValues;
    }

    /**
     * Gets the localizable title of the type.
     *
     * @return the title, cannot return {@code null}.
     */
    public String getTitle()
    {
        return title;
    }

    /**
     * Gets the dually-named name suffixes where first is English script variant and second is Russian script variant.
     *
     * @return the name suffixes, cannot return {@code null}.
     */
    public String[] getNameSuffixes()
    {
        return nameSuffix;
    }

    /**
     * Gets the name suffix for the specific script variant of the project.
     *
     * @param scriptVariant the script variant, cannot be {@code null}.
     * @return the name suffix, cannot return {@code null}.
     */
    public String getNameSuffix(ScriptVariant scriptVariant)
    {
        return nameSuffix[scriptVariant.getValue()];
    }

    /**
     * Gets the feature values for the type of common module.
     *
     * @param mobileOnly the mobile use only, if true - returns values without features that are not available
     * in mobile application.
     * @return the feature values, cannot return {@code null}.
     */
    public Map<EStructuralFeature, Object> getFeatureValues(boolean mobileOnly)
    {
        if (mobileOnly)
        {
            if (featureValuesMobileOnly == null)
            {
                Map<EStructuralFeature, Object> forMobile = new HashMap<>();
                forMobile.putAll(featureValues);
                forMobile.remove(COMMON_MODULE__EXTERNAL_CONNECTION);
                forMobile.remove(COMMON_MODULE__PRIVILEGED);
                featureValuesMobileOnly = Map.copyOf(forMobile);
            }
            return featureValuesMobileOnly;
        }
        return featureValues;
    }

    /**
     * Find type by common module name which is most closes with suffix.
     * If non of suffixes matched then {@link CommonModuleTypes#SERVER} returns.
     *
     * @param name the name of common module, cannot be {@code null}.
     * @param script the script variant, cannot be {@code null}.
     * @return the common module type, cannot return {@code null}.
     */
    public static CommonModuleTypes findClosestTypeByName(String name, ScriptVariant script)
    {
        List<CommonModuleTypes> result = new ArrayList<>();
        CommonModuleTypes[] values = values();
        for (int i = 0; i < values.length; i++)
        {
            CommonModuleTypes type = values[i];
            String suffix = type.getNameSuffix(script);
            if (!suffix.isEmpty() && name.endsWith(suffix))
            {
                result.add(type);
            }
        }
        if (result.isEmpty())
        {
            result.add(SERVER);
        }
        if (result.size() > 1)
        {
            Collections.sort(result, (o1, o2) -> o2.getNameSuffix(script).length() - o1.getNameSuffix(script).length());
        }
        return result.get(0);
    }

}
