/*
 * 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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.ambari.server.api.resources;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.ambari.server.api.query.QueryImpl;
import org.apache.ambari.server.api.services.RootServiceComponentConfigurationService;
import org.apache.ambari.server.controller.internal.ClusterKerberosDescriptorResourceProvider;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
import org.apache.ambari.server.view.ViewRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Factory for creating resource instances.
 */
public class ResourceInstanceFactoryImpl implements ResourceInstanceFactory {


  /**
   * The logger.
   */
  private final static Logger LOG =
          LoggerFactory.getLogger(ResourceInstanceFactoryImpl.class);

  /**
   * Map of external resource definitions (added through views).
   */
  private final static Map<Resource.Type, ResourceDefinition> resourceDefinitions =
    new HashMap<>();


  @Override
  public ResourceInstance createResource(Resource.Type type, Map<Resource.Type, String> mapIds) {

    // this code changes hot name to lower case
    try {
      if (mapIds.containsKey(Resource.Type.Host)) {
        String hostName = mapIds.get(Resource.Type.Host);
        if (hostName != null) {
          mapIds.put(Resource.Type.Host, hostName.toLowerCase());
        }
      }
    } catch(Exception e) {
      LOG.error("Lowercase host name value in resource failed with error:" + e);
    }

    ResourceDefinition resourceDefinition = getResourceDefinition(type, mapIds);

    return new QueryImpl(mapIds, resourceDefinition, ClusterControllerHelper.getClusterController());
  }

  /**
   * Associate an external resource definition with a type.
   *
   * @param type        the resource type
   * @param definition  the resource definition
   */
  public static void addResourceDefinition(Resource.Type type, ResourceDefinition definition) {
    resourceDefinitions.put(type, definition);
  }

  /**
   * Get a resource definition for the given type.
   *
   * @param type    the resource type
   * @param mapIds  the map of ids
   *
   * @return the resource definition
   */
  public static ResourceDefinition getResourceDefinition(Resource.Type type, Map<Resource.Type, String> mapIds) {
    ResourceDefinition resourceDefinition;

    // Check to see if there is an external resource definition registered for the given type.
    if (resourceDefinitions.containsKey(type)) {
      return resourceDefinitions.get(type);
    }

    //todo: consider ResourceDependencyManager : Map<Resource.Type, ResourceDefinition>
    switch (type.getInternalType()) {
      case Cluster:
        resourceDefinition = new ClusterResourceDefinition();
        break;

      case Service:
        resourceDefinition = new ServiceResourceDefinition();
        break;

      case Host:
        resourceDefinition = mapIds.containsKey(Resource.Type.Cluster) ?
            new HostResourceDefinition() : new DetachedHostResourceDefinition();
        break;

      case Component:
        resourceDefinition = new ComponentResourceDefinition();
        break;

      case HostComponent:
        resourceDefinition = new HostComponentResourceDefinition();
        break;

      case Action:
        resourceDefinition = new ActionResourceDefinition();
        break;

      case Configuration:
        resourceDefinition = new ConfigurationResourceDefinition();
        break;

      case ServiceConfigVersion:
        resourceDefinition = new ServiceConfigVersionResourceDefinition();
        break;

      case Task:
        resourceDefinition = new TaskResourceDefinition();
        break;

      case User:
        resourceDefinition = new UserResourceDefinition();
        break;

      case UserAuthenticationSource:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.UserAuthenticationSource, "source", "sources");
        break;

      case Group:
        resourceDefinition = new GroupResourceDefinition();
        break;

      case Member:
        resourceDefinition = new MemberResourceDefinition();
        break;

      case Mpack:
        resourceDefinition = new MpackResourceDefinition();
        break;

      case Request:
        resourceDefinition = new RequestResourceDefinition();
        break;

      case Stack:
        resourceDefinition = new StackResourceDefinition();
        break;

      case StackVersion:
        resourceDefinition = new StackVersionResourceDefinition();
        break;

      case StackLevelConfiguration:
        resourceDefinition = new StackLevelConfigurationResourceDefinition();
        break;

      case StackService:
        resourceDefinition = new StackServiceResourceDefinition();
        break;

      case StackServiceComponent:
        resourceDefinition = new StackServiceComponentResourceDefinition();
        break;

      case StackServiceComponentDependency:
        resourceDefinition = new StackDependencyResourceDefinition();
        break;

      case StackConfiguration:
        resourceDefinition = new StackConfigurationResourceDefinition();
        break;

      case StackConfigurationDependency:
        resourceDefinition = new StackConfigurationDependencyResourceDefinition();
        break;

      case Extension:
        resourceDefinition = new ExtensionResourceDefinition();
        break;

      case ExtensionVersion:
        resourceDefinition = new ExtensionVersionResourceDefinition();
        break;

      case ExtensionLink:
        resourceDefinition = new ExtensionLinkResourceDefinition();
        break;

      case OperatingSystem:
        resourceDefinition = new OperatingSystemResourceDefinition();
        break;

      case Repository:
        resourceDefinition = new RepositoryResourceDefinition();
        break;

      case DRFeed:
        resourceDefinition = new FeedResourceDefinition();
        break;

      case DRTargetCluster:
        resourceDefinition = new TargetClusterResourceDefinition();
        break;

      case DRInstance:
        resourceDefinition = new InstanceResourceDefinition();
        break;

      case Workflow:
        resourceDefinition = new WorkflowResourceDefinition();
        break;

      case Job:
        resourceDefinition = new JobResourceDefinition();
        break;

      case TaskAttempt:
        resourceDefinition = new TaskAttemptResourceDefinition();
        break;

      case RootService:
        resourceDefinition = new RootServiceResourceDefinition();
        break;

      case RootServiceComponent:
        resourceDefinition = new RootServiceComponentResourceDefinition();
        break;

      case RootServiceComponentConfiguration:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.RootServiceComponentConfiguration,
            "configuration", "configurations",
            null, RootServiceComponentConfigurationService.DIRECTIVES_MAP);
        break;

