package es.uv.saic.service;

import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.UncheckedIOException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import es.uv.saic.domain.Indicador;
import es.uv.saic.domain.Plantilla;
import es.uv.saic.domain.PlantillaComentario;
import es.uv.saic.domain.PlantillaConversation;
import es.uv.saic.domain.PlantillaRepository;
import es.uv.saic.dto.IndicadorEnquestaDTO;
import es.uv.saic.dto.IndicadorEnquestaValorDTOImp;
import es.uv.saic.dto.InstanciaTascaDTO;
import es.uv.saic.dto.NomProcesOrganDTO;
import es.uv.saic.dto.OrganDTO;
import es.uv.saic.feign.CoreClient;

@Service
public class PlantillaService {

	@Autowired
	private PlantillaRepository r;
	@Value("${saic.data.filePath}")
	private String filePath;
	@Value("${saic.data.tmpPath}")
	private String tmpPath;
	@Value("${saic.data.templates.fileNotFound}")
	private String fileNotFound;
	@Value("${saic.data.templates.filePath}")
	private String templatePath;
	@Value("${saic.data.templates.logoPath}")
	private String logoPath;
	@Autowired
	private CoreClient core;
	
	private static final DecimalFormat df = new DecimalFormat("0.00");
	
	public List<Plantilla> findAll(){
		return this.r.findAll();
	}
	
	public Plantilla findByID(Integer id) {
		return this.r.findByIdPlantilla(id);
	}
	
	public Plantilla findByVersioCodiAmbit(Integer versio, String codi, String ambit) {
		return this.r.findByVersioCodiAmbit(versio, codi, ambit);
	}
	
	public void save(Plantilla p) {
		this.r.save(p);
		this.r.flush();
	}
	
	public void delete(Plantilla p) {
		this.r.delete(p);
	}
	
	public boolean isUsed(Integer idPlantilla) {
		List<Plantilla> l = this.r.findUsedByIdPlantilla(idPlantilla);
		if(l.size() > 0) {
			return true;
		}
		return false;
	}
	
	public String addTemplateData(InstanciaTascaDTO it, String template){
		HashMap<String, String> context = new HashMap<String, String>();
		HashMap<String, String> header = new HashMap<String, String>();
		HashMap<String, String> img = new HashMap<String, String>();
		 
		Integer idTitulacio = it.getInstancia().getTitulacio();
		Integer cursAvaluat = it.getInstancia().getCursAvaluat();
		Integer idCentre = it.getInstancia().getCentre();
		String ambit = "G";
		String opcionsStr = it.getTasca().getOpcions().replaceAll("^\\.$", "");
		String [] opcions = new String[0];
		if(!opcionsStr.isEmpty()) {
			opcions = opcionsStr.split(";");
		}
		if(it.getInstancia().getTlugar().equals("C")) {
			if(it.getInstancia().getTitulacio() == 2) {
				ambit = "M";
			}
		}
		
		/** Add header information **/
		addHeaderData(it.getInstancia().getNomval(), it.getInstancia().getLugar(), it.getInstancia().getNomValTitulacio(), it.getInstancia().getCursAvaluat(), header, img);
		
		/** Iterate elements inside {[loop]} ... {[endloop]} **/
		template = this.iterateLoopTag(template, idCentre, ambit, cursAvaluat, context);
		
		/** Iterate elements with data-loop attribute **/
		Document doc = Jsoup.parse(template);
		doc = iterateLoopAttr(idCentre, cursAvaluat, doc, context);
		
		/** Add template data (non iterated data)**/
		getTemplateData(idTitulacio, idCentre, cursAvaluat, context);	
		this.replaceValues(doc, context, header, img);
		
		/** Replace sections if specified in options **/
		this.replaceSection(opcions, it, doc);
		
		return doc.html();	
	}
	
	public String addTemplateData(Integer idTitulacio, Integer idCentre, Integer curs, String template){
		
		HashMap<String, String> context = new HashMap<String, String>();
		HashMap<String, String> header = new HashMap<String, String>();
		HashMap<String, String> img = new HashMap<String, String>();
		 
		OrganDTO centre = core.findOrganById("C", idCentre);
		OrganDTO titulacio = core.findOrganById("T", idTitulacio);
		
		/** Add header information **/
		addHeaderData(centre.getNomVal(), centre.getLugar2(), titulacio.getNomVal(), curs, header, img);
							
		/** Iterate elements inside {[loop]} ... {[endloop]} **/
		template = this.iterateLoopTag(template, idCentre, titulacio.getTambit(), curs, context);
		
		/** Iterate elements with data-loop attribute **/
		Document doc = Jsoup.parse(template);
		doc = iterateLoopAttr(idCentre, curs, doc, context);
		
		/** Add template data (non iterated data)**/
		getTemplateData(idTitulacio, idCentre, curs, context);	
		this.replaceValues(doc, context, header, img);
		
		return doc.html();	
	}
	
