/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.apache.myfaces.trinidadinternal.renderkit.core.xhtml;

import java.io.IOException;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.apache.myfaces.trinidad.bean.FacesBean;
import org.apache.myfaces.trinidad.bean.PropertyKey;

import org.apache.myfaces.trinidad.context.RenderingContext;
import org.apache.myfaces.trinidadinternal.renderkit.core.CoreRendererUtils;
import org.apache.myfaces.trinidad.skin.Icon;

/**
 */
public abstract class SimpleSelectBooleanRenderer extends FormInputRenderer
{
  public SimpleSelectBooleanRenderer(FacesBean.Type type)
  {
    super(type);
  }
  
  @Override
  protected void findTypeConstants(FacesBean.Type type)
  {
    super.findTypeConstants(type);
    _textKey = type.findKey("text");
    _accessKeyKey = type.findKey("accessKey");
  }

  
  //**********************
  //encode
  //**********************


  abstract protected Object getValueAttr(RenderingContext arc);

  abstract protected Object getType();

  abstract protected String getIconName(
    boolean selected,
    boolean disabled
  );
  
  abstract protected String getIconAltTextName(
    boolean selected
  );


  protected void renderNameAttribute(
    FacesContext        context,
    RenderingContext arc,
    FacesBean           bean
    )throws IOException
  {
    // no-op
  }
  
  /**
   * @TODO use new renderAllAttributes that doesn't render styleclass once
   * Jeanne adds it.
   */
  @Override
  protected void encodeAllAsElement(
    FacesContext        context,
    RenderingContext arc,
    UIComponent         component,
    FacesBean           bean) throws IOException
  {
    ResponseWriter writer = context.getResponseWriter();
    String clientId = LabelAndMessageRenderer.__getCachedClientId(arc); 
    
    boolean getRenderSimpleSpan = getRenderSimpleSpan(bean);
    if (getRenderSimpleSpan)
    {
      writer.startElement("span", component);
      // put the outer style class here, like af_selectBooleanRadio, styleClass,
      // inlineStyle, 'state' styles like p_AFDisabled, etc.
      renderRootDomElementStyles(context, arc, component, bean);
      // render transformed id so that it can be PPR'd.
      _renderPartialId(context, arc, clientId);
    }
    // Added span around the entire element
    // for the Visual Editor and PPR. See bug # 2222541.
    writer.startElement("span", component);
    
    // render the events only if the browser supports JavaScript
    if (supportsScripting(arc))
    {
      renderSpanEventHandlers(context, bean);
    }
    
    renderStyleClass(context, arc, getContentStyleClass(bean)); 
    renderInlineStyleAttribute(context, arc, getContentStyle(bean));

    if (!getRenderSimpleSpan)
    {
      // render transformed id so that it can be PPR'd.
      _renderPartialId(context, arc, clientId);
    }

    char accessKey;
    if (supportsAccessKeys(arc))
      accessKey = getAccessKey(bean);
    else
      accessKey = CHAR_UNDEFINED;

    if (isAutoSubmit(bean))
      AutoSubmitUtils.writeDependencies(context, arc);
    
    writer.startElement("input", null);
    renderId(context, component);
    // Not calling super.renderAllAttributes or style classes are written out
    renderShortDescAttribute(context, arc, bean);
    
    // render the events only if the browser supports JavaScript
    if (supportsScripting(arc))
    {
      renderInputEventHandlers(context, bean);
    }
    renderDisabledAttribute(context, arc, bean);
    if (!shouldRenderName(context, component))
      renderNameAttribute(context, arc, bean);    
    if (accessKey != CHAR_UNDEFINED)
      writer.writeAttribute("accesskey", Character.toString(accessKey), null);
    writer.writeAttribute("type", getType(), null);
    writer.writeAttribute("value", getValueAttr(arc), null);
    
    Object value = getSubmittedValue(bean);
    if (value == null)
      value = getValue(bean);
    
    if (!(value instanceof Boolean)) 
      value = Boolean.valueOf(getConvertedString(context, component, bean)); 

    if ( Boolean.TRUE.equals(value))  
      writer.writeAttribute("checked", Boolean.TRUE, "value");

    writer.endElement("input");
    
    String text = getText(bean);

    boolean renderLabelTags = _isLabelTagRendered(arc, text, accessKey);
    
    if (renderLabelTags)
    {
      writer.startElement("label", null);

      writer.writeAttribute("for", clientId, null);
      HiddenLabelUtils.rememberLabel(arc, clientId);
    }

    renderText(context, arc, component, bean, true, false, text, accessKey);

    if (renderLabelTags)
    {
      writer.endElement("label");
    }

    // see bug 2880407 we dont need to render hidden label when wrapped with
    // fieldset and legend
    if (isHiddenLabelRequired(arc))
      renderShortDescAsHiddenLabel(context, arc, component, bean);
        
    writer.endElement("span"); 
    if (getRenderSimpleSpan)
      writer.endElement("span");      
  }