      case RootServiceHostComponent:
        resourceDefinition = new RootServiceHostComponentResourceDefinition();
        break;

      case ConfigGroup:
        resourceDefinition = new ConfigGroupResourceDefinition();
        break;

      case RequestSchedule:
        resourceDefinition = new RequestScheduleResourceDefinition();
        break;

      case View:
        resourceDefinition = new ViewResourceDefinition();
        break;

      case ViewVersion:
        resourceDefinition = new ViewVersionResourceDefinition();
        break;

      case ViewInstance:
        String viewName = mapIds.get(Resource.Type.View);
        String version  = mapIds.get(Resource.Type.ViewVersion);

        Set<SubResourceDefinition> subResourceDefinitions = (viewName == null || version == null)  ?
            Collections.emptySet() :
            ViewRegistry.getInstance().getSubResourceDefinitions(viewName, version);

        resourceDefinition = new ViewInstanceResourceDefinition(subResourceDefinitions);
        break;

      case ViewURL:
        resourceDefinition = new ViewUrlResourceDefinition();
        break;

      case Blueprint:
        resourceDefinition = new BlueprintResourceDefinition();
        break;

      case Recommendation:
        resourceDefinition = new RecommendationResourceDefinition();
        break;

      case Validation:
        resourceDefinition = new ValidationResourceDefinition();
        break;

      case HostComponentProcess:
        resourceDefinition = new HostComponentProcessResourceDefinition();
        break;

      case Permission:
        resourceDefinition = new PermissionResourceDefinition();
        break;

      case Alert:
        resourceDefinition = new AlertResourceDefinition();
        break;

      case AlertDefinition:
        resourceDefinition = new AlertDefResourceDefinition();
        break;

      case AlertHistory:
        resourceDefinition = new AlertHistoryResourceDefinition();
        break;

      case AlertGroup:
        resourceDefinition = new AlertGroupResourceDefinition();
        break;

      case AlertTarget:
        resourceDefinition = new AlertTargetResourceDefinition();
        break;

      case AlertNotice:
        resourceDefinition = new AlertNoticeResourceDefinition();
        break;

      case AmbariPrivilege:
        resourceDefinition = new PrivilegeResourceDefinition(Resource.Type.AmbariPrivilege);
        break;

      case ClusterPrivilege:
        resourceDefinition = new PrivilegeResourceDefinition(Resource.Type.ClusterPrivilege);
        break;

      case ViewPrivilege:
        resourceDefinition = new PrivilegeResourceDefinition(Resource.Type.ViewPrivilege);
        break;

      case UserPrivilege:
        resourceDefinition = new PrivilegeResourceDefinition(Resource.Type.UserPrivilege);
        break;