	private void addHeaderData(String nomVal, Integer lugar, String nomValTitulacion, Integer curs, HashMap<String, String> header, HashMap<String, String> img) {
		header.put("centre", nomVal);
		header.put("titulacio", nomValTitulacion);
		header.put("curs", "CURSO "+Integer.toString(curs-1)+" - "+Integer.toString(curs));
		header.put("curs_anterior", Integer.toString(curs-2)+" - "+Integer.toString(curs-1));
		header.put("conv-ant1", "CONVOCATORIA "+Integer.toString(curs-1)+" - "+Integer.toString(curs));
		header.put("conv-ant2", "CONVOCATORIA "+Integer.toString(curs-2)+" - "+Integer.toString(curs-1));
		header.put("periode-ant1", Integer.toString(curs-6)+" - "+Integer.toString(curs-1));
		header.put("periode-ant2", Integer.toString(curs-7)+" - "+Integer.toString(curs-2));
		ClassPathResource fuv = new ClassPathResource("/static/logos/UV.png");
		ClassPathResource fc = new ClassPathResource("/static/logos/C"+Integer.toString(lugar)+".png");
		String logouv_b64;
		String logoc_b64;
		try {
			logouv_b64 = "data:image/png;base64, "+Base64.getEncoder().encodeToString(fuv.getInputStream().readAllBytes());
		} catch (IOException e) {
			logouv_b64 = "https://saic.uv.es/public/logos/UV.png";
		}
		try {
			logoc_b64 = "data:image/png;base64, "+Base64.getEncoder().encodeToString(fc.getInputStream().readAllBytes());
		} catch (IOException e) {
			logoc_b64 = "https://saic.uv.es/public/logos/C"+Integer.toString(lugar)+".png";
		}
		img.put("logo_centre", logoc_b64);
		img.put("logo_uv", logouv_b64);
	}
	
	private Document iterateLoopAttr(Integer idCentre, Integer curs, Document doc, HashMap<String, String> context) {
		Elements loop_elements = doc.select("*[data-loop]");
		for(Element e : loop_elements) {
			String Tambit = e.attr("data-loop");
			List<OrganDTO> tits = core.getTitulacionsByCentreTambit(idCentre, Tambit);
			for(OrganDTO org : tits) {
				context.clear();
				Element e_copy = e.clone();
				getTemplateData(org.getLugar(), org.getLugar2(), curs, context);
				context.put("titulacio_loop", org.getNomVal());
				Elements ielements = e_copy.select("*:matchesWholeOwnText(\\{\\{([^\\}]*)\\}\\})");
				for(Element ielem : ielements) {
					String t = ielem.html().replace("{", "").replace("}", "");
					String i = this.formatValue(context.get(t));
					if(i != null) {
						if(ielem.tagName().equals("td")) {
							ielem.attr("class", "mceEditable");
							ielem.attr("id", "ind_"+t);
							ielem.html(i);
						}
						else {
							ielem.html(i);
						}
					}
					else {
						if(ielem.tagName().equals("td")) {
							ielem.attr("class", "mceEditable");
							ielem.attr("id", "ind_"+t);
							ielem.html("");
						}
						else {
							ielem.html("");
						}
					}
				}		
				e.after(e_copy);
			}
			e.remove();
		}
		context.clear();
		return doc;
	}
	
	private String iterateLoopTag(String template, Integer idCentre, String Tambit, Integer curs, HashMap<String, String> context) {
		Pattern pattern = Pattern.compile(".*<p>\\{\\[loop\\]\\}</p>([\\S\\s]*)<p>\\{\\[endloop\\]\\}</p>.*");
		Matcher matcher = pattern.matcher(template);
		String replaced = "";
		
		while(matcher.find()) {
			String group = matcher.group(1);
			Document tab = Jsoup.parse(group);
			List<OrganDTO> tits = core.getTitulacionsByCentreTambit(idCentre, Tambit);
			for(OrganDTO t : tits) {
				Document tabcopy = Jsoup.parse(tab.html());
				context.clear();
				getTemplateData(t.getLugar(), t.getLugar2(), curs, context);
				context.put("titulacio_loop", t.getNomVal());
				tabcopy = this.replaceValuesLoop(tabcopy, context);
				replaced += tabcopy.html()+"<p></p>";
			}
			template = template.replace("<p>{[loop]}</p>"+group+"<p>{[endloop]}</p>", replaced);
		}
		context.clear();
		return template;
	}
	
