package hirondelle.web4j.ui.translate;

import java.util.Locale;
import java.util.logging.Logger;
import java.util.regex.*;

import hirondelle.web4j.BuildImpl;
import hirondelle.web4j.request.LocaleSource;
import hirondelle.web4j.ui.tag.TagHelper;
import hirondelle.web4j.util.Util;
import hirondelle.web4j.util.Regex;
import hirondelle.web4j.util.EscapeChars;

/**
 Custom tag for translating 
 <a href="http://www.w3.org/TR/html4/struct/global.html#adef-title"><tt>TITLE</tt></a>,  
 <a href="http://www.w3.org/TR/html4/struct/objects.html#adef-alt"><tt>ALT</tt></a> 
 and submit-button 
 <a href="http://www.w3.org/TR/html4/interact/forms.html#adef-value-INPUT"><tt>VALUE</tt></a>
 attributes in markup.
 
 <P><span class="highlight">By using this custom tag <em>once</em> in a template JSP, 
 it is often possible to translate <em>all</em> of the <tt>TITLE</tt>, 
 <tt>ALT</tt>, and submit-button <tt>VALUE</tt> attributes appearing in an entire application.</span>
 
 <P>The <tt>VALUE</tt> attribute is translated only for <tt>SUBMIT</tt> controls. (This 
 <tt>VALUE</tt> attribute isn't really a tooltip, of course : it is rendered as the button text. 
 For this custom tag, the distinction is not very important.)
 
 <P>The <tt>TITLE</tt> attribute applies to a large number of HTML tags, and
 the <tt>ALT</tt> attribute applies to several tags. In both cases, these 
 items generate pop-up "tool tips", which are visible to the end user. If the application is 
 multilingual, then they require translation.
 
 <P>This custom tag accepts HTML markup for its body, and will do a search and 
 replace on its content, replacing the values of all <tt>TITLE</tt>, <tt>ALT</tt> and 
 submit-button <tt>VALUE</tt> attributes (that have visible content) with translated values. 
 The translations are provided by the configured implementations of 
 {@link Translator} and {@link LocaleSource}.
*/
public final class Tooltips extends TagHelper {
  
  /**
   Control the escaping of special characters.
    
   <P>By default, this tag will escape any special characters appearing in the 
   <tt>TITLE</tt> or <tt>ALT</tt> attribute, using {@link EscapeChars#forHTML(String)}. 
   To override this default behaviour, set this value to <tt>false</tt>. 
  */
  public void setEscapeChars(boolean aValue){
    fEscapeChars = aValue;
  }
  
  /**
   Scan the body of this tag, and translate the values of all <tt>TITLE</tt>, <tt>ALT</tt>, 
   and submit-button <tt>VALUE</tt> attributes.
   
  <P>Uses the configured {@link Translator} and {@link LocaleSource}.
   
   <P>In addition, this method uses {@link EscapeChars#forHTML(String)} to ensure that 
   any special characters are escaped. This behavior can be overridden using {@link #setEscapeChars(boolean)}. 
  */
  @Override protected String getEmittedText(String aOriginalBody) {
    //fLogger.finest("Original Body: " + aOriginalBody);
    String result = translateTooltips(aOriginalBody);
    result = translateSubmitButtons(result);
    //fLogger.finest("TranslateTooltips translated : " + result.toString());
    return result;
  }

  /**
   Pattern which returns the value of <tt>TITLE</tt> and <tt>ALT</tt> attributes (including any quotes), 
   as group 2, which is to be translated. 
  */
  static final Pattern TOOLTIP = Pattern.compile(
    "(<[^>]* (?:title=|alt=))" + Regex.ATTR_VALUE + "([^>]*>)",  
    Pattern.CASE_INSENSITIVE
  );
  
  /**
   Pattern which returns the value of <tt>VALUE</tt> attribute (including any quotes) of a <tt>SUBMIT</tt>
   control, as group 2, which is to be translated.
   
   <P>Small nuisance restriction : the general order of items must follow this style, where <tt>type</tt>
   and <tt>value</tt> precede all other attributes, and <tt>type</tt> precedes <tt>value</tt>:
   <PRE>
    &lt;input type="submit" value="Add" [any other attributes go here]&gt;
   </PRE>
  */
  static final Pattern SUBMIT_BUTTON = Pattern.compile(
    "(<input(?:\\s)* (?:type=\"submit\"|type='submit'|type=submit)(?:\\s)* value=)" + Regex.ATTR_VALUE + "([^>]*>)", 
    Pattern.CASE_INSENSITIVE
  );
  
  // PRIVATE //
  
  private static final Logger fLogger = Util.getLogger(Tooltips.class);
  private boolean fEscapeChars = true;
  private LocaleSource fLocaleSource = BuildImpl.forLocaleSource();
  private Translator fTranslator = BuildImpl.forTranslator();
  
  private String translateTooltips(String aInput){
    return scanAndReplace(TOOLTIP, aInput);
  }
  
  private String translateSubmitButtons(String aInput){
    return scanAndReplace(SUBMIT_BUTTON, aInput);
  }
  
  private String scanAndReplace(Pattern aPattern, String aInput){
    StringBuffer result = new StringBuffer();
    Matcher matcher = aPattern.matcher(aInput);
    while ( matcher.find() ) {
      matcher.appendReplacement(result, getReplacement(matcher));
    }
    matcher.appendTail(result);
    return result.toString();
  }
  
  private String getReplacement(Matcher aMatcher){
    String result = null;
    String baseText = Util.removeQuotes(aMatcher.group(Regex.SECOND_GROUP));
    if (Util.textHasContent(baseText)){
      String start = aMatcher.group(Regex.FIRST_GROUP);
      String end = aMatcher.group(Regex.THIRD_GROUP);
      Locale locale = fLocaleSource.get(getRequest());
      String translatedText = fTranslator.get(baseText, locale);
      if ( fEscapeChars ){
        translatedText = EscapeChars.forHTML(translatedText);
      }
      result = start + Util.quote(translatedText) + end;
    }
    else {
      result = aMatcher.group(Regex.ENTIRE_MATCH);
    }
    result = EscapeChars.forReplacementString(result);
    fLogger.finest("TITLE/ALT base Text: " + Util.quote(baseText) + " has replacement text : " + Util.quote(result));
    return result;
  }
}
