package com.coreoz.ppt;

import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.sl.usermodel.Hyperlink;
import org.apache.poi.sl.usermodel.SimpleShape;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFAutoShape;
import org.apache.poi.xslf.usermodel.XSLFPictureShape;
import org.apache.poi.xslf.usermodel.XSLFShape;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.apache.poi.xslf.usermodel.XSLFTable;
import org.apache.poi.xslf.usermodel.XSLFTableCell;
import org.apache.poi.xslf.usermodel.XSLFTableRow;
import org.apache.poi.xslf.usermodel.XSLFTextParagraph;
import org.apache.poi.xslf.usermodel.XSLFTextShape;

import lombok.SneakyThrows;

/**
 * Variable format in the PPT: $/variableName/
 */
public class PptTemplates {

	@SneakyThrows
	public XMLSlideShow process(InputStream pptData, PptMapper mapper) {
		try(XMLSlideShow ppt = new XMLSlideShow(pptData)) {
			processPpt(ppt, mapper);
			
			return ppt;
		}
	}
	
	public void processPpt(XMLSlideShow ppt, PptMapper mapper) {
		for(XSLFSlide slide : ppt.getSlides()) {
			Iterator<XSLFShape> shapeIterator = slide.getShapes().iterator();
			while(shapeIterator.hasNext()) {
				if(processShape(ppt, slide, shapeIterator.next(), mapper)) {
					shapeIterator.remove();
				}
			}
		}
	}
	
	// internal
	
	/**
	 * Handles shape modification
	 * @return true is the shape should be removed
	 */
	private boolean processShape(XMLSlideShow ppt, XSLFSlide slide, XSLFShape shape, PptMapper mapper) {
		if(shape instanceof XSLFTextShape) {
			return processTextShape((XSLFTextShape) shape, mapper);
		}
		if(shape instanceof XSLFTable) {
			return processTableShape((XSLFTable) shape, mapper);
		}
		if(shape instanceof XSLFAutoShape) {
			return processGeometryShape((XSLFAutoShape) shape, mapper);
		}
		if(shape instanceof XSLFPictureShape) {
			return processImageShape(ppt, slide, (XSLFPictureShape) shape, mapper);
		}
		return false;
	}
	
	private boolean processImageShape(XMLSlideShow ppt, XSLFSlide slide, XSLFPictureShape imageShape, PptMapper mapper) {
		if(shouldHide(imageShape, mapper)) {
			return true;
		}
		
		
		return false;
	}

	private boolean processGeometryShape(XSLFAutoShape geometryShape, PptMapper mapper) {
		if(shouldHide(geometryShape, mapper)) {
			return true;
		}
		
		processTextParagraphs(geometryShape.getTextParagraphs(), mapper);
		
		return false;
	}
	
	private boolean processTableShape(XSLFTable tableShape, PptMapper mapper) {
		for(XSLFTableRow row : tableShape.getRows()) {
			for(XSLFTableCell cell : row.getCells()) {
				processTextParagraphs(cell.getTextParagraphs(), mapper);
			}
		}
		
		return false;
	}
	
	private boolean processTextShape(XSLFTextShape textShape, PptMapper mapper) {
		if(shouldHide(textShape, mapper)) {
			return true;
		}
		
		processTextParagraphs(textShape.getTextParagraphs(), mapper);
		
		return false;
	}

	private void processTextParagraphs(List<XSLFTextParagraph> paragraphs, PptMapper mapper) {
		for(XSLFTextParagraph paragraph : paragraphs) {
			PptParser.replaceTextVariable(paragraph, mapper);
		}
	}
	
	private boolean shouldHide(SimpleShape<?, ?> simpleShape, PptMapper mapper) {
		return parseHyperlinkVariale(simpleShape)
			.flatMap(shapeVariable ->
				mapper.hideMapping(shapeVariable.getName(), shapeVariable.getArg1())
			)
			.orElse(false);
	}
	
	private Optional<PptVariable> parseHyperlinkVariale(SimpleShape<?, ?> simpleShape) {
		Hyperlink<?, ?> link = simpleShape.getHyperlink();
		if(link != null && link.getTypeEnum() == HyperlinkType.URL) {
			return PptParser.parse(link.getAddress());
		}
		return Optional.empty();
	}
	
}

@@ -864,16 +864,27 @@
 ableName
+:'argument'
 /%0A */%0Apu
@@ -3114,25 +3114,35 @@
 per) %7B%0A%09%09for
-(
+ (Iterator%3C
 XSLFTextPara
@@ -3150,35 +3150,223 @@
 raph
- paragraph : paragraphs) %7B%0A
+%3E iterator = paragraphs.iterator(); iterator.hasNext();) %7B%0A%09%09%09XSLFTextParagraph paragraph = iterator.next();%0A%09%09%09%0A%09%09%09if(shouldHide(PptParser.parse(paragraph.getText()), mapper)) %7B%0A%09%09%09%09iterator.remove();%0A%09%09%09%7D else %7B%0A%09
 %09%09%09P
@@ -3410,24 +3410,29 @@
 h, mapper);%0A
+%09%09%09%7D%0A
 %09%09%7D%0A%09%7D%0A%09%0A%09pr
@@ -3516,16 +3516,27 @@
 %09return 
+shouldHide(
 parseHyp
@@ -3561,16 +3561,129 @@
 leShape)
+, mapper);%0A%09%7D%0A%09%0A%09private boolean shouldHide(Optional%3CPptVariable%3E variable, PptMapper mapper) %7B%0A%09%09return variable
 %0A%09%09%09.fla

Handle paragraph hiding