	private Document replaceValues(Document doc, HashMap<String, String> context, HashMap<String, String> header, HashMap<String, String> img) {
		Elements elems = doc.select("*:matchesWholeOwnText(\\{\\{([^\\}]*)\\}\\})");
		for(Element e : elems) {
			String t = e.html().replace("{", "").replace("}", "");
			if(header.containsKey(t)) {
				e.html(header.get(t));
			}
			else if(img.containsKey(t)) {
				Element x = new Element("img");
				x.attr("src", img.get(t));
				x.attr("style", "display:block; margin-left:auto; margin-right:auto; width:100px; max-width:100px;");
				x.attr("class", "logo");
				e.html("");
				e.appendChild(x);	
			}
			else {
				String i = this.formatValue(context.get(t));
				if(i != null) {
					if(e.parent().tagName().equals("td")) {
						e.parent().attr("class", "mceEditable");
						e.parent().attr("id", "ind_"+t);
						e.parent().attr("style", e.attr("style"));
						e.parent().html(i);
					}
					else {
						e.attr("class", "mceEditable");
						e.attr("ind", "ind_"+t);
						e.html(i);
					}
				}
				else {
					if(e.parent().tagName().equals("td")) {
						e.parent().attr("class", "mceEditable");
						e.parent().attr("id", "ind_"+t);
						e.parent().attr("style", e.attr("style"));
						e.parent().html("");
					}
					else {
						e.attr("class", "mceEditable");
						e.attr("ind", "ind_"+t);
						e.html("");
					}
				}
			}
		}
		return doc;
	}
	
	private void getTemplateData(Integer idTitulacio, Integer idCentre, Integer curs, HashMap<String, String> context) {
		
		/*  Indicadores del data warehouse */
		try {
			List<Indicador> indicadores;
			indicadores = core.getFromTitulacion(idTitulacio, curs);
			for(Indicador i : indicadores) { 
				context.put(i.getIndicador(), i.getValor());
			}
		} 
		catch(Exception e) { }
		
		/*  Indicadores de encuestas y otros almacenados en BD  */
		IndicadorEnquestaDTO indicadorEnquestaDTO = new IndicadorEnquestaDTO(idTitulacio, idCentre, curs);
		List<IndicadorEnquestaValorDTOImp> enquestesT = core.getAllInds2(indicadorEnquestaDTO);
				
		for(IndicadorEnquestaValorDTOImp i : enquestesT) {
			String indicador = i.getAmbit().toLowerCase().equals("t") ? (i.getEnquesta().toLowerCase()+"_"+i.getIndicador().toLowerCase()) : (i.getEnquesta().toLowerCase()+"_"+i.getIndicador().toLowerCase()+"_"+i.getAmbit().toLowerCase());
			if(i.getNum() == null) {
				indicador = i.getTipus().toLowerCase().equals("avg") ? indicador : (indicador += "_"+i.getTipus().toLowerCase());		
				indicador = i.getCursd() == null ? indicador : (indicador += "_"+i.getCursd().toLowerCase());
				context.put(indicador, formatValue(i.getValor()));
			}
			else {
				context.put(indicador, i.getNum());
			}
			
		}
	}
	
	private Document replaceValuesLoop(Document doc, HashMap<String, String> context) {
		Elements elems = doc.select("*:matchesWholeOwnText(\\{\\{([^\\}]*)\\}\\})");
		for(Element e : elems) {
			String t = e.html().replace("{", "").replace("}", "");
			String i = this.formatValue(context.get(t));
			if(i != null) {
				if(e.parent().tagName().equals("td")) {
					e.parent().attr("class", "mceEditable");
					e.parent().attr("id", "ind_"+t);
					e.parent().attr("style", e.attr("style"));
					e.parent().html(i);
				}
				else {
					e.attr("class", "mceEditable");
					e.attr("ind", "ind_"+t);
					e.html(i);
				}
			}
			else {
				if(e.parent().tagName().equals("td")) {
					e.parent().attr("class", "mceEditable");
					e.parent().attr("id", "ind_"+t);
					e.parent().attr("style", e.attr("style"));
					e.parent().html("");
				}
				else {
					e.attr("class", "mceEditable");
					e.attr("ind", "ind_"+t);
					e.html("");
				}
			}
		}
		return doc;
	}
	
