/*
 * 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.solr.core;

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

import org.apache.solr.common.util.StrUtils;
import org.apache.solr.request.SolrRequestHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 */
public final class RequestHandlers {
  public static Logger log = LoggerFactory.getLogger(RequestHandlers.class);

  protected final SolrCore core;

  final PluginRegistry<SolrRequestHandler> handlers;

  /**
   * Trim the trailing '/' if it's there, and convert null to empty string.
   * 
   * we want:
   *  /update/csv   and
   *  /update/csv/
   * to map to the same handler 
   * 
   */
  public static String normalize( String p )
  {
    if(p == null) return "";
    if( p.endsWith( "/" ) && p.length() > 1 )
      return p.substring( 0, p.length()-1 );
    
    return p;
  }
  
  public RequestHandlers(SolrCore core) {
      this.core = core;
    handlers =  new PluginRegistry<>(SolrRequestHandler.class, core);
  }

  /**
   * @return the RequestHandler registered at the given name 
   */
  public SolrRequestHandler get(String handlerName) {
    return handlers.get(normalize(handlerName));
  }

  /**
   * Handlers must be initialized before calling this function.  As soon as this is
   * called, the handler can immediately accept requests.
   * 
   * This call is thread safe.
   * 
   * @return the previous handler at the given path or null
   */
  public SolrRequestHandler register( String handlerName, SolrRequestHandler handler ) {
    String norm = normalize(handlerName);
    if (handler == null) {
      return handlers.remove(norm);
    }
    return handlers.put(norm, handler);
//    return register(handlerName, new PluginRegistry.PluginHolder<>(null, handler));
  }


  /**
   * Returns an unmodifiable Map containing the registered handlers
   */
  public PluginRegistry<SolrRequestHandler> getRequestHandlers() {
    return handlers;
  }


  /**
   * Read solrconfig.xml and register the appropriate handlers
   * 
   * This function should <b>only</b> be called from the SolrCore constructor.  It is
   * not intended as a public API.
   * 
   * While the normal runtime registration contract is that handlers MUST be initialized
   * before they are registered, this function does not do that exactly.
   *
   * This function registers all handlers first and then calls init() for each one.
   *
   * This is OK because this function is only called at startup and there is no chance that
   * a handler could be asked to handle a request before it is initialized.
   * 
   * The advantage to this approach is that handlers can know what path they are registered
   * to and what other handlers are available at startup.
   * 
   * Handlers will be registered and initialized in the order they appear in solrconfig.xml
   */

  void initHandlersFromConfig(SolrConfig config) {
    List<PluginInfo> implicits = ImplicitPlugins.getHandlers(core);
    // use link map so we iterate in the same order
    Map<String, PluginInfo> infoMap= new LinkedHashMap<>();
    //deduping implicit and explicit requesthandlers
    for (PluginInfo info : implicits) infoMap.put(info.name,info);
    for (PluginInfo info : config.getPluginInfos(SolrRequestHandler.class.getName())) infoMap.put(info.name, info);
    ArrayList<PluginInfo> infos = new ArrayList<>(infoMap.values());

    List<PluginInfo> modifiedInfos = new ArrayList<>();
    for (PluginInfo info : infos) {
      modifiedInfos.add(applyInitParams(config, info));
    }
    handlers.init(Collections.emptyMap(),core, modifiedInfos);
    handlers.alias(handlers.getDefault(), "");
    log.info("Registered paths: {}" , StrUtils.join(new ArrayList<>(handlers.keySet()) , ',' ));
    if(!handlers.alias( "/select","")){
      if(!handlers.alias( "standard","")){
        log.warn("no default request handler is registered (either '/select' or 'standard')");
      }
    }

  }

  private PluginInfo applyInitParams(SolrConfig config, PluginInfo info) {
    List<InitParams> ags = new ArrayList<>();
    String p = info.attributes.get(InitParams.TYPE);
    if(p!=null) {
      for (String arg : StrUtils.splitSmart(p, ',')) {
        if(config.getInitParams().containsKey(arg)) ags.add(config.getInitParams().get(arg));
        else log.warn("INVALID paramSet {} in requestHandler {}", arg, info.toString());
      }
    }
    for (InitParams args : config.getInitParams().values())
      if(args.matchPath(info.name)) ags.add(args);
    if(!ags.isEmpty()){
      info = info.copy();
      for (InitParams initParam : ags) {
        initParam.apply(info);
      }
    }
    return info;
  }

  public void close() {
    handlers.close();
  }
}







