package gr.uoa.di.web.utils;

import eu.dnetlib.domain.data.Document;
import eu.dnetlib.domain.enabling.Vocabulary;
import eu.dnetlib.domain.functionality.DisplayType;
import eu.dnetlib.domain.functionality.DocumentField;
import eu.dnetlib.domain.functionality.DriverUrlDisplayType;
import eu.dnetlib.domain.functionality.ExternalUrlDisplayType;
import eu.dnetlib.domain.functionality.InternalUrlDisplayType;
import eu.dnetlib.domain.functionality.PlainTextDisplayType;
import eu.dnetlib.domain.functionality.SearchDisplayType;
import gr.uoa.di.web.utils.webdocument.StyledValue;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.lang.StringEscapeUtils;

public class XMLGenerator {
	
	//private static Logger logger = Logger.getLogger(XMLGenerator.class);
	
	public static List<StyledValue> generate(String fieldname, List<DisplayType> displays, String cssClass, 
			Boolean forcedescription, Map<Locale, String> descriptionMap, Document document, Vocabulary vocabulary, 
			Locale locale, List<StyledValue> xmlValuesList) {
		
		List<String> fieldvalues = document.getFieldValues(fieldname);
	
		if (fieldvalues != null && !fieldvalues.isEmpty()) {			
	
			if(fieldname.toLowerCase().equals("identifier")) {
				boolean hasPDF = false;
				for(String fieldValue: fieldvalues) {
					if (fieldValue.trim().endsWith(".pdf")) {
						//logger.debug("pdf? " + fieldValue);
						for(DisplayType displayType:displays) {
							StyledValue styledValue = new StyledValue(generate(displayType, fieldname, fieldValue, document, locale), cssClass);
							xmlValuesList.add(styledValue);
						}
						hasPDF = true;
						return xmlValuesList; // returns the link of the pdf file
					}
				}
				
				if (!hasPDF) {
					for(DisplayType displayType:displays) {
						StyledValue styledValue = new StyledValue(generate(displayType, fieldname, fieldvalues.get(0), document, locale), cssClass);
						xmlValuesList.add(styledValue);
					}
					
					return xmlValuesList; // returns the first identifier it finds
				}
			}
			
			for(String fieldValue: fieldvalues) {
				if( vocabulary != null ) {
					String englishName = vocabulary.getEnglishName(fieldValue);
					if (englishName != null) {
						fieldValue = vocabulary.getEnglishName(fieldValue);
					}
				}
				
				for(DisplayType displayType:displays) {
					StyledValue styledValue = new StyledValue(generate(displayType, fieldname, fieldValue, document, locale), cssClass);
					//logger.debug("stld value " + styledValue.getValue());
					xmlValuesList.add(styledValue);
				}
			}		
			return xmlValuesList;
			
		} else {		
			if (forcedescription != null && forcedescription) {
				fieldname = LocaleDescriptionUtil.getFieldDescription(descriptionMap, locale).substring
					(2, LocaleDescriptionUtil.getFieldDescription(descriptionMap, locale).length() - 1);
				DocumentField newfield = new DocumentField();
				newfield.setName(fieldname);
				fieldvalues = document.getFieldValues(fieldname);
				
				if (fieldvalues != null) {
					List<DisplayType> simpleDisplayList = new ArrayList<DisplayType>();
					simpleDisplayList.add(new PlainTextDisplayType(fieldname));
					generate(newfield.getName(), simpleDisplayList, cssClass, true, descriptionMap, document, vocabulary, locale, xmlValuesList);				
				}
			}
				
		return xmlValuesList;
		}
	}
	
	private static String generate(DisplayType displayType, String fieldName,
			String fieldValue, Document document, Locale locale) {

		if (displayType instanceof PlainTextDisplayType) {
			return generate((PlainTextDisplayType)displayType, fieldName, fieldValue, document, locale);
		}  
		
		return generate((DriverUrlDisplayType)displayType, fieldName, fieldValue, document, locale);
	
	}
	