  @Override
  protected void encodeAllAsNonElement(
    FacesContext        context,
    RenderingContext arc,
    UIComponent         component,
    FacesBean           bean) throws IOException
  {
    ResponseWriter writer = context.getResponseWriter();
    boolean getRenderSimpleSpan = getRenderSimpleSpan(bean);
    String clientId = LabelAndMessageRenderer.__getCachedClientId(arc);    

    if (getRenderSimpleSpan)
    {
      writer.startElement("span", component);
      // put the outer style class here, like af_selectBooleanRadio, styleClass,
      // inlineStyle, 'state' styles like p_AFDisabled, etc.
      renderRootDomElementStyles(context, arc, component, bean);
      // render transformed id so that it can be PPR'd.
      _renderPartialId(context, arc, clientId);
    }
    
    // Added span around the entire element
    // for the Visual Editor and PPR. See bug # 2222541.
    writer.startElement("div", component);
    if (!getRenderSimpleSpan)
    {
      // render transformed id on the span so that it can be PPR'd.
      // this needs to be composite id even when readOnly is true for PPR
      _renderPartialId(context, arc, clientId);
    }
    renderStyleClass(context, arc, getContentStyleClass(bean));
    renderInlineStyleAttribute(context, arc, getContentStyle(bean));
    renderShortDescAttribute(context, arc, bean);
    
    Object value = getSubmittedValue(bean);
    if ( value == null)
      value = getValue(bean);

    if (!(value instanceof Boolean)) 
      value = Boolean.valueOf(getConvertedString(context, component, bean)); 

    boolean selected = Boolean.TRUE.equals(value);
    boolean disabled = getDisabled(bean);

    String iconName = getIconName(selected, disabled);     
    Icon icon = arc.getIcon(iconName);
    
    String altTextName = getIconAltTextName(selected);

    String shortDesc = arc.getTranslatedString(altTextName);   
    OutputUtils.renderIcon(context, arc, icon, shortDesc, null );                      

    String text = getText(bean);    
    if (text != null)
    {
      writer.writeText(XhtmlConstants.NBSP_STRING, null);
      
      // output the text directly since we have no access key
      writer.writeText(text, "text");        
    }

    writer.endElement("div");   
    if (getRenderSimpleSpan)
      writer.endElement("span");
  }

  protected void renderSpanEventHandlers(
    FacesContext context, 
    FacesBean    bean) throws IOException
  {
    //no op
  }
  
  protected void renderInputEventHandlers(
    FacesContext context, 
    FacesBean    bean) throws IOException
  {
    renderEventHandlers(context, bean); 
  }

  protected void renderAccessKeyAttribute(
    FacesContext        context,
    RenderingContext arc,
    FacesBean           bean) throws IOException
  {
  }  
  
  /**
   * Renders the node text
   */
  protected void renderText(
    FacesContext        context,
    RenderingContext arc,
    UIComponent         component,
    FacesBean           bean,
    boolean             renderAccessKeys,
    boolean             renderID,
    String              text,
    char                accessKey
    ) throws IOException
  {

    if (text != null)
    {
      ResponseWriter writer = context.getResponseWriter();

      writer.startElement("span", component);
      if (renderID)
        renderId( context, component);

      if (renderAccessKeys && supportsAccessKeys(arc))
      {
        // hightlight any access keys with an underline
        AccessKeyUtils.renderAccessKeyText(context, text, accessKey,
                                           SkinSelectors.AF_ACCESSKEY_STYLE_CLASS);
      }
      else
      {
        // output the text directly since we have no access key
        writer.writeText(text, null);
      }

      writer.endElement("span");
    }
  }
  
  protected String getAutoSubmitScript(
    FacesBean bean
  )
  {
    RenderingContext arc = RenderingContext.getCurrentInstance();
    String source = LabelAndMessageRenderer.__getCachedClientId(arc);
    boolean immediate = isImmediate(bean);
    boolean isRadio = isRadio();
    return AutoSubmitUtils.getSubmitScript(arc, source, 
                                           immediate, isRadio,
                                           XhtmlConstants.AUTOSUBMIT_EVENT, null, true);
  }                     

  protected boolean isRadio()
  {
    return false;
  }


  protected String getCompositeId(String clientId)
  {
    return clientId + XhtmlConstants.COMPOSITE_ID_EXTENSION + "c";   
  }
    
  protected String getText(FacesBean bean)
  {
    return toString(bean.getProperty(_textKey));
  }

  protected char getAccessKey(FacesBean bean)
  {
    return toChar(bean.getProperty(_accessKeyKey));
  }

  /**
   * return true if you want to render the span that goes around the simple
   * element, where we put the root dom element styles.
   */
  protected boolean getRenderSimpleSpan(FacesBean bean)
  {
    return getSimple(bean);
  }
 
 
    
  /**
   * checks to see if we need label tags around this element and its text.
   * we don't need label tags if this element is readOnly or disabled, or if
   * it does not have any text.
   */
  private boolean _isLabelTagRendered(
    RenderingContext arc,
    String              text,
    char                accessKey
    )
  {
    if (text == null)
      return false;

    if (isInaccessibleMode(arc) &&
        accessKey == CHAR_UNDEFINED)
      return false;

    return true;
  }

  /**
   * this id is rendered so that the node can be ppr replaced
   */
  private void _renderPartialId(
    FacesContext        context,
    RenderingContext arc,
    String              clientId
  )throws IOException
  {
    if (clientId != null && CoreRendererUtils.supportsPartialRendering(arc))
    {
      
      String compositeId = getCompositeId(clientId);

      context.getResponseWriter().writeAttribute("id", compositeId, null );
    }
  }


  private PropertyKey _textKey;
  private PropertyKey _accessKeyKey;
}
