package co.edu.uniandes.accordant.model;

import java.io.BufferedReader;
import java.net.MalformedURLException;
import java.net.URL;
import org.jpmml.model.PMMLUtil;
import org.dmg.pmml.DataDictionary;
import org.dmg.pmml.DataField;
import org.dmg.pmml.DataType;
import org.dmg.pmml.PMML;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.JAXBException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import co.edu.uniandes.accordant_dv.DeploymentModel;
import co.edu.uniandes.accordant_dv.TypeDevice;
import co.edu.uniandes.accordant_fv.AccessType;
import co.edu.uniandes.accordant_fv.Buffering;
import co.edu.uniandes.accordant_fv.DeliveryGuarantee;
import co.edu.uniandes.accordant_fv.Field;
import co.edu.uniandes.accordant_fv.NotificationModel;
import co.edu.uniandes.accordant_fv.ProcessingModel;
import co.edu.uniandes.accordant_fv.SyncType;
import co.edu.uniandes.accordant_fv.Throughput;
import co.edu.uniandes.accordant_rq.ConstraintType;
import co.edu.uniandes.accordant_rq.MetricUnit;
import co.edu.uniandes.accordant_rq.QAMetric;
import co.edu.uniandes.accordant_rq.QAttribute;

public class Util {

	public static String getValueMatch(String pattern, String text) {
		Pattern rId = Pattern.compile(pattern);
		Matcher mId = rId.matcher(text);
		mId.find();
		return mId.group(1);
	}
	
	public static String formatName(String name) {
		return name!=null? name.replaceAll(" ", "_"):null;
	}

	public static String getMxCellParent(Element objectElement) {
		NodeList mxCells = objectElement.getElementsByTagName("mxCell");
		for (int g = 0; g < mxCells.getLength(); g++) {
			Node mxCell = mxCells.item(g);
			NamedNodeMap attMap=mxCell.getAttributes();
			Node parent = attMap.getNamedItem("parent");
			if(parent!=null) {
				return parent.getNodeValue();
			}
			/*if (mxCell.getParentNode()!=null) {
				
			}*/
			/*if (mxCell.getNodeType() == Node.ELEMENT_NODE) {
				Element el = (Element) mxCell;
			}*/
		}	
		return null;
	}
	
	
	public static String[] getValuesMatch(String pattern, String text) {
		Pattern r = Pattern.compile(pattern);
		Matcher m = r.matcher(text);
		m.find();
		
		int con = m.groupCount();
		String[] values = new String[con];
		for(int i=0;i<con;i++) {
			values[i] = m.group(i+1); 
		}
		return values;
	}
	
