package OOXMLconvert;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import org.jdom.Element;

/**
 *
 * @author ferou
 */
public class ElementPicture {
	
	/** XML element obsahujúci obrázok */
	private Element elDrawing = null;
	
	// vlastnosti obrazka
	/** spôsob vloženia (floating/inline) */
	private String anchor = "";
	/** šírka obrázku */
	private int width = 0;
	/** výška obrázku */
	private int height = 0;
	/** hrúbka orámovania obrázka */
	private int borderWidth = 0;
	/** CSS štýl orámovania obrázka */
	private String borderStyle = "";
	/** CSS farba orámovania obrázka */
	private String borderColor = "";
	/** kam odkazuje link, ak je obrázok odkazom */
	private String linkURL = "";

	/** relatívna cesta k obrázku */
	private String relPath = "";
	/** absolútna cesta k obrázku */
	private String absPath = "";

	/**
	 * Konštruktor triedy na prácu s obrázkom.
	 * @param elDrawing XML element obsahujúci "drawing" element
	 */
	public ElementPicture(Element elDrawing) {
		this.elDrawing = elDrawing;
	}
	
	/**
	 * Funkcia uloží obrázok do potrebného formátu.
	 * @param image obrázok, ktorý chceme uložiť
	 * @param imgType typ obrázka
	 * @return <i>true</i> ak nenastane chyba, <i>false</i> inak
	 */
	private boolean saveImage(BufferedImage image, String imgType) {
		try {
			// vytvor súbor, kam sa obrázok uloží a zároveň vytvor všetky potrebné adresáre
			File outputFile = new File(this.absPath);
			outputFile.getParentFile().mkdirs();
			// na zaklade typu povodneho obrazka, uloz do zodpovedajuceho formatu
			if (imgType.trim().equals("jpg") || imgType.trim().equals("jpeg")) {
				// uloz obrazok ako jpg
				Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
				ImageWriter writer = (ImageWriter)iter.next();
				ImageWriteParam iwp = writer.getDefaultWriteParam();
				iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
				// nastav kvalitu
				float jpegQuality = Global.ooxmlParams.jpegQuality;
				iwp.setCompressionQuality(jpegQuality);
				// zapis do vystupneho suboru
				FileImageOutputStream output = new FileImageOutputStream(outputFile);
				writer.setOutput(output);
				IIOImage iioimage = new IIOImage(image, null, null);
				writer.write(null, iioimage, iwp);
				writer.dispose();
			}
			else if (imgType.trim().equals("png")) {
				// uloz obrazok ako png
				ImageIO.write(image, "png", outputFile);
			}
			else if (imgType.trim().equals("gif")) {
				// uloz obrazok ako gif
				ImageIO.write(image, "gif", outputFile);
			}
			else if (imgType.trim().equals("png")) {
				// uloz obrazok ako png
				ImageIO.write(image, "png", outputFile);
			}
			return true;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (saveImage): " + e.toString());
			return false;
		}
	}
	
	/**
	 * Funkcia pripraví obrázok.
	 * @param imgFile súbor s obrázkom
	 * @return <i>true</i> ak nenastane chyba, <i>false</i> inak
	 */
	private boolean prepareImage(File imgFile) {
		try {
			// priprav meno obrazka
			this.relPath = Global.ooxmlParams.imgDir + imgFile.getName();
			this.absPath = Functions.addEndSlash(Global.ooxmlParams.xhtmlDest.getParent()) + this.relPath;
			// zmen vsetky lomitka v ceste aby sa to na webe zobrazovalo spravne
			this.relPath = Functions.setWebSlashes(this.relPath);
			// povodny obrazok
			BufferedImage img = ImageIO.read(imgFile);
			// novy vysledny obrazok
			BufferedImage scaledImage = new BufferedImage(this.width, this.height, BufferedImage.TYPE_INT_RGB);
			// zmensi obrazok na nastavenu velkost
			Graphics2D graphics = scaledImage.createGraphics();
			graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
			graphics.drawImage(img, 0, 0, this.width, this.height, null);
			// pripona obrazka (aby sme mohli tak isto ulozit vysledny obrazok)
			String imgType = Functions.getExtension(imgFile);
			saveImage(scaledImage, imgType);
			return true;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (prepareImage): " + e.toString());
			return false;
		}
	}
	