	private String formatValue(String v) {	
		if(v == null) return ""; 
		if(v.isEmpty() | v.isBlank()) return "";
		if(v.equals("NP")) return "NP";
				
		try {
		    double d = Double.parseDouble(v);
		    return (Integer.toString((int)d).equals(v) ? v : df.format(d).replace(",", "."));
		} 
		catch (NumberFormatException e) { }
		
		if(v.endsWith("%") && v.startsWith(".")) {
			return "0"+v;
		}
		
		return v;
		
	}
	
	public String savePDF(String content, BigInteger idtascai) throws IOException, InterruptedException {
		content = content.replace("<p><!-- pagebreak --></p>", "<p class=\"pagebreak\"><!-- pagebreak --></p>");
		Document d = Jsoup.parse(content, "UTF8");
		d.head().append("<style>"
				+ "@page{size: 297mm 210mm;} "
				+ "html{ max-width:297mm; max-height:210mm; margin:0; padding:0; transform:scale(0.925); transform-origin:left top; } "
				+ "table { padding:2px !important; }"
				+ "th, td { padding:2px !important; }"
				+ ".pagebreak { page-break-before:always; }"
				+ "</style>");
		List<Element> trs = d.getElementsByTag("tr");
		for(Element t : trs) {
			if(t.hasAttr("height")) {
				t.removeAttr("height");
			}
			if(t.hasAttr("style")) {
				String style = t.attr("style");
				style = style.replaceAll("height:[ \\d.pxcmin]{1,};", "");
				t.attr("style", style);
			}
		}
		List<Element> tds = d.getElementsByTag("td");
		for(Element t : tds) {
			if(t.hasAttr("height")) {
				t.removeAttr("height");
			}
			if(t.hasAttr("style")) {
				String style = t.attr("style");
				style = style.replaceAll("height:[ \\d.pxcmin]{1,};", "");
				t.attr("style", style);
			}
		}
		List<Element> ignore = d.getElementsByClass("pdfignore");
		for(Element e: ignore) {
			e.remove();
		}
		
		InstanciaTascaDTO ita = core.findInstanciaTascaById(idtascai);
		if(ita.getTasca().getNomRol().equals("u_uq")) {
			d.body().append(this.parseComments(d.html()));
		}
		
		String basecommand = "google-chrome --headless --disable-gpu --no-pdf-header-footer --run-all-compositor-stages-before-draw --no-sandbox";
		String dst = idtascai.toString()+".pdf";		
		File src = File.createTempFile("saic-pdfexport-", ".tmp.html", new File(tmpPath));
		src.deleteOnExit();
		PrintWriter out = new PrintWriter(src.getAbsolutePath());
		out.println(d.html());
		out.flush();
		out.close();
		System.out.println(basecommand+" --print-to-pdf='"+filePath+dst+"' "+src.getAbsolutePath());
		
		ProcessBuilder pb = new ProcessBuilder("bash", "-c", basecommand+" --print-to-pdf='"+filePath+dst+"' "+src.getAbsolutePath());
		Process pr = pb.start();
		pr.waitFor();
		src.delete();
		
		return dst;
	}
	
