/*
 * 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.controller.internal;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
import org.apache.ambari.server.controller.spi.NoSuchResourceException;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.RequestStatus;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.ResourceAlreadyExistsException;
import org.apache.ambari.server.controller.spi.SystemException;
import org.apache.ambari.server.controller.spi.UnsupportedPropertyException;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.ambari.server.state.StackInfo;
import org.apache.ambari.server.state.ThemeInfo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ThemeArtifactResourceProvider extends AbstractControllerResourceProvider {

  public static final String STACK_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "stack_name");
  public static final String STACK_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "stack_version");
  public static final String STACK_SERVICE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "service_name");
  public static final String THEME_FILE_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "file_name");
  public static final String THEME_DEFAULT_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "default");
  public static final String THEME_DATA_PROPERTY_ID = PropertyHelper.getPropertyId("ThemeInfo", "theme_data");

  /**
   * primary key fields
   */
  public static Set<String> pkPropertyIds = new HashSet<String>();
  /**
   * map of resource type to fk field
   */
  public static Map<Resource.Type, String> keyPropertyIds =
    new HashMap<Resource.Type, String>();

  /**
   * resource properties
   */
  public static Set<String> propertyIds = new HashSet<String>();

  static {
    keyPropertyIds.put(Resource.Type.Theme, THEME_FILE_NAME_PROPERTY_ID);
    keyPropertyIds.put(Resource.Type.Stack, STACK_NAME_PROPERTY_ID);
    keyPropertyIds.put(Resource.Type.StackVersion, STACK_VERSION_PROPERTY_ID);
    keyPropertyIds.put(Resource.Type.StackService, STACK_SERVICE_NAME_PROPERTY_ID);

    pkPropertyIds.add(THEME_FILE_NAME_PROPERTY_ID);

    // resource properties
    propertyIds.add(STACK_NAME_PROPERTY_ID);
    propertyIds.add(STACK_VERSION_PROPERTY_ID);
    propertyIds.add(STACK_SERVICE_NAME_PROPERTY_ID);
    propertyIds.add(THEME_FILE_NAME_PROPERTY_ID);
    propertyIds.add(THEME_DEFAULT_PROPERTY_ID);
    propertyIds.add(THEME_DATA_PROPERTY_ID);
  }

  /**
   * Create a  new resource provider for the given management controller.
   *
   * @param managementController the management controller
   */
  protected ThemeArtifactResourceProvider(AmbariManagementController managementController) {
    super(propertyIds, keyPropertyIds, managementController);
  }

  @Override
  public RequestStatus createResources(Request request) throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException,
      NoSuchParentResourceException {
    throw new UnsupportedOperationException("Creating of themes is not supported");
  }

  @Override
  public Set<Resource> getResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException,
      NoSuchParentResourceException {

    Set<Resource> resources = new LinkedHashSet<Resource>();

    resources.addAll(getThemes(request, predicate));
    // add other artifacts types here

    if (resources.isEmpty()) {
      throw new NoSuchResourceException(
        "The requested resource doesn't exist: Themes not found, " + predicate);
    }

    return resources;

  }

  @Override
  public RequestStatus updateResources(Request request, Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException,
      NoSuchParentResourceException {
    throw new UnsupportedOperationException("Updating of themes is not supported");
  }

  @Override
  public RequestStatus deleteResources(Predicate predicate) throws SystemException, UnsupportedPropertyException, NoSuchResourceException, NoSuchParentResourceException {
    throw new UnsupportedOperationException("Deleting of themes is not supported");
  }

  private Set<Resource> getThemes(Request request, Predicate predicate) throws NoSuchParentResourceException,
    NoSuchResourceException, UnsupportedPropertyException, SystemException {

    Set<Resource> resources = new LinkedHashSet<Resource>();
    for (Map<String, Object> properties : getPropertyMaps(predicate)) {
      String themeFileName = (String) properties.get(THEME_FILE_NAME_PROPERTY_ID);


      String stackName = (String) properties.get(STACK_NAME_PROPERTY_ID);
      String stackVersion = (String) properties.get(STACK_VERSION_PROPERTY_ID);
      String stackService = (String) properties.get(STACK_SERVICE_NAME_PROPERTY_ID);

      StackInfo stackInfo;
      try {
        stackInfo = getManagementController().getAmbariMetaInfo().getStack(stackName, stackVersion);
      } catch (AmbariException e) {
        throw new NoSuchParentResourceException(String.format(
          "Parent stack resource doesn't exist: stackName='%s', stackVersion='%s'", stackName, stackVersion));
      }

      List<ServiceInfo> serviceInfoList = new ArrayList<ServiceInfo>();

      if (stackService == null) {
        serviceInfoList.addAll(stackInfo.getServices());
      } else {
        ServiceInfo service = stackInfo.getService(stackService);
        if (service == null) {
          throw new NoSuchParentResourceException(String.format(
            "Parent stack/service resource doesn't exist: stackName='%s', stackVersion='%s', serviceName='%s'",
            stackName, stackVersion, stackService));
        }
        serviceInfoList.add(service);
      }

      for (ServiceInfo serviceInfo : serviceInfoList) {
        List<ThemeInfo> serviceThemes = new ArrayList<ThemeInfo>();
        if (themeFileName != null) {
          LOG.debug("Getting themes from service {}, themes = {}", serviceInfo.getName(), serviceInfo.getThemesMap());
          serviceThemes.add(serviceInfo.getThemesMap().get(themeFileName));
        } else {
          for (ThemeInfo themeInfo : serviceInfo.getThemesMap().values()) {
            if (themeInfo.getIsDefault()) {
              serviceThemes.add(0, themeInfo);
            } else {
              serviceThemes.add(themeInfo);
            }
          }
        }

        List<Resource> serviceResources = new ArrayList<Resource>();
        for (ThemeInfo themeInfo : serviceThemes) {
          Resource resource = new ResourceImpl(Resource.Type.Theme);
          Set<String> requestedIds = getRequestPropertyIds(request, predicate);
          setResourceProperty(resource, THEME_FILE_NAME_PROPERTY_ID, themeInfo.getFileName(), requestedIds);
          setResourceProperty(resource, THEME_DEFAULT_PROPERTY_ID, themeInfo.getIsDefault(), requestedIds);
          setResourceProperty(resource, THEME_DATA_PROPERTY_ID, themeInfo.getThemeMap(), requestedIds);
          setResourceProperty(resource, STACK_NAME_PROPERTY_ID, stackName, requestedIds);
          setResourceProperty(resource, STACK_VERSION_PROPERTY_ID, stackVersion, requestedIds);
          setResourceProperty(resource, STACK_SERVICE_NAME_PROPERTY_ID, serviceInfo.getName(), requestedIds);
          serviceResources.add(resource);
        }

        resources.addAll(serviceResources);

      }
    }
    return resources;
  }

  @Override
  protected Set<String> getPKPropertyIds() {
    return null;
  }
}
