package com.mongodb.hvdf.rollup;

import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.hvdf.api.ConfigurationError;
import com.mongodb.hvdf.api.ServiceException;
import com.mongodb.hvdf.configuration.PluginConfiguration;

public class GroupCountRollup extends RollupOperation {
	
    protected final String fieldExtention;
	protected final List<RangeCounter> ranges;

	public GroupCountRollup(PluginConfiguration config){
		
		this.fieldExtention = "." + "group_count";
		
		// any field mentioned in the config is a target
		DBObject rawConfig = config.getRaw();
		ranges = new ArrayList<RangeCounter>(rawConfig.keySet().size());
		
		for(String fieldName : rawConfig.keySet()){
			Object rangesObj = rawConfig.get(fieldName);
			if(rangesObj instanceof BasicDBList){
				ranges.add(new RangeCounter(fieldName, (BasicDBList)rangesObj));				
			} else {
				// the field must specify a range of values
				throw new ServiceException("Expected a list of range values", 
						ConfigurationError.INVALID_CONFIG_TYPE).
							set("configuring", GroupCountRollup.class).
							set("field", fieldName);	
			}
		}		
	}

	@Override
	public DBObject getUpdateClause(DBObject sample) {

		BasicDBObject incFields = new BasicDBObject();
		for(RangeCounter counter : ranges){
			
			Object valueObj = getNestedFieldValue(counter.getFieldName(), sample);
			if(valueObj != null){
				if (valueObj instanceof DBObject){					
					// this might be a nested *do everything* request
					// TODO : handle recursing into the object adding all fields
					
				} else {
					// this is the normal case, we can apply it directly as a clause
					incFields.append(counter.getIncField(valueObj), 1);
				}
			}
		}
		
		if(incFields.size() > 0){
			return new BasicDBObject("$inc", incFields);
		} else {
			return new BasicDBObject();
		}
	}	
}

class RangeCounter{

    private static Logger logger = LoggerFactory.getLogger(RangeCounter.class);

    private final String fieldName;
    private final String fieldNamePrefix;
	private final TreeSet<Object> rangesValues;
	
	public RangeCounter(String fieldName, BasicDBList rangesObj) {
		this.fieldName = fieldName;
		this.fieldNamePrefix = fieldName + ".group_count.";
		this.rangesValues = new TreeSet<Object>();
		this.rangesValues.addAll(rangesObj);
	}
	
	public String getFieldName() {
		return this.fieldName;
	}

	public String getIncField(Object value){
		
		if(value != null){
			try{
				Object floor = rangesValues.floor(value);
				if(floor != null){
					return this.fieldNamePrefix + floor.toString();
				}
			} catch(Exception e) {
				logger.warn("Value {} for field {} not compatible with ranges", value, this.fieldNamePrefix);
			}
		}
		
		// Doesnt fit any range, its in the low category
		return this.fieldNamePrefix + "low";
	}
	
}