package org.etsi.mts.tdl.tools.to.docx;


import org.docx4j.XmlUtils;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.StyleDefinitionsPart;
import org.docx4j.wml.*;
import org.docx4j.jaxb.*;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.xml.bind.JAXBElement;

public class WordTemplateTestObjective {

	
    String NAME = "<TESTOBJECTIVENAMELABEL_PLACEHOLDER>";
    String DESCRIPTION = "<DESCRIPTIONLABEL_PLACEHOLDER>";
    String URI = "<URIOFOBJECTIVELABEL_PLACEHOLDER>";
    String CONFIGURATION = "<CONFIGURATIONLABEL_PLACEHOLDER>";
    String PICS = "<PICSSELECTIONLABEL_PLACEHOLDER>";
    String INITIAL = "<INITIALCONDITIONSLABEL_PLACEHOLDER>";
    String EXPECTED = "<EXPECTEDBEHAVIOURLABEL_PLACEHOLDER>";
    String FINAL = "<FINALCONDITIONSLABEL_PLACEHOLDER>";
    String WHEN = "<EXPECTEDBEHAVIOURLABEL_WHENPART_PLACEHOLDER>";
    String THEN = "<EXPECTEDBEHAVIOURLABEL_THENPART_PLACEHOLDER>";

    String THEN_DIRECTION = "<THEN_DIRECTION>";
    String WHEN_DIRECTION = "<WHEN_DIRECTION>";

	
	private String templateFileName = "TO_TableTemplates.docx";
	private String inputFileName = "empty.docx";
	
//	private final String optFieldPrefix = "<OPT_";      // The prefix of place-holders that if replace string is empty cause the table row to be deleted. 
	

	/**
	 * Constructor for class WordTemplateTestObjective
	 */
 	public WordTemplateTestObjective () {
    	
 	}




