package frame.object.dao;

import frame.object.meta.EntityMeta;
import frame.object.meta.Field;
import frame.util.ContentBuilder;
import frame.util.Util;
import frame.variant.IVariantsConsumer;
import frame.variant.IVariantsProvider;
import frame.variant.VariantLink;
import frame.variant.ValueType;
import frame.variant.translator.ITranslator;

import java.util.*;

public class Filter implements IVariantsConsumer, IVariantsProvider {

	private EntityMeta entityMeta;
	private Map<String, FilterItem> items;
	private String rawFilter;
	private boolean raw = false;
	private boolean empty;

	
	public Filter() {
		items = new HashMap<String, FilterItem>();
	}
	
	public Filter(String filter) {
		rawFilter = filter;
		raw = true;
	}
	
	public void addAll(EntityMeta entityMeta, Set<String> excludeNames, IVariantsProvider...valueProviders) throws Exception {
		this.entityMeta = entityMeta;
		
		for (int i = 0; i < valueProviders.length; i++) {
			IVariantsProvider provider = valueProviders[i];
			VariantLink.moveOnConsumer(provider, this, excludeNames);
		}
	}

	public void add(String fieldName, String value) {
		this.addItem(fieldName, "=", value);
	}

	public void addItem(String fieldName, String operator, String value) {
		FilterItem item = new FilterItem(fieldName, operator, value);
		items.put(fieldName, item);
	}

	@Override
	public List<String> getVariantNameList() {
		List<String> result = new ArrayList<String>();
		result.add("orderby");
		
		return result;
	}

	@Override
	public boolean containsVariant(String name) {
		return "filter".equalsIgnoreCase(name);
	}

	@Override
	public Object getVariantValue(String name) {
		return toString();
	}

	@Override
	public void setVariant(String name, Object value) throws Exception {
		if (value == null) {
			return;
		}
		
		if (entityMeta == null) {
			throw new Exception("filter entityMeta is null");
		}
		
		if(!entityMeta.contains(name)) {
			return;
		}
		Field field = entityMeta.getField(name);
		
		if (field == null) {
			return;
		}
		
		ITranslator translator = field.getTranslator();
		ValueType type = field.getValueType();
		
		if (ValueType.String == type) {
			String stringValue = value.toString();
			String segmentValue = null;
			
			//1. like
			if (stringValue.startsWith("(like)")) {
				if (stringValue.endsWith("(like)")) {
					//1.1 all like
					segmentValue = "'%" + stringValue.substring(7, stringValue.length() - 6) + "%'";
					addItem(name, "like", segmentValue);
				}
				else {
					//1.2 left like
					segmentValue = "'%" + stringValue.substring(7) + "'";
					addItem(name, "like", segmentValue);
				}
			}
			else if (stringValue.endsWith("(like)")) {
				//1.3 right like
				segmentValue = "'" + stringValue.substring(0, stringValue.length() - 6) + "%'";
				addItem(name, "like", segmentValue);
			}
			
			else {
				//1.3 no like
				segmentValue = "'" + stringValue + "'";
				addItem(name, "=", segmentValue);
			}
		}
		else {
			String segmentValue = translator.toSqlString(value);
			addItem(name, "=", segmentValue);
		}
	}
	
	@Override
	public void setVariants(IVariantsProvider... providers) throws Exception { 
		if (providers == null || providers.length == 0) {
			return;
		}
		
		Set<String> keySet = items.keySet();
		
		for (String key: keySet) {
			for (IVariantsProvider provider: providers) {
				if (provider.containsVariant(key)) {
					FilterItem item = items.get(key);
					Object value = provider.getVariantValue(key);
					
					if (!Util.isEmptyStr(value)) {
						item.setValue(String.valueOf(value));
					}
				}
			}
		}
	}
	
	@Override
	public boolean isVariantNull(String name) {
		return empty;
	}

	
	public FilterItem getItemByName(String name) {
		if (items == null) {
			return null;
		}
		return items.get(name);
	}
	
	public FilterItem removeItemByName(String name) {
		if (items == null) {
			return null;
		}
		return items.remove(name);
	}
	
	public boolean isRaw() {
		return raw;
	}

	public String getRawFilter() {
		return rawFilter;
	}

	@Override
	public String toString() {
		if (raw) {
			return rawFilter;
		}
		
		if (items.isEmpty()) {
			return null;
		}
		
		ContentBuilder result = new ContentBuilder(" and ");
		Set<String> keySet = items.keySet();
		for (String key : keySet) {
			FilterItem filterItem = items.get(key);
			result.append(filterItem.toSQLString());
		}
		
		if (result.isEmpty()) {
			result.append(" 1 = 1 ");
		}
		
		return "(" + result + ")";
	}
	
}