	/**
	 * Funkcia prejde daným solidFill elementom a zistí farbu čiary borderu.
	 * @param solidFillEl vstupný XML solidFill element
	 */
	private void parseSolidFillElement(Element solidFillEl) {
		try {
			List els = solidFillEl.getChildren();
			Iterator elsIterator = els.iterator();
			while (elsIterator.hasNext()) {
				Element el = (Element) elsIterator.next();
				if (el.getName().trim().equals("srgbClr")) {
					// farba je v HEX formate
					String c = el.getAttributeValue("val");
					if (c.trim().equals("") || c.trim().equals("auto")) {
						c = "000000";
					}
					this.borderColor = "#" + c;
				}
				else if (el.getName().trim().equals("schemeClr")) {
					String scheme = el.getAttributeValue("val");
					String color = "#" + Global.ooxmlTheme.getColor(scheme);
					if (color.trim().length() == 7) {
						this.borderColor = color;
					}
					else {
						this.borderColor = "#000000";
					}
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseSolidFillElement): " + e.toString());
		}
	}
	
	/**
	 * Funkcia na základe border štýlu nastaví CSS border štýl
	 * @param xmlBorderStyle ooXML border štýl
	 */
	private void determineBorderStyle(String xmlBorderStyle) {
		try {
			xmlBorderStyle = xmlBorderStyle.trim();
			if (xmlBorderStyle.equals("dash")) {
				this.borderStyle = "dashed";
			}
			else if (xmlBorderStyle.equals("dot")) {
				this.borderStyle = "dotted";
			}
			else if (xmlBorderStyle.equals("solid")) {
				this.borderStyle = "solid";
			}
			else if (xmlBorderStyle.contains("dash")) {
				this.borderStyle = "dashed";
			}
			else if (xmlBorderStyle.contains("dot")) {
				this.borderStyle = "dotted";
			}
			else {
				this.borderStyle = "solid";
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (determineBorderStyle): " + e.toString());
		}
	}
	
	/**
	 * Funkcia prejde daným "spPr" XML elementom a zapíše CSS štýl
	 * @param spPrEl vstupný "spPr" XML element
	 */
	private void parseShapeElement(Element spPrEl) {
		try {
			// default na styl ramceka
			this.borderStyle = "solid";
			
			Element lnEl = spPrEl.getChild("ln", Global.ans);
			if (lnEl != null) {
				int lnWidth = Functions.intValDef(lnEl.getAttributeValue("w"),0);
				this.borderWidth = Functions.emuToPx(lnWidth);
				List els = lnEl.getChildren();
				Iterator elsIterator = els.iterator();
				while (elsIterator.hasNext()) {
					Element el = (Element) elsIterator.next();
					if (el.getName().trim().equals("solidFill")) {
						parseSolidFillElement(el);
					}
					else if (el.getName().trim().equals("prstDash")) {
						String xmlBorderStyle = el.getAttributeValue("val");
						determineBorderStyle(xmlBorderStyle);
					}
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseShapeElement): " + e.toString());
		}
	}
	
	/**
	 * Funkcia prejde daným "docPr" XML elementom a prípradne pripraví link
	 * @param docPrEl vstupný "docPr" XML element
	 */
	private void parseDocPrElement(Element docPrEl) {
		try {
			// default na styl ramceka
			this.borderStyle = "solid";
			
			Element hlinkClickEl = docPrEl.getChild("hlinkClick", Global.ans);
			if (hlinkClickEl != null) {
				String id = hlinkClickEl.getAttributeValue("id",Global.rns);
				String destURL = Global.ooxmlRels.hyperlinks.get(id);
				if ((destURL != null) && (!destURL.trim().isEmpty())) {
					this.linkURL = destURL;
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseDocPrElement): " + e.toString());
		}
	}
	
	/**
	 * Funkcia prejde daným "pic" elementom.
	 * @param picEl XML "pic" element obsahujúci obrázok
	 * @return <i>true</i> ak nenastala chyba, <i>false</i> inak
	 */
	private boolean parsePicture(Element picEl) {
		try {
			List els = picEl.getChildren();
			Iterator elsIterator = els.iterator();
			while (elsIterator.hasNext()) {
				Element el = (Element) elsIterator.next();
				if (el.getName().trim().equals("blipFill")) {
					String picId = el.getChild("blip", Global.ans).getAttributeValue("embed", Global.rns);
					if (Global.ooxmlRels.imgFiles.containsKey(picId)) {
						File imgFile = Global.ooxmlRels.imgFiles.get(picId);
						if (imgFile.exists()) {
							if (!prepareImage(imgFile)) {
								return false;
							}
						}
						else {
							return false;
						}
					}
				}
				if (el.getName().trim().equals("nvPicPr")) {
					// picture properties zatial nepotrebujeme
				}				
				if (el.getName().trim().equals("spPr")) {
					// prejdi "shape properties" elementom
					parseShapeElement(el);
				}
			}
			return true;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parsePicture): " + e.toString());
			return false;
		}
	}
	
	/**
	 * Funkcia prejde daným positionH elementom a zistí jeho pozíciu (offset/zarovnanie)
	 * @param posEl positionH element
	 */
	private void determinePosition(Element posEl) {
		try {
			Element elChild = posEl.getChild("posOffset", Global.wpns);
			if (elChild != null) {
				int offset = Functions.emuToCm(Functions.intValDef(elChild.getText(),0));
				// ak je offset viac ako 8 cm (standardny stred) vyhodnot ako zarovnanie doprava
				if (offset > 8) {
					this.anchor = "floatingRight";
				}
			}
			else {
				elChild = posEl.getChild("align", Global.wpns);
				if (elChild != null) {
					String align = elChild.getText();
					if (align.equals("right")) {
						this.anchor = "floatingRight";
					}
				}
			}
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (determinePosition): " + e.toString());
		}
	}
	
	/**
	 * Funkcia prejde daným potomkom "drawing" XML elementu.
	 * @return <i>true</i> ak je element vizuálne prázdny <i>false</i> inak
	 */
	private boolean parseDrawingElements(Element el) {
		try {
			if (el.getName().trim().equals("extent")) {
				// precitaj rozmery obrazka
				this.width = Functions.emuToPx(Functions.intValDef(el.getAttributeValue("cx"),1));
				if (this.width < 1) {
					this.width = 1;
				}
				this.height = Functions.emuToPx(Functions.intValDef(el.getAttributeValue("cy"),1));
				if (this.height < 1) {
					this.height = 1;
				}
			}
			else if (el.getName().trim().equals("positionH")) {
				// zisti ci ma element nastaveny offset alebo zarovnanie
				determinePosition(el);
			}
			else if (el.getName().trim().equals("positionV")) {
				// vertikalne umiestnenie nas nezaujima
			}
			else if (el.getName().trim().equals("docPr")) {
				// element moze obsahovat informaciu o tom, ze obrazok je link
				parseDocPrElement(el);
			}
			// wrapping
			else if (el.getName().trim().equals("wrapTopAndBottom")) {
				this.anchor = "newLine";
			}
			else if (el.getName().trim().equals("graphic")) {
				try {
					Element e1 = el.getChild("graphicData", Global.ans);
					if (e1 == null) {
						return false;
					}
					Element e2 = e1.getChild("pic",Global.picns);
					if (e2 == null) {
						return false;
					}
					if (!parsePicture(e2)) {
						return false;
					}
				}
				catch (NullPointerException e) {
					return false;
				}
			}
			return true;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseDrawing): " + e.toString());
			return false;
		}
	}
	
	/**
	 * Funkcia prejde daným "drawing" XML elementom.
	 * @return <i>true</i> ak je element vizuálne prázdny <i>false</i> inak
	 */
	private boolean parseDrawing() {
		try {
			// zistime akym sposobom sa obrazok vklada do textu
			Element anchoringEl = (Element)elDrawing.getChildren().get(0);
			if (anchoringEl.getName().trim().equals("inline")) {
				this.anchor = "inline";
			}
			else if (anchoringEl.getName().trim().equals("anchor")) {
				this.anchor = "floatingLeft";
			}
			// prejdi vsetkych potomkov
			List els = anchoringEl.getChildren();
			Iterator elsIterator = els.iterator();
			while (elsIterator.hasNext()) {
				Element el = (Element) elsIterator.next();
				if (!parseDrawingElements(el)) {
					return false;
				}
			}
			return true;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (parseDrawing): " + e.toString());
			return false;
		}
	}
	
	/**
	 * Funkcia, ktorá prejde daným "elDrawing" XML elementom a vráti výstupné XHTML
	 * @return výstupné XHTML - list elementov
	 */
	public LinkedList<Element> getXHTMLImage() {
		try {
			LinkedList<Element> retEl = new LinkedList<Element>();
			if (parseDrawing()) {
				
				// CSS styly
				String styles = "";
				
				// vytvor XHTML "img" element
				Element xhtmlImg = new Element("img");
				
				// nastav umiestnenie obrazku
				xhtmlImg.setAttribute("src", this.relPath);
				// nastav vysku a sirku
				xhtmlImg.setAttribute("width", this.width + "");
				xhtmlImg.setAttribute("height", this.height + "");
				// ako alt nastav nazov obrazku
				xhtmlImg.setAttribute("alt", this.relPath.substring(this.relPath.lastIndexOf("/")+1));
				
				// ak je nastaveny anchor na floating, tak nastav v styloch float
				if (this.anchor.equals("floatingLeft")) {
					styles += "float: left;";
				}
				else if (this.anchor.equals("floatingRight")) {
					styles += "float: right;";
				}
				else if(this.anchor.equals("newLine")) {
					retEl.add(new Element("br"));
				}
				
				// ak je nastaveny border
				if ((this.borderWidth > 0) && (!this.borderColor.trim().isEmpty()) && (!this.borderStyle.trim().isEmpty())) {
					styles += "border: " + this.borderWidth + "px " + this.borderStyle + " " + this.borderColor + ";";
				}
				else {
					styles += "border: 0;";
				}
				
				if (Global.ooxmlParams.formatPreservingLevel == 2) {
					styles += "margin: 5px;";
				}
				
				// nastav CSS styly
				if (Global.ooxmlParams.formatPreservingLevel > 0 && !styles.trim().isEmpty()) {
					String className = Global.cssOutput.addClassN(".img", styles.trim());
					xhtmlImg.setAttribute("class", className);
				}
				
				// ak je obrazok odkazom, tak ho daj do elementu "a" a vrat ten
				if ((this.linkURL != null) && (!this.linkURL.trim().isEmpty())) {
					Element elA = new Element("a");
					elA.setAttribute("href", this.linkURL);
					elA.addContent(xhtmlImg);
					retEl.add(elA);
				}
				else {
					retEl.add(xhtmlImg);					
				}
				
				return retEl;
			}
			return null;
		}
		catch (Exception e) {
			System.err.println(this.getClass().getName() + " (getXHTMLImage): " + e.toString());
			return null;
		}
	}

}