	public byte[] toPDF(String content, Optional<BigInteger> idtascai) throws IOException, InterruptedException {
		content = content.replace("<p><!-- pagebreak --></p>", "<p class=\"pagebreak\"><!-- pagebreak --></p>");
		Document d = Jsoup.parse(content, "UTF8");
		d.head().append("<style>"
				+ "@page{size: 297mm 210mm;} "
				+ "html{ max-width:297mm; max-height:210mm; margin:0; padding:0; transform:scale(0.925); transform-origin:left top; } "
				+ "table { padding:2px !important; }"
				+ "th, td { padding:2px !important; }"
				+ ".pagebreak { page-break-before:always; }"
				+ "</style>");
		List<Element> trs = d.getElementsByTag("tr");
		for(Element t : trs) {
			if(t.hasAttr("height")) {
				t.removeAttr("height");
			}
			if(t.hasAttr("style")) {
				String style = t.attr("style");
				style = style.replaceAll("height:[ \\d.pxcmin]{1,};", "");
				t.attr("style", style);
			}
		}
		List<Element> tds = d.getElementsByTag("td");
		for(Element t : tds) {
			if(t.hasAttr("height")) {
				t.removeAttr("height");
			}
			if(t.hasAttr("style")) {
				String style = t.attr("style");
				style = style.replaceAll("height:[ \\d.pxcmin]{1,};", "");
				t.attr("style", style);
			}
		}
		List<Element> ignore = d.getElementsByClass("pdfignore");
		for(Element e: ignore) {
			e.remove();
		}
		
		if(idtascai.isPresent()) {
			InstanciaTascaDTO ita = core.findInstanciaTascaById(idtascai.get());
			if(ita.getTasca().getNomRol().equals("u_uq")) {
				d.body().append(this.parseComments(d.html()));
			}
		}
		
		d.body().append(this.parseComments(d.html()));
				
		String basecommand = "google-chrome --headless --disable-gpu --no-pdf-header-footer --run-all-compositor-stages-before-draw --no-sandbox";	
		File dst = File.createTempFile("saic-pdfpreview-", ".tmp.pdf", new File(tmpPath));
		File src = File.createTempFile("saic-pdfpreview-", ".tmp.html", new File(tmpPath));
		src.deleteOnExit();
		dst.deleteOnExit();
		
		PrintWriter out = new PrintWriter(src.getAbsolutePath());
		out.println(d.html());
		out.flush();
		out.close();
		ProcessBuilder pb = new ProcessBuilder("bash", "-c", basecommand+" --print-to-pdf='"+dst+"' "+src.getAbsolutePath());
		Process pr = pb.start();
		pr.waitFor();
				
		byte[] bytes = Files.readAllBytes(dst.toPath());
		src.delete();
		dst.delete();
		
		return bytes;
	}
	
	private String parseComments(String content) throws JsonParseException, JsonMappingException, IOException {
		// <!--tinycomments\|2\.1\|data:application\/json;base64,([A-Za-z0-9]*)=-->
		Pattern pattern = Pattern.compile("\\<\\!\\-\\-tinycomments\\|2\\.1\\|data\\:application\\/json\\;base64\\,([A-Za-z0-9\\/\\+\\\\]*)\\=*\\-\\-\\>");
		Matcher matcher = pattern.matcher(content);
		String rawComments = "";
		if(matcher.find()) {
			byte[] decoded = Base64.getDecoder().decode(matcher.group(1));
			rawComments = new String(decoded, "UTF-8");
			
			if(rawComments.length() < 10) {
				return "";
			}
			
			rawComments = rawComments.replaceAll("^.", "[");
			rawComments = rawComments.replaceAll(".$", "]");
			rawComments = rawComments.replaceAll("\"mce-conversation\\_\\d*\":", "");
			
			ObjectMapper mapper = new ObjectMapper();
			List<PlantillaConversation> comments = mapper.readValue(rawComments, new TypeReference<List<PlantillaConversation>>(){});
			
			String tabComments =  "<p class=\"pagebreak\"><!-- pagebreak --></p>"
								+ "<h3>COMENTARIOS GENERADOS DURANTE LA REVISIÓN DEL DOCUMENTO</h3>"
								+ "<h3><small>Nota: Esta página no será visible en la versión final publicada del documento.</small></h3>"
								+ "<table style=\"border-collapse: collapse; width: 297mm; border-width: 1px; border-spacing: 0px; border-color: rgb(149, 165, 166); margin-left: 0px; margin-right: auto;\" border=\"1\" width=\"297mm\" cellspacing=\"0\" cellpadding=\"8\">"
								+ "<thead>"
								+ "    <tr style=\"padding: 0px; border-width: 1px; border-color: rgb(149, 165, 166);\" bgcolor=\"#153d63\">"
								+ "      <th style=\"padding: 0px; border-width: 1px; border-color: rgb(149, 165, 166); color:white;\" scope=\"col\">Usuario</th>"
								+ "      <th style=\"padding: 0px; border-width: 1px; border-color: rgb(149, 165, 166); color:white;\" scope=\"col\">Nombre</th>"
								+ "      <th style=\"padding: 0px; border-width: 1px; border-color: rgb(149, 165, 166); color:white;\" scope=\"col\">Comentario</th>"
								+ "    </tr>"
								+ "  </thead>"
								+ "<tbody>";
			for(PlantillaConversation conv: comments) {
				for(PlantillaComentario c: conv.getComments()) {
					tabComments += ("<tr style=\"padding: 0px; border-width: 1px; border-color: rgb(149, 165, 166);\"> "
								  + "  <td style=\"padding: 0px; border-width: 1px; border-color: rgb(149, 165, 166);\">"+c.getAuthor()+"</td>"
								  + "  <td style=\"padding: 0px; border-width: 1px; border-color: rgb(149, 165, 166);\">"+c.getAuthorName()+"</td>"
								  + "  <td style=\"padding: 0px; border-width: 1px; border-color: rgb(149, 165, 166);\">"+c.getContent()+"</td>"
								  + "</tr> ");
				}
			}
			tabComments += "</tbody></table>";
			return tabComments;
		}
		
		return "";
	}
	
