/*
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community 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://opensource.org/licenses/ecl2.txt
 *
 * 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.opencastproject.solr;

import org.opencastproject.solr.internal.EmbeddedSolrServerWrapper;
import org.opencastproject.util.PathSupport;

import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.core.OpencastSolrConfig;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrCore;
import org.apache.solr.schema.IndexSchema;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.net.URL;

/**
 * Factory class that will create clients to solr server instances that are either running inside the local virtual
 * machine or remotely.
 */
public final class SolrServerFactory {

  /** Log facility */
  private static final Logger logger = LoggerFactory.getLogger(SolrServerFactory.class);

  /** Configuration key for default embedded solr location */
  public static final String PROP_GLOBAL_SOLR_DIR = "org.opencastproject.solr.dir";

  /** Default embedded solr location (relative to KARAF_DATA) */
  public static final String DEFAULT_GLOBAL_SOLR_DIR = "solr-indexes";

  /** Disallow construction of this utility class */
  private SolrServerFactory() {
  }

  /**
   * Constructor. Prepares solr connection.
   *
   * @param solrDir
   *          The directory of the solr instance.
   * @param dataDir
   *          The directory of the solr index data.
   */
  public static SolrServer newEmbeddedInstance(File solrDir, File dataDir) throws SolrServerException {
    try {
      final CoreContainer cc = new CoreContainer(solrDir.getAbsolutePath());
      SolrConfig config = new OpencastSolrConfig(solrDir.getAbsolutePath(), SolrConfig.DEFAULT_CONF_FILE, null);
      IndexSchema schema = new IndexSchema(config, solrDir + "/conf/schema.xml", null);
      SolrCore core0 = new SolrCore(null, dataDir.getAbsolutePath(), config, schema, null);
      cc.register("core0", core0, false);
      return new EmbeddedSolrServerWrapper(cc, "core0");
    } catch (Exception e) {
      throw new SolrServerException(e);
    }
  }

  /**
   * Constructor. Prepares solr connection.
   *
   * @param url
   *          the connection url to the solr server
   */
  public static SolrServer newRemoteInstance(URL url) {
    try {
      CommonsHttpSolrServer server = new CommonsHttpSolrServer(url);
      server.setSoTimeout(5000);
      server.setConnectionTimeout(5000);
      server.setDefaultMaxConnectionsPerHost(100);
      server.setMaxTotalConnections(100);
      server.setFollowRedirects(false); // defaults to false
      server.setAllowCompression(true);
      server.setMaxRetries(1); // defaults to 0. > 1 not recommended.
      server.setParser(new XMLResponseParser()); // binary parser is used by default
      return server;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Shuts down a solr server or, in the case of a connection to a remote server, closes the connection.
   *
   * @param solrServer
   *          the solr server instance
   */
  public static void shutdown(SolrServer solrServer) {
    if (solrServer instanceof EmbeddedSolrServerWrapper) {
      ((EmbeddedSolrServerWrapper) solrServer).shutdown();
    } else {
      // TODO: there doesn't appear to be any mechanism to close a remote connection to a solr server.
    }
  }

  /**
   * Get location for embedded SOLR from configuration.
   *
   * @param cc
   *          ComponentContext to get configuration from
   * @param indexKey
   *          Configuration key for specific index
   * @param defaultDir
   *          Default directory for specific index
   */
  public static String getEmbeddedDir(ComponentContext cc, String indexKey, String defaultDir)
          throws IllegalStateException {
    /* Check if we have a specific key for this SOLR index */
    String solrDir = cc.getBundleContext().getProperty(indexKey);
    if (StringUtils.isNotEmpty(solrDir)) {
      return solrDir;
    }
    logger.debug("No explicit location configuration for embedded solr. Trying $\\{{}\\}/{}",
        PROP_GLOBAL_SOLR_DIR, defaultDir);

    /* Check if we have a globas SOLR directory configuration */
    solrDir = cc.getBundleContext().getProperty(PROP_GLOBAL_SOLR_DIR);
    if (StringUtils.isNotEmpty(solrDir)) {
      return PathSupport.concat(solrDir, defaultDir);
    }
    logger.debug("No general location configuration for embedded solr. Trying $\\{karaf.data\\}/{}/{}",
        DEFAULT_GLOBAL_SOLR_DIR, defaultDir);
    solrDir = cc.getBundleContext().getProperty("karaf.data");
    if (StringUtils.isNotEmpty(solrDir)) {
      solrDir = PathSupport.concat(solrDir, SolrServerFactory.DEFAULT_GLOBAL_SOLR_DIR);
      return PathSupport.concat(solrDir, defaultDir);
    }

    /* No config found. No fallback possible */
    throw new IllegalStateException("Could not set SOLR root dir. All properties are empty.");
  }

}