      case GroupPrivilege:
        resourceDefinition = new PrivilegeResourceDefinition(Resource.Type.GroupPrivilege);
        break;

      case ViewPermission:
        resourceDefinition = new ViewPermissionResourceDefinition();
        break;

      case ClientConfig:
        resourceDefinition = new ClientConfigResourceDefinition();
        break;

      case LdapSyncEvent:
        resourceDefinition = new LdapSyncEventResourceDefinition();
        break;

      case RepositoryVersion:
        resourceDefinition = new RepositoryVersionResourceDefinition();
        break;

      case CompatibleRepositoryVersion:
        resourceDefinition = new CompatibleRepositoryVersionDefinition();
        break;

      case HostStackVersion:
        resourceDefinition = new ComponentStackVersionResourceDefinition(Resource.Type.HostStackVersion);
        break;

      case ClusterStackVersion:
        resourceDefinition = new ComponentStackVersionResourceDefinition(Resource.Type.ClusterStackVersion);
        break;

      case Upgrade:
        resourceDefinition = new UpgradeResourceDefinition();
        break;

      case UpgradeGroup:
        resourceDefinition = new SimpleResourceDefinition(
            Resource.Type.UpgradeGroup, "upgrade_group", "upgrade_groups",
            Resource.Type.UpgradeItem);
        break;

      case UpgradeItem:
        resourceDefinition = new SimpleResourceDefinition(
            Resource.Type.UpgradeItem, "upgrade_item", "upgrade_items", Resource.Type.Task);
        break;

      case UpgradeSummary:
        resourceDefinition = new SimpleResourceDefinition(
            Resource.Type.UpgradeSummary, "upgrade_summary", "upgrade_summary");
        break;

      case PreUpgradeCheck:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.PreUpgradeCheck, "rolling_upgrade_check", "rolling_upgrade_checks");
        break;

      case Stage:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.Stage, "stage", "stages", Resource.Type.Task);
        break;

      case StackArtifact:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.StackArtifact, "artifact", "artifacts");
        break;

      case Artifact:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.Artifact, "artifact", "artifacts");
        break;

      case Theme:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.Theme, "theme", "themes");
        break;

      case QuickLink:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.QuickLink, "quicklink", "quicklinks");
        break;

      case Widget:
        resourceDefinition = new WidgetResourceDefinition();
        break;

      case WidgetLayout:
        resourceDefinition = new WidgetLayoutResourceDefinition();
        break;

      case ActiveWidgetLayout:
        resourceDefinition = new ActiveWidgetLayoutResourceDefinition();
        break;

      case HostKerberosIdentity:
        resourceDefinition = new HostKerberosIdentityResourceDefinition();
        break;

      case KerberosDescriptor:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.KerberosDescriptor, "kerberos_descriptor", "kerberos_descriptors");
        break;

      case Credential:
        resourceDefinition = new CredentialResourceDefinition();
        break;

      case RoleAuthorization:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.RoleAuthorization, "authorization", "authorizations");
        break;

      case UserAuthorization:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.UserAuthorization, "authorization", "authorizations");
        break;

      case Setting:
        resourceDefinition = new SimpleResourceDefinition(Resource.Type.Setting, "setting", "settings");
        break;

      case VersionDefinition:
        resourceDefinition = new VersionDefinitionResourceDefinition();
        break;

      case ClusterKerberosDescriptor:
        resourceDefinition = new SimpleResourceDefinition(
            Resource.Type.ClusterKerberosDescriptor,
            "kerberos_descriptor",
            "kerberos_descriptors",
            null,
            Collections.singletonMap(SimpleResourceDefinition.DirectiveType.READ,
                Arrays.asList(
                    ClusterKerberosDescriptorResourceProvider.DIRECTIVE_EVALUATE_WHEN_CLAUSE,
                    ClusterKerberosDescriptorResourceProvider.DIRECTIVE_ADDITIONAL_SERVICES
                ))
        );
        break;

      case LoggingQuery:
        resourceDefinition = new LoggingResourceDefinition();
        break;

      case RemoteCluster:
        resourceDefinition = new RemoteClusterResourceDefinition();
        break;

      case Auth:
        resourceDefinition = new AuthResourceDefinition();
        break;

      default:
        throw new IllegalArgumentException("Unsupported resource type: " + type);
    }
    return resourceDefinition;
  }
}
