/*
 * 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;

import static org.apache.ambari.server.agent.ExecutionCommand.KeyNames.JDK_LOCATION;

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.ObjectNotFoundException;
import org.apache.ambari.server.api.services.AmbariMetaInfo;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.utils.StageUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang.ArrayUtils;

import com.google.inject.Inject;

public class RootServiceResponseFactory extends
    AbstractRootServiceResponseFactory {

  private static final String RUNNING_STATE = "RUNNING";
  public static final String NOT_APPLICABLE = "NOT_APPLICABLE";
  @Inject
  private Configuration configs;
  
  @Inject
  private AmbariMetaInfo ambariMetaInfo;

  @Inject
  private AmbariManagementController managementController;
  
  
  @Override
  public Set<RootServiceResponse> getRootServices(RootServiceRequest request) throws ObjectNotFoundException {
    
    Set<RootServiceResponse> response;
    
    String serviceName = null;
    
    if (request != null)
      serviceName = request.getServiceName();

    if (serviceName != null) {
      RootService service;
      try {
        service = RootService.valueOf(serviceName);
      }
      catch (IllegalArgumentException ex) {
        throw new ObjectNotFoundException("Root service name: " + serviceName);
      }
      
      response = Collections.singleton(new RootServiceResponse(service.toString()));
    } else {
      response = new HashSet<>();
      
      for (RootService service: RootService.values())
        response.add(new RootServiceResponse(service.toString()));
    }    
    return response;
  }
  
  @Override
  public Set<RootServiceComponentResponse> getRootServiceComponents(
      RootServiceComponentRequest request) throws ObjectNotFoundException {
    Set<RootServiceComponentResponse> response = new HashSet<>();

    String serviceName = request.getServiceName();
    String componentName = request.getComponentName();
    RootService service;

    try {
      service = RootService.valueOf(serviceName);
    }
    catch (IllegalArgumentException ex) {
      throw new ObjectNotFoundException("Root service name: " + serviceName);
    }
    catch (NullPointerException np) {
      throw new ObjectNotFoundException("Root service name: null");
    }
    
    if (componentName != null) {
      RootComponent component;
      try {
        component = RootComponent.valueOf(componentName);
        if (!ArrayUtils.contains(service.getComponents(), component))
          throw new ObjectNotFoundException("No component name: " + componentName + "in service: " + serviceName);
      }
      catch (IllegalArgumentException ex) {
        throw new ObjectNotFoundException("Component name: " + componentName);
      }
      response = Collections.singleton(new RootServiceComponentResponse(serviceName, component.toString(),
                                       getComponentVersion(componentName, null),
                                       getComponentProperties(componentName)));
    } else {
    
      for (RootComponent component: service.getComponents())
        response.add(new RootServiceComponentResponse(serviceName, component.toString(),
                     getComponentVersion(component.name(), null),
                     getComponentProperties(component.name())));
      }
    return response;
  }

  private String getComponentVersion(String componentName, HostResponse host) {
    RootComponent component = RootComponent.valueOf(componentName);
    String componentVersion;
      
    switch (component) {
      case AMBARI_SERVER:
        componentVersion = ambariMetaInfo.getServerVersion();
        break;
      case AMBARI_AGENT:
        //Could be defined on level of host component
        
        if (host == null)
          componentVersion = NOT_APPLICABLE;
        else
          componentVersion = host.getAgentVersion().getVersion();
        break;

      default:
        componentVersion = null;
      }
      
    return componentVersion;
  }
  
  private Map<String, String> getComponentProperties(String componentName){
    
    Map<String, String> response;
    Set<String> propertiesToHideInResponse;
    RootComponent component = null;

    if (componentName != null) {
      component = RootComponent.valueOf(componentName);
      
      switch (component) {
      case AMBARI_SERVER:
        response = configs.getAmbariProperties();
        response.put(JDK_LOCATION, managementController.getJdkResourceUrl());
        response.put("java.version", System.getProperty("java.specification.version"));
        propertiesToHideInResponse = configs.getPropertiesToBlackList();
        for(String key : propertiesToHideInResponse) {
      	  response.remove(key);
      	}
        break;

      default:
        response = Collections.emptyMap();
      }
    }
    else
      response = Collections.emptyMap();

    return response;
  }

  @Override
  public Set<RootServiceHostComponentResponse> getRootServiceHostComponent(RootServiceHostComponentRequest request, Set<HostResponse> hosts) throws AmbariException {
    Set<RootServiceHostComponentResponse> response = new HashSet<>();

    String serviceName = request.getServiceName();
    Set<RootServiceComponentResponse> rootServiceComponents =
        getRootServiceComponents(new RootServiceComponentRequest(serviceName, request.getComponentName()));

    //Cartesian product with hosts and components
    for (RootServiceComponentResponse component : rootServiceComponents) {
      
      Set<HostResponse> filteredHosts = new HashSet<>(hosts);
      
      //Make some filtering of hosts if need
      if (component.getComponentName().equals(RootComponent.AMBARI_SERVER.name())) {
        CollectionUtils.filter(filteredHosts, new Predicate() {
          @Override
          public boolean evaluate(Object arg0) {
            HostResponse hostResponse = (HostResponse) arg0;
            return hostResponse.getHostname().equals(StageUtils.getHostName());
          }
        });
      }
      
      for (HostResponse host : filteredHosts) {
        String state;
        if (component.getComponentName().equals(RootComponent.AMBARI_SERVER.name())) {
          state = RUNNING_STATE;
        } else {
          state = host.getHostState().toString();
        }
        String componentVersion = getComponentVersion(component.getComponentName(), host);
        response.add(new RootServiceHostComponentResponse(serviceName, host.getHostname(), component.getComponentName(), state, componentVersion, component.getProperties()));
      }
    }
    
    return response;
  }
}
