/*
 * Copyright 2011 Specto Technologies Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package com.spectotechnologies.util;

import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Functions to process simple tasks on HTML data.
 *
 * @author Specto Technologies Inc., Alexandre Lavoie
 */
public class HTMLUtils
{
	public static final String SEPARATOR_ONE = "<wbr />"; // Problem : IE8, Safari/Windows
	public static final String SEPARATOR_TWO = "&#8203;"; // Problem : IE 5.5, IE 6
	public static final String SEPARATOR_THREE = "&shy;"; // Problem : FireFox 2

	public static String makeBreakableLine(String p_sLine, int p_nInterval, String p_sSeparator)
	{
		String sTemporaryLine = p_sLine;
		String sResult = "";
		int nInterval = p_nInterval;

		do
		{
			if(nInterval > sTemporaryLine.length())
			{
				nInterval = sTemporaryLine.length();

				sResult = sResult + sTemporaryLine.substring(0,nInterval);
			}
			else
			{
				sResult = sResult + sTemporaryLine.substring(0,nInterval) + p_sSeparator;
			}

			sTemporaryLine = sTemporaryLine.substring(nInterval,sTemporaryLine.length());
		}
		while(sTemporaryLine.length() > 0);

		return sResult;
	}

	/**
	 * 2009-05-13 : Alexandre Lavoie
	 *
	 * Replaces all tags in list by specified replacement.
	 *
	 * <tag> <tag/> <tag /> <tag></tag>
	 *
	 * @param p_sText Text to be verified
	 * @param p_lTags Tags to search
	 * @param p_sReplacement Replacement value
	 * @return
	 */
	public static String replaceTags(String p_sText, List<String> p_lTags, String p_sReplacement)
	{
		String sResult;
		Pattern oPattern;
		Matcher oMatcher;
		String sTag;
		int nTag;
		String sPattern;
		int nCharacter;

		sResult = p_sText;

		for(nTag = 0;nTag < p_lTags.size();nTag++)
		{
			sTag = p_lTags.get(nTag);

			/*
 				Example of tag pattern

 				(<img[^/>]*
				            (/>|
				             >([^<]|
				               <[^/]|
				               </[^i]|
				               </i[^m]|
				               </im[^g]
				              )*</img>
				            )
				)
			 */
			sPattern = "(<" + sTag + "[^/>]*";
			sPattern += "(/>|";

			sPattern += ">([^<]|";
			sPattern += "<[^/]|";

			sPattern += "</[^" + sTag.charAt(0) + "]|";

			for(nCharacter = 0;nCharacter < (sTag.length() - 1);nCharacter++)
			{
				sPattern += "</" + sTag.charAt(nCharacter) + "[^" + sTag.charAt(nCharacter + 1) + "]|";
			}

			sPattern += ")*</" + sTag + ">";
			sPattern += ")";
			sPattern += ")";

			//System.out.println("Pattern : " + sPattern);

			oPattern = Pattern.compile(sPattern,Pattern.CASE_INSENSITIVE);
			oMatcher = oPattern.matcher(sResult);

			sResult = oMatcher.replaceAll(p_sReplacement);
		}

		return sResult;
	}

	/**
	 * Function that trims HTML text content keeping necessary HTML tags.
	 *
	 * @param p_sText HTML content to be trimmed
	 * @param p_nLength Maximum text length
	 * @return
	 */
	public static String trim(String p_sText, int p_nLength)
	{
		if(p_sText == null || p_sText.isEmpty())
			return "";

		if(p_sText.length() < p_nLength)
			return p_sText;

		String sTempText;
		String sValue;

		StringBuilder sbResult = new StringBuilder();
		int nTextLength = p_sText.length();
		int nResultLength = 0;

		Stack oStackHTMLTags = new Stack();

		// Start string valid patterns
		Pattern oPatternText				= Pattern.compile("^[^<&]+");
		Pattern oPatternHTMLTag				= Pattern.compile("^<.*?>");
		Pattern oPatternSpecialCharacter	= Pattern.compile("^&[^;]*?;");
		Pattern oPatternHTMLTagName			= Pattern.compile("^<[a-z]*");
		Matcher oMatcher;

		boolean bFoundMatch;
		int nPosition = 0;

		while(nPosition < nTextLength)
		{
			sTempText = p_sText.substring(nPosition);
			
			bFoundMatch = false;
			
			if(!bFoundMatch)
			{
				oMatcher = oPatternText.matcher(sTempText);
				if(oMatcher.find())
				{
					sValue = oMatcher.group();
					nPosition += sValue.length();

					if(sValue.length() >= (p_nLength - nResultLength))
					{
						sbResult.append(sValue.substring(0,p_nLength - nResultLength));

						sbResult.append(" [...]");
						break;
					}
					else
					{
						sbResult.append(sValue);
						nResultLength += sValue.length();
					}

					bFoundMatch = true;
				}
			}

			if(!bFoundMatch)
			{
				oMatcher = oPatternHTMLTag.matcher(sTempText);
				if(oMatcher.find())
				{
					sValue = oMatcher.group();

					sbResult.append(sValue);
					nPosition += sValue.length();

					oMatcher = oPatternHTMLTagName.matcher(sValue);

					// Add HTML open tag to stack
					if(sValue.indexOf('/') == -1 && oMatcher.find())
					{
						sValue = oMatcher.group();

						oStackHTMLTags.push("</" + sValue.substring(1) + ">");
					}
					// Remove HTML close tag
					else if(sValue.indexOf('/') == 1)
					{
						oStackHTMLTags.pop();
					}
					
					bFoundMatch = true;
				}
			}

			if(!bFoundMatch)
			{
				oMatcher = oPatternSpecialCharacter.matcher(sTempText);
				if(oMatcher.find())
				{
					sValue = oMatcher.group();

					if(1 >= (p_nLength - nResultLength))
					{
						sbResult.append(sValue.substring(0,p_nLength - nResultLength));

						sbResult.append(" [...]");
						break;
					}
					else
					{
						sbResult.append(sValue);
						nResultLength += 1;
					}

					nPosition += sValue.length();
					bFoundMatch = true;
				}
			}
		}

		// Reconstruct HTML close tags
		while(oStackHTMLTags.size() > 0)
		{
			sValue = oStackHTMLTags.pop().toString();
			sbResult.append(sValue);
		}
		
		return sbResult.toString();
	}
}