package com.lucidworks.storm.solr;

import org.apache.solr.common.SolrInputDocument;

import java.util.List;
import java.util.Map;

public class NestedDocumentMapper implements SolrInputDocumentMapper {

  protected String idFieldName = "id";
  protected String nestedIdSep = ".";

  public SolrInputDocument toInputDoc(String docId, Object obj) {
    Map mapObj = (Map)obj;
    SolrInputDocument doc = new SolrInputDocument();
    doc.setField(idFieldName, docId);
    addFieldsToDoc(docId, doc, mapObj);
    return doc;
  }

  protected void addFieldsToDoc(String blockId, SolrInputDocument doc, Map map) {
    for (Object key : map.keySet()) {
      Object val = map.get(key);
      if (val != null)
        addFieldToDoc(blockId, doc, key.toString(), val);
    }
  }

  protected void addFieldToDoc(String blockId, SolrInputDocument doc, String fieldName, Object fieldValue) {
    if (fieldValue instanceof List) {
      // peek in the list to see if the values are flat or nested objects
      List list = (List)fieldValue;
      if (list.isEmpty())
        return;

      Object first = list.get(0);
      if (first instanceof Map) {
        // nested object here
        for (int i=0; i < list.size(); i++) {
          Map childMap = (Map)list.get(i);
          Object childId = childMap.get(idFieldName);
          if (childId == null)
            childId = String.format("%s%s%s%s%d",
                doc.getFieldValue(idFieldName), nestedIdSep, fieldName, nestedIdSep, i);

          SolrInputDocument child = new SolrInputDocument();
          child.setField(idFieldName, childId.toString());
          doc.addChildDocument(child);
          addFieldsToDoc(blockId, child, childMap);
        }
      } else if (first instanceof List) {
        // list of list
        for (int i=0; i < list.size(); i++) {
          String listId = String.format("%s%s%s%s%d",
              doc.getFieldValue(idFieldName), nestedIdSep, fieldName, nestedIdSep, i);
          SolrInputDocument child = new SolrInputDocument();
          child.setField(idFieldName, listId);
          doc.addChildDocument(child);
          addFieldToDoc(blockId, child, listId, (List) list.get(i));
        }
      } else {
        // just a multi-valued field here
        doc.setField(fieldName, fieldValue);
      }
    } else if (fieldValue instanceof Map) {
      Map childMap = (Map)fieldValue;
      Object childId = childMap.get(idFieldName);
      if (childId == null)
        childId = String.format("%s%s%s", doc.getFieldValue(idFieldName), nestedIdSep, fieldName);

      SolrInputDocument child = new SolrInputDocument();
      child.setField(idFieldName, childId.toString());
      doc.addChildDocument(child);
      addFieldsToDoc(blockId, child, childMap);
    } else {
      // default behavior, just add this field to the doc
      doc.setField(fieldName, fieldValue);
    }
  }
}