	public static ArrayList<String> readFile(String fileName) {
		ArrayList<String> lines = new ArrayList<>();
		
		BufferedReader reader;
		try {
			reader = new BufferedReader(new FileReader("files/"+fileName));
			String line = reader.readLine();
			while (line != null) {
				// read next line
				lines.add(line);				
				line = reader.readLine();
			}
			
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return lines;
	}
	
	/**
	 * 
	 * @param xmlFileName Name of the xml file, i.e. students.xml. The path of the file must be included
	 * @return
	 */
	public static Document parseXML(String xmlFileName) throws Exception{
		File xmlFile = new File(xmlFileName);
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document doc = builder.parse(xmlFile);
		return doc;
	}
	
	
	public static Document parseXML(InputStream xmlStream) throws Exception{
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document doc = builder.parse(xmlStream);
		return doc;
	}
	
	
	public static Document parseXML(InputSource xmlSource) throws Exception{
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		Document doc = builder.parse(xmlSource);
		return doc;
	}
	
	public static List<String[]> getPMMLFields(String urlPMML)  {
			try {
				System.out.println("URLPMML: "+urlPMML);
				URL url = new URL(urlPMML);
				InputStream input =	url.openStream();
				PMML model;
				model = PMMLUtil.unmarshal(input);
				if (model != null) {
					List<String[]> fields= new ArrayList<String[]>();
					DataDictionary dataDict = model.getDataDictionary();
					for (DataField pmmlfield : dataDict.getDataFields()) {
						String name = pmmlfield.getName().getValue();
						String dtype= parseDTypes(pmmlfield.getDataType());
						fields.add(new String[]{name,dtype});
					}
					return fields;
				}
				input.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
	}		
	
	

	public static NotificationModel parseNotification(String notModel) {
		if ("CENTRAL".equalsIgnoreCase(notModel)) {
			return NotificationModel.CENTRAL;
		} else if ("POLLED".equalsIgnoreCase(notModel)) {
			return NotificationModel.POLLED;
		} else if ("PUBSUB".equalsIgnoreCase(notModel)) {
			return NotificationModel.PUBSUB;
		} else if ("QUEUED".equalsIgnoreCase(notModel)) {
			return NotificationModel.QUEUED;
		}
		return null;
	}
	
	public static Throughput parseThroughput(String tp) {
		if ("ATOMIC".equalsIgnoreCase(tp)) {
			return Throughput.ATOMIC;
		} else if ("HIGH_ORDER".equalsIgnoreCase(tp)) {
			return Throughput.HIGH_ORDER;
		}
		return null;
	}
	
	public static Buffering parseBuffering(String buffering) {
		if ("BUFFERED".equalsIgnoreCase(buffering)) {
			return Buffering.BUFFERED;
		} else if ("UNBUFFERED".equalsIgnoreCase(buffering)) {
			return Buffering.UNBUFFERED;
		}
		return null;
	}
	
	
	public static SyncType parseSyncType(String stype) {
		if ("async".equalsIgnoreCase(stype)) {
			return SyncType.ASYNC;
		} else if ("sync".equalsIgnoreCase(stype)) {
			return SyncType.SYNC;
		}
		return null;
	}
	
	public static DeliveryGuarantee parseDelivery(String delivery) {
		if ("BestEffort".equalsIgnoreCase(delivery)) {
			return DeliveryGuarantee.BEST_EFFORT;
		} else if ("AtLeastOne".equalsIgnoreCase(delivery)) {
			return DeliveryGuarantee.AT_LEAST_ONCE;
		} else if ("AtMostOne".equalsIgnoreCase(delivery)) {
			return DeliveryGuarantee.AT_MOST_ONCE;
		}else if ("ExactlyOne".equalsIgnoreCase(delivery)) {
			return DeliveryGuarantee.EXACTLY_ONCE;
		}
		return null;
	}
	
	
	public static AccessType parseAccessType(String type) {
		if ("Database".equalsIgnoreCase(type)) {
			return AccessType.DATABASE;
		} else if ("FileSystem".equalsIgnoreCase(type)) {
			return AccessType.FILESYSTEM;
		} else if ("HDFS".equalsIgnoreCase(type)) {
			return AccessType.HDFS;
		}else if ("HTTP".equalsIgnoreCase(type)) {
			return AccessType.HTTP;
		}
		return null;
	}
	
	public static ProcessingModel parseProcModel(String procModel) {
		if ("Batch".equalsIgnoreCase(procModel)) {
			return ProcessingModel.BATCH;
		} else if ("Stream".equalsIgnoreCase(procModel)) {
			return ProcessingModel.STREAM;
		} else if ("Microbatch".equalsIgnoreCase(procModel)) {
			return ProcessingModel.MICROBATCH;
		}
		return null;
	}
	
	public static QAMetric parseQAMetric(String qaMetric) {
		if (qaMetric.equalsIgnoreCase("Deadline")) {
			return QAMetric.DEADLINE;
		} else if (qaMetric.equalsIgnoreCase("Latency")) {
			return QAMetric.LATENCY;
		} else if (qaMetric.equalsIgnoreCase("Throughput")) {
			return QAMetric.THROUGHPUT;
		} else if (qaMetric.equalsIgnoreCase("Update Time")) {
			return QAMetric.UPDATETIME;
		}
		return null;
	}

	public static QAttribute parseQAttribute(String qAttribute) {
		if (qAttribute.equalsIgnoreCase("Availability")) {
			return QAttribute.AVAILABILITY;
		} else if (qAttribute.equalsIgnoreCase("Performance")) {
			return QAttribute.PERFORMANCE;
		} else if (qAttribute.equalsIgnoreCase("Modifiability")) {
			return QAttribute.MODIFIABILITY;
		} else if (qAttribute.equalsIgnoreCase("Testability")) {
			return QAttribute.TESTABILITY;
		} else if (qAttribute.equalsIgnoreCase("Scalability")) {
			return QAttribute.SCALABILITY;
		}
		return null;
	}

	public static MetricUnit parseUnit(String unit) {
		if (unit.equalsIgnoreCase("Hours")) {
			return MetricUnit.HOURS;
		} else if (unit.equalsIgnoreCase("Minutes")) {
			return MetricUnit.MINUTES;
		} else if (unit.equalsIgnoreCase("Seconds")) {
			return MetricUnit.SECONDS;
		} else if (unit.equalsIgnoreCase("Millis")) {
			return MetricUnit.MILLIS;
		} else if (unit.equalsIgnoreCase("Messages/seg")) {
			return MetricUnit.MSGS;
		} else if (unit.equalsIgnoreCase("Bits/seg")) {
			return MetricUnit.BYTES;
		}
		return null;
	}

	public static ConstraintType parseType(String type) {
		if (type.equals("Technology")) {
			return ConstraintType.TECH_VENDOR;
		} else if (type.equals("Architectural Style")) {
			return ConstraintType.ARCH_STYLE;
		} else if (type.equals("Cloud Model")) {
			return ConstraintType.CLOUD_PROV;
		} else if (type.equals("Processing Model")) {
			return ConstraintType.PROC_MODEL;
		}
		return null;
	}
	
	public static String parseDTypes(DataType datatype) {
		switch (datatype.name()) {
      	case "FLOAT": return "Float";
			case "INTEGER": return"Integer";
			case "DOUBLE": return"Double";
			case "BOOLEAN":return "Boolean";
			case "DATE":return "Date";
			default: return "String";
		}
	}
	
	
	public static TypeDevice parseDeviceType(String type) {
		if (type.equals("LARGE")) {
			return TypeDevice.LARGE;
		} else if (type.equals("MEDIUM")) {
			return TypeDevice.MEDIUM;
		} else if (type.equals("SMALL")) {
			return TypeDevice.SMALL;
		} else if (type.equals("MICRO")) {
			return TypeDevice.MICRO;
		} else if (type.equals("XLARGE")) {
			return TypeDevice.XLARGE;
		}		
		return null;
	}
	
	public static DeploymentModel parseDeploymentModel(String model) {
		if (model.equals("CLOUD")) {
			return DeploymentModel.CLOUD;
		} else if (model.equals("HYBRID")) {
			return DeploymentModel.HYBRID;
		} else if (model.equals("ONPREMISES")) {
			return DeploymentModel.ONPREMISES;
		}		
		return null;
	}
}