	public void init() {
		try {
			this.wordMLPackageTemplates = WordprocessingMLPackage.createPackage();
		} catch (InvalidFormatException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    	try {
			this.wordMLPackageTO = WordprocessingMLPackage.load(new java.io.File(getInputFileName()));
		} catch (Docx4JException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    	// Open template file
    	try {
			this.wordMLPackageTemplates = openTemplatesDocument(getTemplateFileName());
		} catch (FileNotFoundException | Docx4JException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    	this.wordTemplateStylePart = wordMLPackageTemplates.getMainDocumentPart().getStyleDefinitionsPart();

		try {
			this.templateStyles = wordTemplateStylePart.getContents();
		} catch (Docx4JException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    	// Extract the list of template tables from the template file
    	this.templateTableList = getAllElementFromObject(wordMLPackageTemplates.getMainDocumentPart(), Tbl.class);
    	
    	
    	// Insert the styles definition part from the templates word file.
    	wordMLPackageTO.getMainDocumentPart().getStyleDefinitionsPart().setContents(templateStyles);
	}
    
    
    
    
    /**
     * Search for the specified table template. If found it sets the cloned table to the 
     * the table template and deletes the table template key row from the cloned table.
     * @param templateKey
     * @return true if the template table with the specified templateKey is found
     */
    public boolean selectTemplateTable( String templateKey )
    {
    	boolean searchResult = false;
    	List<Object> rows;
    	Tr templateRow;
    	
		for (Iterator<Object> iterator = templateTableList.iterator(); iterator.hasNext();) {
			Object tbl = iterator.next();
			List<?> textElements = getAllElementFromObject(tbl, Text.class);
			for (Object text : textElements) {
				Text textElement = (Text) text;
				if (textElement.getValue() != null && textElement.getValue().equals(templateKey)) {
					
					
					selectedTemplateTable = (Tbl) XmlUtils.deepCopy(tbl);  // Clone the selected table
					
					rows = getAllElementFromObject(selectedTemplateTable, Tr.class);
					
					templateRow = (Tr) rows.get(0);  // Get the first table row where the table key is defined

					selectedTemplateTable.getContent().remove(templateRow);

					searchResult = true;
					return searchResult;
				}
			}
		}
		return searchResult;
	}
    
    
    
    /**
     * Retrieve the list of place holder identifiers of the selected table template.
     * @return  The list of place holder identifiers
     */
    
    public List<String> getAllPlaceholders() {   
    	
    	List<String> placeHolderStrs = new ArrayList<>();
    	List<Object> paragraphs = getAllElementFromObject(selectedTemplateTable, P.class);
    	 
    	for (Object p : paragraphs) 
    	{
			List<Object> texts = getAllElementFromObject(p, Text.class);
			for (Object t : texts) 
			{
				Text content = (Text) t;
				if (content.getValue().matches("<[a-zA-Z_0-9]+>") )  // Any sequence of word characters enclosed by '<' and '>' characters 
				{  
					placeHolderStrs.add(content.getValue());
				} else {
					//System.out.println("::"+content.getValue());
				}
			}
		}
        return placeHolderStrs;
    }
    
    
    /**
     * Get the list of table template identifiers 
     * @return  the list of table template identifiers 
     */
    public  List<String> getTemplateIdentifiers() {
	   	
    	List<String> tableIds = new ArrayList<>();

    	
		for (Iterator<Object> iterator = templateTableList.iterator(); iterator.hasNext();) 
		{
			Object tbl = iterator.next();
			List<Object> rows = getAllElementFromObject(tbl, Tr.class);
			if ( !rows.isEmpty() ) 
			{
				Tr templateRow = (Tr) rows.get(0);  // Get the first table row where the table key is defined
				List<?> textElements = getAllElementFromObject(templateRow, Text.class);
				for (Object text : textElements) 
				{
					Text textElement = (Text) text;
					if (textElement.getValue() != null ) 
					{
						tableIds.add(textElement.getValue());
					}
				}
			}
		}    	
    	
    	return tableIds;
    }
    
    
    public boolean createNewTableFromSelectedTemplate() {
    	boolean result = false;
    	Tbl newTable = new Tbl();
    	
    	if ( selectedTemplateTable != null ) 
    	{
    		newTable = (Tbl) XmlUtils.deepCopy(selectedTemplateTable);
    		workingTable = newTable;
    		result = true;
    	}
    	return result;
    	
    }
    
    public void writeDocumentToFile( String fname )
    {
		try {
			writeDocxToStream( wordMLPackageTO, fname );
		} catch (IOException | Docx4JException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}    
    }

    

	/**
	 * Replace the specified placeholder string with the specified text (list of strings).
	 * @param newText
	 * @param placeholder
	 */
    public void substitutePlaceholder( List<String> newText, String placeholder ) {
    	

    	List<Object> trows = getAllElementFromObject(workingTable, Tr.class);
    	P toReplace = null;
    	
    	List<Object> replaceparagraphs = getAllElementFromObject(workingTable, P.class);
		for (Object p : replaceparagraphs) {
			List<Object> texts = getAllElementFromObject(p, Text.class);
			for (Object t : texts) {
				Text content = (Text) t;
				if (content.getValue().equals(placeholder)) {
					toReplace = (P) p;
					break;
				}
			}
		}
		if (toReplace == null) {
			//System.out.println(":: Could not match placeholder " +placeholder);
		}
    	
    	for (Object tr : trows) 
    	{
    		Tr tblrow = (Tr) tr;
    		
    		List<Object> tcells = getAllElementFromObject(tr, Tc.class);
    		
    		for (Object tc : tcells) 
    		{
    			Tc tblcell = (Tc) tc;
    			
    			List<Object> paragraphs = getAllElementFromObject(tc, P.class);
    			for (Object p : paragraphs) 
    			{
        			List<Object> texts = getAllElementFromObject(p, Text.class);
        			for (Object t : texts) 
        			{
        				Text content = (Text) t;
        				if (content.getValue().equals(placeholder)) 
        				{
        					
        					boolean isNewTextEmpty = false;
        					if ( newText == null || newText.isEmpty()) 
        					{
        						newText = Arrays.asList("");
        						//isNewTextEmpty = true;
        					}
        					
        					if (!( isNewTextEmpty //&&  (placeholder.startsWith(optFieldPrefix))
        							) )  // Placeholder to be replaced with text
        					{
	        			        Iterator<String> iterator = newText.iterator();
	        			        while ( iterator.hasNext() ) 
	        			        {
	        			        	P copy = new P();
	        			        	copy = (P) XmlUtils.deepCopy(toReplace);
	        			        	
	        						// replace the text elements from the copy
	        						List<?> copytexts = getAllElementFromObject(copy, Text.class);
	        						if (copytexts.size() > 0) {
	        							Text textToReplace = (Text) copytexts.get(0);
	        							textToReplace.setValue( (String) iterator.next());
	        							textToReplace.setSpace("preserve");
	        						}
	        						        			        	        			        	
	            			        tblcell.getContent().add(copy);
	
	        			        }
	        			        tblcell.getContent().remove(0);  // Delete original placeholder paragraph
        					} 
        					else  // The replacement text list is empty and the placeholder is an optional field so the table row is deleted.)
        					{
        						//workingTable.getContent().remove(tblrow);
        					}
        						
        				}
        			}   				
    			}
    		}
		}
	}    

    
    
    
    
	
	
	public void addWorkingTableToDocument() {
	   wordMLPackageTO.getMainDocumentPart().addObject(workingTable);  
	}
	

	public void addParagraphToDocument( String s ) {
		org.docx4j.wml.ObjectFactory locfactory = Context.getWmlObjectFactory();
       	org.docx4j.wml.P para = locfactory.createP();
		org.docx4j.wml.Text t = locfactory.createText();    		
		org.docx4j.wml.R run = locfactory.createR();
		
    	t.setValue( s );
		run.getContent().add(t);
		para.getContent().add(run);
    	this.wordMLPackageTO.getMainDocumentPart().addObject(para); 
				
	}
	

	/**
	 * retrieve a list elements of specified type contained in specified object 
	 * @param obj the object from which to retrieve the type of elements
	 * @param toSearch
	 * @return
	 */
    private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
		List<Object> result = new ArrayList<Object>();
		if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();
 
		if (obj.getClass().equals(toSearch))
			result.add(obj);
		else if (obj instanceof ContentAccessor) {
			List<?> children = ((ContentAccessor) obj).getContent();
			for (Object child : children) {
				result.addAll(getAllElementFromObject(child, toSearch));
			}
 
		}
		return result;
	}
	
	
	
	
    /**
     *  Open the specified template file 
     */
	private static WordprocessingMLPackage openTemplatesDocument(String name) throws Docx4JException, FileNotFoundException 
	{
		WordprocessingMLPackage template = WordprocessingMLPackage.load(new FileInputStream(new File(name)));
		return template;
	}
	
	
	
	
    /**
     *  Save the modified template to the specified output Word file.
     */
	private static void writeDocxToStream(WordprocessingMLPackage template, String target) throws IOException, Docx4JException 
	{
		File f = new File(target);
		template.save(f);
	}
	
	
	
	
	
 	
	// Private variables 
	
	public String getTemplateFileName() {
		return templateFileName;
	}




	public void setTemplateFileName(String templateFileName) {
		this.templateFileName = templateFileName;
	}






	public String getInputFileName() {
		return inputFileName;
	}




	public void setInputFileName(String inputFileName) {
		this.inputFileName = inputFileName;
	}






	private Tbl selectedTemplateTable;   // The orignal selected table template
	private Tbl workingTable = null;     // The table being modified before added to the output file
	
	private List<Object> templateTableList;
	
    
	private WordprocessingMLPackage  wordMLPackageTemplates;    // Word document with Table templates

	private StyleDefinitionsPart wordTemplateStylePart;   // Style part in the template document

	private Styles templateStyles;
	
	
	private WordprocessingMLPackage  wordMLPackageTO = null;    // New Word document to be with filled Test Objective tables

	
    

    
	
}
