package eu.dnetlib.domain.functionality;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import eu.dnetlib.domain.LocaleHolder;
import eu.dnetlib.domain.data.Document;

public class DocumentField {

	public enum Cutpoint {
		LINE, VALUE, PART;
	}

	private String name = null;
	private Map<Locale, String> descriptionMap = null;
	private Boolean forceDescription = null;
	private Map<Locale, String> secondaryLinkMap;
	private String vocabulary = null;
	private List<DisplayType> displays = new ArrayList<DisplayType>();
	private int maxCharacters = Integer.MAX_VALUE;
	private Cutpoint cutpoint = Cutpoint.LINE;
	private String cssClass = null;
	
	public DocumentField() {

	}

	public DocumentField(String name, Map<Locale, String> description, String vocabulary,
			List<DisplayType> displays, String cssClass) {
		this.name = name;
		this.setDescriptionMap(description);
		this.vocabulary = vocabulary;
		this.displays.addAll(displays);
		this.cssClass = cssClass;
	}

	public DocumentField(String name, Map<Locale, String>  description, String vocabulary,
			List<DisplayType> displays, String cssClass, int maxCharacters) {
		this.name = name;
		this.setDescriptionMap(description);
		this.vocabulary = vocabulary;
		this.displays.addAll(displays);
		this.cssClass = cssClass;
		if (maxCharacters < 1) {
			throw new RuntimeException(
					"The maximum number of characters must be greater than 0.");
		} else {
			this.maxCharacters = maxCharacters;
		}
	}

	public DocumentField(String name, Map<Locale, String> description, String vocabulary,
			List<DisplayType> displays, String cssClass, Cutpoint cutpoint,
			int maxCharacters) {
		this.name = name;
		this.setDescriptionMap(description);
		this.vocabulary = vocabulary;
		this.displays.addAll(displays);
		this.cssClass = cssClass;
		this.cutpoint = cutpoint;
		if (maxCharacters < 1) {
			throw new RuntimeException(
					"The maximum number of characters must be greater than 0.");
		} else {
			this.maxCharacters = maxCharacters;
		}
	}

	public List<List<String>> getMessage(Document document) {

		List<List<String>> messages = new ArrayList<List<String>>();
		int characterCount = 0;

		List<String> fieldValues = document.getFieldValues(name);
		if (fieldValues.isEmpty() && this.forceDescription != null
				&& forceDescription.equals(true)) {

			String fieldName = getDescription().substring(2, getDescription().length() - 1);
			fieldValues = document.getFieldValues(fieldName);
			List<DisplayType> simpleDisplay = new ArrayList<DisplayType>();
			simpleDisplay.add(new PlainTextDisplayType(fieldName));
			generateValues(fieldName, fieldValues, characterCount, document,
					simpleDisplay, messages);
		} else {
			generateValues(name, fieldValues, characterCount, document,
					displays, messages);
		}

		return messages;
	}

	public void generateValues(String fieldName, List<String> fieldValues,
			int characterCount, Document document, List<DisplayType> displays,
			List<List<String>> messages) {

		for (int i = 0; i < fieldValues.size()
				&& (characterCount < maxCharacters); i++) {
			
			List<String> values = new ArrayList<String>();
			String value = document.getFieldValues(fieldName).get(i);
			
			for (DisplayType type : displays) {
				StringBuilder builder = new StringBuilder();
	
				boolean useSecondaryLink = false;
				if (messages.size() > 0 && getSecondaryLink()!= null && !getSecondaryLink().isEmpty()) {
					useSecondaryLink = true;
				}
				
				int messageSize = 0;
				if (useSecondaryLink == true) {
					messageSize = type.getDisplayMessage(builder, document,
						value, getSecondaryLink());
				} else {
					messageSize = type.getDisplayMessage(builder, document,
							value);
				}
				
				if (characterCount + messageSize > maxCharacters) {
					switch (cutpoint) {
					case LINE:
						values.add(builder.toString());
						break;
					case VALUE:
						if (characterCount <= maxCharacters) {
							values.add(builder.toString());
						}
						break;
					case PART:
						String s = builder.toString();
						s = s.substring(0, maxCharacters - characterCount)
								+ "...";
						values.add(s);
						break;
					}
				} else {
					values.add(builder.toString());
				}

				characterCount += messageSize;
			}		
			messages.add(values);
		}
	}

	public String getDescription(Document document) {
		return getDescription();
	}

	public String getCssClass(Document document) {
		return getCssClass();
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getVocabulary() {
		return vocabulary;
	}

	public void setVocabulary(String vocabulary) {
		this.vocabulary = vocabulary;
	}

	public List<DisplayType> getDisplays() {
		return displays;
	}

	public void setDisplays(List<DisplayType> displays) {
		this.displays = displays;
	}

	public void setMaxCharacters(int maxCharacters) {
		this.maxCharacters = maxCharacters;
	}

	public int getMaxCharacters() {
		return maxCharacters;
	}

	

	public Cutpoint getCutpoint() {
		return cutpoint;
	}

	public void setCutpoint(Cutpoint cutpoint) {
		this.cutpoint = cutpoint;
	}

	public String getCssClass() {
		return cssClass;
	}

	public void setCssClass(String cssClass) {
		this.cssClass = cssClass;
	}

	public void setDescriptionMap(Map<Locale, String> descriptionMap) {
		this.descriptionMap = descriptionMap;
	}

	public Map<Locale, String> getDescriptionMap() {
		return descriptionMap;
	}
	
	public String getDescription(){
		String localeDescription = descriptionMap.get(LocaleHolder.getLocale());
		if (localeDescription != null) {
			return localeDescription;
		}
		return descriptionMap.get(new Locale("en", "GB"));
	}

	public void setSecondaryLinkMap(Map<Locale, String> secondaryLinkMap) {
		this.secondaryLinkMap = secondaryLinkMap;
	}

	public Map<Locale, String> getSecondaryLinkMap() {
		return secondaryLinkMap;
	}

	public String getSecondaryLink() {
		return secondaryLinkMap.get(LocaleHolder.getLocale());
	}
	
	public Boolean getForceDescription() {
		return forceDescription;
	}

	public void setForceDescription(Boolean forceDescription) {
		this.forceDescription = forceDescription;
	}

}