	public Document replaceSection(String[] opcions, InstanciaTascaDTO it, Document doc) {
		if(opcions.length > 0) {
			NomProcesOrganDTO nomProcesOrganDTO = new NomProcesOrganDTO(opcions[0], 
																		it.getInstancia().getTlugar(), 
																		it.getInstancia().getLugar(), 
																		it.getInstancia().getCentre(), 
																		it.getInstancia().getTitulacio());
			InstanciaTascaDTO itOld = core.getReportFromNomProcesOrgan(nomProcesOrganDTO);
			try {
				Document doc2 = Jsoup.parse(itOld.getText());
				Elements target = null;
				Elements source = null;
				if(opcions.length == 2) {
					target = this.extractTemplateAnchor(doc2, true, opcions[1]);
					source = this.extractTemplateAnchor(doc, false, opcions[1]);
				}
				else {
					target = this.extractTemplateAnchor(doc2, true, opcions[1], opcions[2]);
					source = this.extractTemplateAnchor(doc, false, opcions[1], opcions[2]);
				}
				
				if(target == null || source == null) {
					return doc;
				}
				Element e = source.get(0);
				e.before(this.clear(target.outerHtml()));
				source.remove();
			}
			catch(NullPointerException e){
				System.out.println("No previous version found for "+it.getIdInstanciaTasca().toString());
			}
			
		}
		return doc;
	}
	
	public Elements extractTemplateAnchor(Document doc, Boolean target, String... args) {
		// <a id="pam"></a>		
		
		/* Hardcoded to fix anchor positions in existing templates */
		try{
			String anchor_ini = args[0];
			Element eFirst = doc.select("p:has(a#"+anchor_ini+")").first();
			Elements elems = eFirst.nextElementSiblings();
			Elements table = elems.select("table:not(.pdfignore)");
			return table;
		}
		catch(Exception e){
			return null;
		}
		
		/* Use this code in future */
		/* 
		if(args.length == 2){
			String anchor_end = args[1];
			Element eLast = doc.select("p:has(a#"+anchor_end+")").first();
			boolean remove = false;
			for(Iterator<Element> iter = elems.iterator(); iter.hasNext();){
				Element x = iter.next();
				if(remove){
					iter.remove();
				}
				if(x.id().equals(anchor_end) || x.html().contains(anchor_end)){
					remove = true;
				}
			}
		}
		*/
		
	}

	public String clear(String html){
		return html.replaceAll("data-mce-style=\"[ ,;:.\\d\\w\\(\\)\\#\\%-]{1,}\"", "")
				   .replaceAll("font-family:[ \\d\\w-,]{1,};", "")
				   .replaceAll("font-size:[ \\d\\w,\\.]{1,};", "")
				   .replaceAll("\\<\\!\\-\\-tinycomments\\|2\\.1\\|data\\:application\\/json\\;base64\\,[A-Za-z0-9\\/\\+\\\\]*\\=*\\-\\-\\>", "")
				   .replaceAll("data\\-mce\\-annotation\\-uid\\=\"mce\\-conversation\\_[a-zA-Z0-9]+\"", "")
				   .replaceAll("data\\-mce\\-annotation\\=\"tinycomments\"", "")
				   .replaceAll("class=\"mce-annotation\"", "");
	}
		
	public static String asString(Resource resource) {
        try (Reader reader = new InputStreamReader(resource.getInputStream(), "UTF-8")) {
            return FileCopyUtils.copyToString(reader);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }
	
	public static String readFileToString(String path) {
	    ResourceLoader resourceLoader = new DefaultResourceLoader();
	    Resource resource = resourceLoader.getResource(path);
	    return asString(resource);
	}
		
}
