/*
 *
 *  * Copyright 2014 Orient Technologies.
 *  *
 *  * Licensed 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 com.orientechnologies.lucene.manager;

import com.orientechnologies.lucene.query.QueryContext;
import com.orientechnologies.lucene.utils.OLuceneIndexUtils;
import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal;
import com.orientechnologies.orient.core.index.OCompositeKey;
import com.orientechnologies.orient.core.index.OIndexEngineException;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage;
import com.orientechnologies.orient.core.storage.impl.local.paginated.OLocalPaginatedStorage;
import org.apache.lucene.document.Document;
import org.apache.lucene.facet.FacetField;
import org.apache.lucene.facet.FacetsConfig;
import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.store.RAMDirectory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Created by Enrico Risa on 22/04/15.
 */
public class OLuceneFacetManager {

  public static final String          FACET_FIELDS = "facetFields";
  protected TaxonomyWriter            taxonomyWriter;
  protected FacetsConfig              config       = new FacetsConfig();
  protected static final String       FACET        = "_facet";

  protected String                    facetField;
  // protected String facetDim;
  private OLuceneIndexManagerAbstract owner;
  private ODocument                   metadata;

  public OLuceneFacetManager(OLuceneIndexManagerAbstract owner, ODocument metadata) throws IOException {
    this.owner = owner;

    this.metadata = metadata;
    buildFacetIndexIfNeeded();
  }

  protected void buildFacetIndexIfNeeded() throws IOException {

    if (metadata != null && metadata.containsField(FACET_FIELDS)) {
      ODatabaseDocumentInternal database = owner.getDatabase();
      Iterable<String> iterable = metadata.field(FACET_FIELDS);
      if (iterable != null) {
        Directory dir = getTaxDirectory(database);
        taxonomyWriter = new DirectoryTaxonomyWriter(dir, IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
        for (String s : iterable) {
          facetField = s;
          // facetField = "facet_" + s;
          // facetDim = s;
          // config.setIndexFieldName(s, "facet_" + s);
          config.setHierarchical(s, true);
        }
      }

    }
  }

  private Directory getTaxDirectory(ODatabaseDocumentInternal database) throws IOException {
    Directory dir = null;
    final OAbstractPaginatedStorage storageLocalAbstract = (OAbstractPaginatedStorage) database.getStorage().getUnderlying();
    if (storageLocalAbstract instanceof OLocalPaginatedStorage) {
      String pathname = getIndexFacetPath((OLocalPaginatedStorage) storageLocalAbstract);
      dir = NIOFSDirectory.open(new File(pathname));
    } else {
      dir = new RAMDirectory();
    }
    return dir;
  }

  protected String getIndexFacetPath(OLocalPaginatedStorage storageLocalAbstract) {
    return storageLocalAbstract.getStoragePath() + File.separator + owner.OLUCENE_BASE_DIR + File.separator + owner.indexName
        + FACET;
  }

  public void delete() {
    ODatabaseDocumentInternal database = owner.getDatabase();
    final OAbstractPaginatedStorage storageLocalAbstract = (OAbstractPaginatedStorage) database.getStorage().getUnderlying();
    if (storageLocalAbstract instanceof OLocalPaginatedStorage) {
      File f = new File(getIndexFacetPath((OLocalPaginatedStorage) storageLocalAbstract));
      OLuceneIndexUtils.deleteFolder(f);
      f = new File(owner.getIndexBasePath((OLocalPaginatedStorage) storageLocalAbstract));
      OLuceneIndexUtils.deleteFolderIfEmpty(f);
    }
  }

  protected Boolean supportsFacets() {
    return taxonomyWriter != null;
  }

  protected boolean isFacetField(String field) {
    if (metadata == null)
      return false;
    if (metadata.field(FACET_FIELDS) == null)
      return false;
    Collection<String> fields = metadata.field(FACET_FIELDS);
    return fields.contains(field);
  }

  protected IndexableField buildFacetField(String f, Object val) {
    String[] path = null;
    if (val instanceof String) {

      path = ((String) val).split("/");
      // path = new String[1];
      // path[0] = (String) val;
    } else if (val instanceof Iterable) {
      Iterable iterable = (Iterable) val;
      List<String> values = new ArrayList<String>();
      for (Object s : iterable) {
        if (s instanceof String) {
          values.add((String) s);
        } else {
          throw new OIndexEngineException("Cannot facet value " + val + " because it is not a string", null);
        }
      }
      path = values.toArray(new String[values.size()]);
    }
    return new FacetField(f, path);
  }

  public Document buildDocument(Document doc) throws IOException {
    return config.build(taxonomyWriter, doc);
  }

  public void commit() {
    try {
      if (taxonomyWriter != null)
        taxonomyWriter.commit();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  public void addFacetContext(QueryContext queryContext, Object key) throws IOException {
    queryContext.setFacet(true);
    queryContext.setFacetField(facetField);
    queryContext.setFacetConfig(config);
    // queryContext.setfacetDim(facetDim);
    queryContext.setReader(new DirectoryTaxonomyReader(getTaxDirectory(owner.getDatabase())));

    if (key instanceof OCompositeKey) {
      List<Object> keys = ((OCompositeKey) key).getKeys();
      for (Object o : keys) {
        if (o instanceof Map) {
          String drillDown = (String) ((Map) o).get("drillDown");
          queryContext.setDrillDownQuery(drillDown);
        }
      }
    }
  }
}