	private static String generate(PlainTextDisplayType displayType, String fieldName,
			String fieldValue, Document document, Locale locale) {

		String displayValue = DisplayValueGenerator.replaceFieldValueExpression(
				document, getExpression(displayType, locale), fieldName, fieldValue);
								
		StringBuffer buffer = new StringBuffer();
		appendElement(buffer, "type", "text/plain");
		appendElement(buffer, "displayValue", displayValue);
		return buffer.toString();
	}
	
	private static String generate(DriverUrlDisplayType displayType, String fieldName,
			String fieldValue, Document document, Locale locale) {
		String displayValue = DisplayValueGenerator.replaceFieldValueExpression(
				document, getExpression(displayType, locale), fieldName, fieldValue);

		StringBuffer buffer = new StringBuffer();
		String type = null;
		String urlType = null;
		
		if (displayType instanceof ExternalUrlDisplayType) {
			type = "url";
			urlType = "external";

		} else if(displayType instanceof SearchDisplayType){
			type = "url";
			urlType = "search";
			
		} else if (displayType instanceof InternalUrlDisplayType){ 
			type = "url";
			urlType = "internal";
			
		} else {
			type = "text/plain";
		}
		 
		//TODO: activate all different url types
		/*
		String urlType = null;
		if (displayType instanceof DocumentExternalUrlDisplayType) {
			urlType = "document";
		}
		*/
		
		appendElement(buffer, "type", type);
		if (urlType != null) {
			appendElement(buffer, "urlType", urlType);
			
			if (displayType instanceof ExternalUrlDisplayType) {
				appendElement(buffer, "href", getUrl((ExternalUrlDisplayType)displayType, document, fieldValue));
				
			} else if (displayType instanceof SearchDisplayType) {					
				appendElement(buffer, "href", getQueryLink((SearchDisplayType)displayType, document, fieldValue));
			
			} else if (displayType instanceof InternalUrlDisplayType) {
				appendElement(buffer, "action", getInternalLink((InternalUrlDisplayType) displayType, fieldValue));
			}

		}
		appendElement(buffer, "displayValue", displayValue);		
		return buffer.toString();
	}

	public static String getUrl(ExternalUrlDisplayType displayType,
			Document document, String fieldValue) {

		String encodedFieldValue = null;
		try {
			encodedFieldValue = URLEncoder.encode(fieldValue, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			encodedFieldValue = fieldValue;
		}
		
		return encodedFieldValue;
	}
	
	public static String getQueryLink(SearchDisplayType displayType,
			Document document, String fieldValue) {

		StringBuilder builder = new StringBuilder();
		builder.append("/xmlResults").append(".action?");
		String query = "(" + ((SearchDisplayType)displayType).getLabel() + "=" + "\"" + fieldValue + "\"" + ")";
		builder.append("query=").append(query);
		builder.append("&page=1&format=xml");
		
		String encodedFieldValue = null;
		try {
			encodedFieldValue = URLEncoder.encode(builder.toString(), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			encodedFieldValue = fieldValue;
		}
		
		return encodedFieldValue;
	}
	
	
	public static String getInternalLink(InternalUrlDisplayType displayType, String fieldValue) {
		StringBuilder builder = new StringBuilder();
		builder.append(displayType.getAction());
		builder.append("?").append(displayType.getParameter()).append("=").append(fieldValue);
		
		return builder.toString();
	}
	
	private static String getExpression(DisplayType displayType, Locale locale) {
		Map<Locale, String> descriptionMap = displayType.getDescriptionMap();
		
		if (descriptionMap != null) {
			String description = descriptionMap.get(locale);
			if (description == null) {
				return descriptionMap.get(new Locale("en", "GB"));
			}
			return description;
			
		} else {
			return null;
			
		}
	}
	
	private static void appendElement(StringBuffer buffer, String tagName, String content) {
		if (content != null && !content.isEmpty()) {
			buffer.append("<").append(tagName).append(">")
				.append(StringEscapeUtils.escapeXml(content)).append("</").append(tagName).append(">");
			
		} else {
			buffer.append("</").append(tagName).append(">");
		}
	}
	
}