package lu.tudor.santec.gecamed.core.gui.controller.document;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import lu.tudor.santec.gecamed.addressbook.ejb.entity.beans.Contact;
import lu.tudor.santec.gecamed.core.gui.MainFrame;
import lu.tudor.santec.gecamed.core.gui.controller.data.State;
import lu.tudor.santec.gecamed.core.gui.plugin.filehandler.FileOpener;
import lu.tudor.santec.gecamed.core.gui.widgets.ErrorDialog;
import lu.tudor.santec.gecamed.core.utils.controller.utils.FileUtils;
import lu.tudor.santec.gecamed.letter.gui.placeholders.PlaceholdersConfig;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;



public abstract class DocumentController
{
	/* ======================================== */
	// 		CONSTANTS
	/* ======================================== */
	
	public static final String 	DOCUMENT_NAME 	= "GECAMDOC";
	
	private static final long 	INTERVALL_TIME 	= 1000;
	
	public static final int	PH_VERSION_OLD	= 0;
	public static final int PH_VERSION_NEW	= 1;
	
	public static final int	TYPE_NEW_TEMPLATE	= 0;
	public static final int	TYPE_OPEN_TEMPLATE	= 1;
	public static final int	TYPE_NEW_LETTER		= 2;
	public static final int	TYPE_OPEN_LETTER	= 3;
	public static final int	TYPE_OTHER			= 4;
	
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
	/** the logger Object for this class */
	private static Logger logger = Logger.getLogger(DocumentController.class.getName());
	
	
	protected State 			state;
	
	protected File 				file;
	
	protected boolean 			openedHidden;
	
	protected Boolean 			containsContactInfo;
	
	protected HashSet<String>	potentialKeys;
	
	protected int				placeholderVersion	= PH_VERSION_NEW;
	
	protected int				type;
	
	private List<String>		contactPlaceholders;
	
	
	
	/* ======================================== */
	// 		CONSTRUCTORS
	/* ======================================== */
	
	public DocumentController ()
	{
		state = new State();
	}
	
	
	
	/* ======================================== */
	// 		CLASS BODY
	/* ======================================== */
	
	protected byte[] getPhysicTemplateFile ()
	{
		InputStream iStream;
		byte[] 		data;
		
		try 
		{
			iStream = getDefaultTemplate();
			data 	= new byte[iStream.available()];
			iStream.read(data);
			return data;
		}
		catch (IOException e) 
		{
		    logger.log(Level.WARN, "Error creating template: ", e);
		}
		return null;
	}
	
	
	protected List<String> getContactPlaceholders ()
	{
		if (contactPlaceholders == null)
			contactPlaceholders	= PlaceholdersConfig.getContactPlaceholders(placeholderVersion);
		
		return contactPlaceholders;
	}
	
	
	protected boolean useNewPlaceholderStyle ()
	{
		return this.placeholderVersion == PH_VERSION_NEW;
	}
	
	
	protected boolean useOldPlaceholderStyle ()
	{
		return this.placeholderVersion == PH_VERSION_OLD;
	}
	
	
	protected int startReplacing (Map<String, String> data)
	{
		int				founds;
		long			time;
		
		
		time	= System.currentTimeMillis();
		founds	= replaceAll(data);
		time	= System.currentTimeMillis() - time;
		logger.info("Replacing " + founds + " words took " + time + "ms.");
		return founds;
	}
	
	
	protected boolean isTemplate ()
	{
		return type <= 1;
	}
	
	
	
	/* ======================================== */
	// 		IMPLEMENTED METHODS
	/* ======================================== */
	
	public void open (int letterType) throws Exception
	{
		open(letterType, false);
	}
	
	
	public void open(int letterType, boolean hidden) throws Exception
	{
		this.openedHidden	= hidden;
		this.type			= letterType;
		
		open();
	}
	
	
	public void createNewDocument() throws IOException
	{
		setDocument(getPhysicTemplateFile(), null);
	}
	
	
	public void createNewDocument(String path) throws IOException
	{
		setDocument(getPhysicTemplateFile(), path);
	}
	
	
	public void setDocument (byte[] document) throws IOException
	{
		setDocument(document, null);
	}
	
	
	public void setDocument(byte[] document, String path) throws IOException
	{
		if (path == null)
		{
			this.file = FileOpener.createGECAMedTempFile(DOCUMENT_NAME, getDocumentExtension());
		}
		else 
		{
			this.file = File.createTempFile(DOCUMENT_NAME, getDocumentExtension(), new File(path));
			this.file.deleteOnExit();
		}
		this.file = FileUtils.createFile(document, this.file);
	}
	
	
	public byte[] getDocument() throws IOException
	{
		return FileUtils.readFile(file);
	}
	
	
	public File getDocumentbyFile() throws IOException
	{
		return file;
	}


	public void waitForUserInput()
	{
		// Controller will be closed when application is closed
		while (!isDocumentClosed()) {
			try 
			{
				Thread.sleep(INTERVALL_TIME);
			} 
			catch (InterruptedException e) 
			{
				logger.log(Level.ERROR, e.getMessage(), e);
			}
		}
	}
	
	
	public State getState()
	{
		return state;
	}
	
	
	public void deleteDocument ()
	{
		if (file != null && file.exists())
		{
			try
			{
				file.delete();
			}
			catch (Throwable e)
			{
				logger.warn(e.getMessage());
			}
		}
	}


	public void closeAndDeleteFile ()
	{
		close();
		deleteDocument();
	}
	
	
	public boolean containsContactInfo()
	{
		return containsContactInfo;
	}
	
	
	public void setPlaceholderVersion (int version)
	{
		this.placeholderVersion = version;
	}
	
	
	public int getPlaceholderVersion ()
	{
		return this.placeholderVersion;
	}
	
	
	public int startReplacing(int version, List<Contact> contacts)
	{
		Map<String, String>	data;
		Contact				contact;
		
		
		setPlaceholderVersion(version);
		
		if (version == PH_VERSION_OLD)
		{
			if (contacts == null || contacts.isEmpty())
				 contact = null;
			else contact = contacts.get(0);
			data = PlaceholdersConfig.getPlaceholdersMap(contact, version);
		}
		else
		{
			try
			{
				data = PlaceholdersConfig.getPlaceholdersMap(contacts, scanForPlaceholders(), version);
			}
			catch (Exception e)
			{
				logger.log(Level.ERROR, e.getMessage(), e);
				ErrorDialog.showErrorDialog(MainFrame.getInstance(), e);
				return 0;
			}
		}
		
		return startReplacing (data);
	}
	
	
	public int getLetterType()
	{
		return type;
	}
	
	
	public void updatePlaceholders()
	{
		Map<String, String> data = PlaceholdersConfig.getOld2NewMap();
		
		
		replaceAll(data);
		setPlaceholderVersion(PH_VERSION_NEW);
	}
	
	
	
	/* ======================================== */
	// 		PUBLIC ABSTRACT METHODS
	/* ======================================== */
	
	public abstract void append (String text);
	
	public abstract void print ();
	
	public abstract void checkIfContainsContactInfo ();
	
	public abstract void saveAsPDF (File pdfFile);
	
	
	
	/* ======================================== */
	// 		PROTECTED ABSTRACT METHODS
	/* ======================================== */
	
	protected abstract void open () throws Exception;
	
	protected abstract void close ();

	protected abstract String getDocumentExtension ();
	
	protected abstract InputStream getDefaultTemplate ();
	
	protected abstract boolean isDocumentClosed ();
	
	protected abstract int replaceAll(Map<String, String> data);
	
	protected abstract Collection<String> scanForPlaceholders ();
}
