/*
 * 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.tobago.renderkit.html.scarborough.standard.tag;

/*
 * Created 07.02.2003 16:00:00.
 * $Id: GridLayoutRenderer.java 1420177 2012-12-11 14:07:05Z lofwyr $
 */

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.tobago.component.ComponentUtil;
import org.apache.myfaces.tobago.component.FixedLayoutToken;
import org.apache.myfaces.tobago.component.HideLayoutToken;
import org.apache.myfaces.tobago.component.LayoutToken;
import org.apache.myfaces.tobago.component.LayoutTokens;
import org.apache.myfaces.tobago.component.MinimumLayoutToken;
import org.apache.myfaces.tobago.component.PixelLayoutToken;
import org.apache.myfaces.tobago.component.RelativeLayoutToken;
import org.apache.myfaces.tobago.component.UICell;
import org.apache.myfaces.tobago.component.UIForm;
import org.apache.myfaces.tobago.component.UIGridLayout;
import org.apache.myfaces.tobago.component.UIHiddenInput;
import org.apache.myfaces.tobago.component.UILayout;
import org.apache.myfaces.tobago.component.UIPage;
import org.apache.myfaces.tobago.config.TobagoConfig;
import org.apache.myfaces.tobago.renderkit.LayoutInformationProvider;
import org.apache.myfaces.tobago.renderkit.RenderUtil;
import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlConstants;
import org.apache.myfaces.tobago.renderkit.html.HtmlRendererUtil;
import org.apache.myfaces.tobago.renderkit.html.HtmlStyleMap;
import org.apache.myfaces.tobago.renderkit.html.StyleClasses;
import org.apache.myfaces.tobago.util.LayoutInfo;
import org.apache.myfaces.tobago.util.LayoutUtil;
import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static org.apache.myfaces.tobago.TobagoConstants.ATTR_BORDER;
import static org.apache.myfaces.tobago.TobagoConstants.ATTR_CELLSPACING;
import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_HEIGHT;
import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_WIDTH;
import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_HEIGHT;
import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_WIDTH;
import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SCROLLBARS;
import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STYLE;
import static org.apache.myfaces.tobago.TobagoConstants.ATTR_WIDTH_LIST;

public class GridLayoutRenderer extends DefaultLayoutRenderer {

  private static final Log LOG = LogFactory.getLog(GridLayoutRenderer.class);

  public int getFixedHeight(FacesContext facesContext, UIComponent component) {
    int height = calculateLayoutHeight(facesContext, component, false);

    LayoutInformationProvider containerRenderer =
        ComponentUtil.getRenderer(facesContext, component);
    height += containerRenderer.getHeaderHeight(facesContext, component);
    height += containerRenderer.getPaddingHeight(facesContext, component);
    return height;
  }

  public int getFixedWidth(FacesContext facesContext, UIComponent component) {
    int width = calculateLayoutWidth(facesContext, component, false);

    LayoutInformationProvider containerRenderer =
         ComponentUtil.getRenderer(facesContext, component);
    width += containerRenderer.getPaddingWidth(facesContext, component);
    return width;
  }

  private int calculateLayoutHeight(
      FacesContext facesContext, UIComponent component, boolean minimum) {
    UIGridLayout layout = (UIGridLayout) UILayout.getLayout(component);
    final List<UIGridLayout.Row> rows = layout.ensureRows();

    LayoutTokens layoutTokens = layout.getRowLayout();

    if (layoutTokens == null && !minimum && LOG.isDebugEnabled()) {
      LOG.debug("No rowLayout found using " + (minimum ? "'minimum'" : "'fixed'")
          + " for all " + rows.size() + " rows."
          + " (clientId='" + layout.getClientId(facesContext) + "')");
    }
    if (rows.size() != layoutTokens.getSize()) {
      LOG.warn("Unbalanced layout: rows.size()=" + rows.size()
          + " != layoutTokens.length=" + layoutTokens.getSize()
          + " rowLayout='" + layoutTokens + "'"
          + " (clientId='" + layout.getClientId(facesContext) + "')");
      layoutTokens.ensureSize(rows.size(), new RelativeLayoutToken(1));
    }
    // TODO alternative? rows.size() == 1 ? new RelativeLayoutToken(1) : new FixedLayoutToken()
    //new FixedLayoutToken() );
    //String[] layoutTokens
    //    = LayoutInfo.createLayoutTokens(rowLayout, rows.size(),
    //        minimum ? "minimum" : "fixed");


    int size = Math.min(rows.size(), layoutTokens.getSize());

    int height = 0;
    height += getMarginAsInt(layout.getMarginTop());
    height += getMarginAsInt(layout.getMarginBottom());
    boolean first = true;
    for (int i = 0; i < size; i++) {
      if (!rowIsRendered(rows.get(i))) {
        continue;
      }
      height += getCellPadding(facesContext, layout,  first);
      first = false;
      LayoutToken token = layoutTokens.get(i);
      if (token instanceof PixelLayoutToken) {
        height += ((PixelLayoutToken) token).getPixel();
      } else if (token instanceof FixedLayoutToken) {
        height += getMaxHeight(facesContext, rows.get(i), false);
      } else if (token instanceof MinimumLayoutToken) {
        height += getMaxHeight(facesContext, rows.get(i), true);
      } else {
        if (!minimum && LOG.isWarnEnabled()) {
          if (layout.getRows() != null) {
            // TODO: this is only an error if the token was explicitly set by application
            LOG.warn("Unable to calculate Height for token '" + token
                + "'! using " + (minimum ? "'minimum'" : "'fixed'") + " , component-id="
                + component.getClientId(facesContext) + " is "
                + component.getRendererType());
          }
        }
        height += getMaxHeight(facesContext, rows.get(i), minimum);
      }
    }

    return height;
  }


  public int calculateLayoutWidth(
      FacesContext facesContext, UIComponent component, boolean minimum) {
    UIGridLayout layout = (UIGridLayout) UILayout.getLayout(component);
    final List<UIGridLayout.Row> rows = layout.ensureRows();
    UIGridLayout.Row row = rows.get(0);


    LayoutTokens layoutTokens = layout.getColumnLayout();

    if (layoutTokens == null && !minimum && LOG.isDebugEnabled()) {
      LOG.debug("No rowLayout found using " + (minimum ? "'minimum'" : "'fixed'")
          + " for all " + rows.size() + " rows of "
          + layout.getClientId(facesContext) + " !");
    }

    if (row.getColumns() != layoutTokens.getSize()) {
      LOG.warn("Unbalanced layout: rows.size()=" + rows.size()
          + " != layoutTokens.length=" + layoutTokens.getSize()
          + " columnLayout='" + layoutTokens + "'"
          + " (clientId='" + layout.getClientId(facesContext) + "')");
      layoutTokens.ensureSize(row.getColumns(), new FixedLayoutToken());
    }

    int size = Math.min(rows.size(), layoutTokens.getSize());

    int width = 0;
    width += getMarginAsInt(layout.getMarginLeft());
    width += getMarginAsInt(layout.getMarginRight());
    boolean first = true;
    for (int i = 0; i < size; i++) {
      if (!columnIsRendered(rows,  i)) {
        continue;
      }
      width += getCellPadding(facesContext, layout,  first);
      first = false;
      LayoutToken token = layoutTokens.get(i);
      if (token instanceof PixelLayoutToken) {
        width += ((PixelLayoutToken) token).getPixel();
      } else if (token instanceof FixedLayoutToken) {
        width += getMaxWidth(facesContext, rows, i, false);
      } else if (token instanceof MinimumLayoutToken) {
        width += getMaxWidth(facesContext, rows, i, true);
      } else {
        if (!minimum && LOG.isWarnEnabled()) {
          LOG.warn("Unable to calculate Width for token '" + token
              + "'! using " + (minimum ? "'minimum'" : "'fixed'") + " , component-id="
              + component.getClientId(facesContext) + " is "
              + component.getRendererType());
        }
        width += getMaxWidth(facesContext, rows,  i, minimum);
      }
    }

    return width;
  }

  private boolean columnIsRendered(List<UIGridLayout.Row> rows, int column) {
    for (UIGridLayout.Row row : rows) {
      Object object = row.getElements().get(column);
      if (object instanceof UIComponent) {
        if (object instanceof UICell) {
          UICell cell = (UICell) object;
          if (cell.getSpanX() > 1) {
            return false;
          }
        }
        UIComponent component = (UIComponent) object;
        if (component.isRendered()) {
          return true;
        }
        // XXX ????
      } else if (UIGridLayout.USED.equals(object)) {
        return true;
      }
    }
    return false;
  }

  private boolean rowIsRendered(UIGridLayout.Row row) {
    for (Object element : row.getElements()) {
      if (element instanceof UIComponent) {
        UIComponent component = (UIComponent) element;
        if (component.isRendered()) {
          return true;
        }
      }
    }
    return false;
  }

  public void encodeChildrenOfComponent(FacesContext facesContext, UIComponent component)
      throws IOException {
    // encode table with component's children

    UIGridLayout layout =  (UIGridLayout) UILayout.getLayout(component);
    HtmlRendererUtil.prepareRender(facesContext, layout);

    layoutEnd(facesContext, layout);
    layoutMargins(layout);

    final Map attributes = layout.getAttributes();
    List columnWidths =  (List) attributes.get(ATTR_WIDTH_LIST);


    TobagoResponseWriter writer = HtmlRendererUtil.getTobagoResponseWriter(facesContext);
    writer.startElement(HtmlConstants.TABLE, layout);
    writer.writeAttributeFromComponent(HtmlAttributes.BORDER, ATTR_BORDER);
    writer.writeClassAttribute();
    writer.writeStyleAttribute();
    writer.writeAttribute(HtmlAttributes.CELLSPACING, 0);
    writer.writeAttribute(HtmlAttributes.CELLPADDING, 0);
    writer.writeAttribute(HtmlAttributes.SUMMARY, "", false);

    boolean first = true;
    if (columnWidths != null) {
      writer.startElement(HtmlConstants.COLGROUP, null);
      for (int i = 0; i < columnWidths.size(); i++) {
        int cellWidth = ((Integer) columnWidths.get(i)).intValue();
        if (cellWidth != LayoutInfo.HIDE) {
          cellWidth += getCellPadding(facesContext, layout, first);
          first = false;
          writer.startElement(HtmlConstants.COL, null);
          writer.writeAttribute(HtmlAttributes.WIDTH, cellWidth);
          writer.endElement(HtmlConstants.COL);
        }
      }
      writer.endElement(HtmlConstants.COLGROUP);
    }


    List<UIGridLayout.Row> rows = layout.ensureRows();
    boolean firstRenderedRow = true;
    for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
      UIGridLayout.Row row = rows.get(rowIndex);
      if (!row.isHidden()) {
        writer.startElement(HtmlConstants.TR, null);

        List cells = row.getElements();
        boolean firstRenderedColum = true;
        for (int columnIndex = 0; columnIndex < cells.size(); columnIndex++) {
          boolean hide = false;

          if (columnWidths != null) {
            Integer columWidth = ((Integer) columnWidths.get(columnIndex));
            hide = columWidth.intValue() == LayoutInfo.HIDE;
          }
          if (!hide) {

            Object object = cells.get(columnIndex);
            if (object.toString().equals(UIGridLayout.USED)) {
              firstRenderedColum = false;
              continue; // ignore the markers UIGridLayout.Used
            } else if (object.equals(UIGridLayout.FREE)) {
              if (LOG.isWarnEnabled() && !layout.isIgnoreFree()) {
                LOG.warn("There are free blocks in the layout: id='"
                    + layout.getClientId(facesContext)
                    + "'");
              }
              firstRenderedColum = false;
              continue;
            }
            UIComponent cell = (UIComponent) object;


            int spanX = UIGridLayout.getSpanX(cell);
            int spanY = UIGridLayout.getSpanY(cell);
            StyleClasses classes = StyleClasses.ensureStyleClassesCopy(layout);
            if (firstRenderedRow) {
              classes.addClass("gridLayout", "first-row"); // XXX not a standard compliant name
            }
            if (firstRenderedColum) {
              classes.addClass("gridLayout", "first-column"); // XXX not a standard compliant name
            }

            int cellWidth = -1;
            if (columnWidths != null) {
              cellWidth = 0;
              for (int i = columnIndex;
                   i < columnIndex + spanX && i < columnWidths.size(); i++) {
                cellWidth += ((Integer) columnWidths.get(i)).intValue()
                    + getCellPadding(facesContext, layout, firstRenderedColum);
                if (firstRenderedColum) {
                  firstRenderedColum = false;
                }
              }
            }


            int cellHeight = -1;
            try {
              Integer layoutHeight = LayoutUtil.getLayoutHeight(cell);
              if (layoutHeight != null) {
                cellHeight = layoutHeight.intValue();
              }
            } catch (Exception e) {
              // ignore
            } // ignore, use 0

            int topPadding = getCellPadding(facesContext, layout, firstRenderedRow);
            String cellStyle =
                (cellWidth != -1 ? "width: " + cellWidth + "px;" : "")
                + (cellHeight != -1 ? " height: " + (cellHeight + topPadding) + "px;" : "");
            cellStyle += getOverflow(cell);


            writer.startElement(HtmlConstants.TD, null);
            writer.writeClassAttribute("tobago-gridLayout-cell-td");
            writer.writeAttribute(HtmlAttributes.STYLE, cellStyle, false);
            if (spanX > 1) {
              writer.writeAttribute(HtmlAttributes.COLSPAN, spanX);
            }
            if (spanY > 1) {
              writer.writeAttribute(HtmlAttributes.ROWSPAN, spanY);
            }

            writer.flush();

            if (ComponentUtil.getAttribute(layout, ATTR_CELLSPACING) != null) {
              cellStyle += " padding: " + getCellSpacing(facesContext, layout) + "px;";
            }

            writer.startElement(HtmlConstants.DIV, null);
            writer.writeClassAttribute(classes);
            writer.writeAttribute(HtmlAttributes.STYLE, cellStyle, false);
            writer.flush();
            RenderUtil.encode(facesContext, cell);

            writer.endElement(HtmlConstants.DIV);
            writer.endElement(HtmlConstants.TD);

            firstRenderedColum = false;

            HtmlRendererUtil.removeStyleClasses(cell);

          }
        }

        writer.endElement(HtmlConstants.TR);
        firstRenderedRow = false;
      }
    }
    writer.endElement(HtmlConstants.TABLE);

    if (TobagoConfig.getInstance(facesContext).isFixLayoutTransparency()) {
      for (UIComponent child : (List<UIComponent>) layout.getParent().getChildren()) {
        if (child instanceof UIHiddenInput) {
          RenderUtil.encode(facesContext, child);
        }
      }
    }
  }

  public void encodeEnd(FacesContext facesContext,
      UIComponent component) throws IOException {
    if (component.getParent() instanceof UIPage) {
      LOG.error("XXXXXXXXXXXXXXXXXXXXXXX  never XXXXXXXXXXXXXXXXXXXXXX", new Exception());
    } else {
      encodeChildrenOfComponent(facesContext, component.getParent());
    }
  }

  private String getOverflow(UIComponent cell) {
    String overflow = "";

    String scrollbars = (String) cell.getAttributes().get(ATTR_SCROLLBARS);
    if (scrollbars != null) {
      if (scrollbars.equals("false")) {
        overflow = " overflow: hidden;";
      } else if (scrollbars.equals("true")) {
        overflow = " overflow: scroll;";
      } else if (scrollbars.equals("auto")) {
        overflow = " overflow: auto;";
      } else {
        if (LOG.isWarnEnabled()) {
          LOG.warn("Illegal value for attribute 'scrollbars' : " + scrollbars);
        }
      }
    }

    return overflow;
  }

  private int getCellPadding(
      FacesContext facesContext, UIComponent component, boolean first) {
    return first ? 0 : getCellSpacing(facesContext, component);
  }

  private int getBorder(UIComponent component) {
    int border = 0;
    String borderWidth
        = (String) component.getAttributes().get(ATTR_BORDER);
    try {
      if (borderWidth != null) {
        border = Integer.parseInt(borderWidth);
      }
    } catch (NumberFormatException e) {
      if (LOG.isWarnEnabled()) {
        LOG.warn("Can't parse border, using 0!");
      }
    }

    return border;
  }

  private int getSpacingSum(UIComponent component, FacesContext facesContext,
      int count) {
    int space = 0;
    int border = getBorder(component);
    int cellSpacing = getCellSpacing(facesContext, component);
    if (border != 0) {
      space = (border * 2) + count * 2;
    }
    space += (count - 1) * cellSpacing;
    if (LOG.isDebugEnabled()) {
      LOG.debug("extra count =" + count + ",  space = " + space);
    }
    return space;
  }

  private int getWidthSpacingSum(UIGridLayout component,
      FacesContext facesContext, int renderedColumnCount) {
    int spacingSum = getSpacingSum(component, facesContext, renderedColumnCount);
    spacingSum += getComponentExtraWidth(facesContext, component);
    return spacingSum;
  }

  private int getCellSpacing(FacesContext facesContext, UIComponent component) {
    String cellspacing = (String) component.getAttributes().get(ATTR_CELLSPACING);
    if (cellspacing != null) {
      try {
        return Integer.parseInt(cellspacing);
      } catch (NumberFormatException e) {
        if (LOG.isWarnEnabled()) {
          LOG.warn("Illegal value for cellspacing : " + cellspacing
              + " using default");
        }
        // ignore and return defaut value
      }
    }
    return getConfiguredValue(facesContext,  component, "cellSpacing");
  }

  private void layoutEnd(FacesContext facesContext, UIComponent component) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("doLayout end");
    }
    UIGridLayout layout = (UIGridLayout) component;
    final Map attributes = layout.getParent().getAttributes();


    boolean needVerticalScrollbar = false;
    Integer innerHeight =
          (Integer) attributes.get(ATTR_INNER_HEIGHT);
    if (innerHeight != null && innerHeight.intValue() > 0) {
      int value = innerHeight.intValue();
      int minimum = calculateLayoutHeight(facesContext, layout.getParent(), true);
      if (minimum > value) {
        value = minimum;
        needVerticalScrollbar = true;
      }
      layoutHeight(Integer.valueOf(value), layout, facesContext);
    } else {
      calculateHiddenForRows(layout.ensureRows());
    }


    Integer innerWidth =
          (Integer) attributes.get(ATTR_INNER_WIDTH);
    if (innerWidth != null && innerWidth.intValue() != -1) {
      int value = innerWidth.intValue();
      if (needVerticalScrollbar) {
        value -= getConfiguredValue(facesContext, component, "scrollbarWidth");
        HtmlRendererUtil.replaceStyleAttribute(layout, "width", value);
      }
      layoutWidth(Integer.valueOf(value), layout, facesContext);
    }

  }

  private int getHeightSpacingSum(UIGridLayout layout,
      FacesContext facesContext, int renderedRowCount) {
    int spacingSum = getSpacingSum(layout, facesContext, renderedRowCount);
    spacingSum += getComponentExtraHeight(facesContext, layout);
    return spacingSum;
  }

  private void layoutWidth(Integer innerWidth, UIGridLayout layout,
      FacesContext facesContext) {

    final List<UIGridLayout.Row> rows = layout.ensureRows();
    //final int columnCount = layout.getColumnCount();

    final LayoutTokens layoutTokens = layout.getColumnLayout();
    //layoutTokens.ensureSize(columnCount, new RelativeLayoutToken(1));

    //LayoutInfo.createLayoutTokens((String)
        //layout.getAttributes().get(ATTR_COLUMNS), columnCount);

    int renderedColumnCount = 0;
    if (!rows.isEmpty()) {
      UIGridLayout.Row row = rows.get(0);
      final List cells = row.getElements();
      renderedColumnCount = cells.size();
      for (int i = 0; i < cells.size(); i++) {
        Object cell = cells.get(i);
        boolean hidden = false;
        if (isHidden(cell)) {
          hidden = true;
          for (int j = 1; j < rows.size(); j++) {
            hidden &= isHidden(rows.get(j).getElements().get(i));
          }
        }
        if (hidden) {
          layoutTokens.set(i, new HideLayoutToken());
          renderedColumnCount--;
        }
      }
    }

    innerWidth -= getWidthSpacingSum(layout, facesContext, renderedColumnCount);
    LayoutInfo layoutInfo =
        new LayoutInfo(layoutTokens.getSize(), innerWidth.intValue(), layoutTokens, layout.getClientId(facesContext),
            layout.isIgnoreFree());

    parseFixedWidth(layoutInfo, layout, facesContext);
    layoutInfo.parseColumnLayout(innerWidth.doubleValue(),
        getCellSpacing(facesContext, layout));

    setColumnWidths(layout, layoutInfo, facesContext);
    layout.getAttributes().put(ATTR_WIDTH_LIST,
        layoutInfo.getSpaceList());
  }

  private boolean isHidden(Object cell) {
    return (cell instanceof UIComponent && !((UIComponent) cell).isRendered())
              || (cell instanceof UIGridLayout.Marker
                  && !((UIGridLayout.Marker) cell).isRendered());
  }

  private void layoutHeight(Integer innerHeight, UIGridLayout layout,
      FacesContext facesContext) {

    final List<UIGridLayout.Row> rows = layout.ensureRows();
    LayoutTokens layoutTokens = layout.getRowLayout();
    layoutTokens.ensureSize(rows.size(), rows.size() == 1 ? new RelativeLayoutToken(1) : new FixedLayoutToken());
        /*LayoutInfo.createLayoutTokens(
        (String) layout.getAttributes().get(ATTR_ROWS),
        rows.size(), rows.size() == 1 ? "1*" : "fixed");*/

    calculateHiddenForRows(rows);
    
    int renderedRowCount = rows.size();
    for (int i = 0; i < rows.size(); i++) {
      if (rows.get(i).isHidden()) {
        layoutTokens.set(i, new HideLayoutToken());
        renderedRowCount--;
      }
    }

    innerHeight -= getHeightSpacingSum(layout, facesContext, renderedRowCount);

    LayoutInfo layoutInfo
        = new LayoutInfo(rows.size(), innerHeight.intValue(), layoutTokens, layout.getClientId(facesContext));

    if (!layoutTokens.isEmpty()) {
      parseFixedHeight(layoutInfo, layout, facesContext);
      layoutInfo.parseColumnLayout(innerHeight.doubleValue(),
          getCellSpacing(facesContext, layout));
    }

    setColumnHeights(layout, layoutInfo, facesContext);

  }

  private void calculateHiddenForRows(List<UIGridLayout.Row> rows) {
    for (int i = 0; i < rows.size(); i++) {
      boolean hidden = true;
      UIGridLayout.Row row = rows.get(i);
      List cells = row.getElements();
      for (Object cell : cells) {
        hidden &= isHidden(cell);
      }
      row.setHidden(hidden);
    }
  }

  private void parseFixedWidth(LayoutInfo layoutInfo, UIGridLayout layout,
      FacesContext facesContext) {
    parseFixedSpace(layoutInfo, layout, true, facesContext);
  }
  private void parseFixedHeight(LayoutInfo layoutInfo, UIGridLayout layout,
      FacesContext facesContext) {
    parseFixedSpace(layoutInfo, layout, false, facesContext);
  }

  public void parseFixedSpace(LayoutInfo layoutInfo, UIGridLayout layout,
      LayoutTokens layoutTokens, boolean width, FacesContext facesContext) {

    //String[] tokens = layoutInfo.getLayoutTokens();
    for (int i = 0; i < layoutTokens.getSize(); i++) {
      LayoutToken token = layoutTokens.get(i);
      if (token instanceof FixedLayoutToken) {
        int max = 0;
        final List<UIGridLayout.Row> rows = layout.ensureRows();
        if (!rows.isEmpty()) {
          if (width) {
            max = getMaxWidth(facesContext, rows, i, false);
          } else {
            if (i < rows.size()) {      //
              UIGridLayout.Row row = rows.get(i);
              max = getMaxHeight(facesContext, row, false);
            } else {
              layoutInfo.update(0, i);
              if (LOG.isWarnEnabled()) {
                LOG.warn("More LayoutTokens found than rows! skipping! tokens = "
                    + token + "  components = "
                    + rows.size());
              }
            }
          }
          layoutInfo.update(max, i);
        }
        if (LOG.isDebugEnabled()) {
          LOG.debug("set column " + i + " from fixed to with " + max);
        }
      }
    }
  }
  private void parseFixedSpace(LayoutInfo layoutInfo, UIGridLayout layout,
                               boolean width, FacesContext facesContext) {

    LayoutTokens tokens = layoutInfo.getLayoutTokens();
    for (int i = 0; i < tokens.getSize(); i++) {
      LayoutToken token = tokens.get(i);
      if (token instanceof FixedLayoutToken) {
        int max = 0;
        final List<UIGridLayout.Row> rows = layout.ensureRows();
        if (!rows.isEmpty()) {
          if (width) {
            max = getMaxWidth(facesContext, rows, i, false);
          } else {
            if (i < rows.size()) {      //
              UIGridLayout.Row row = rows.get(i);
              max = getMaxHeight(facesContext, row, false);
            } else {
              layoutInfo.update(0, i);
              if (LOG.isWarnEnabled()) {
                LOG.warn("More LayoutTokens found than rows! skipping! tokens = "
                    + token + "  components = "
                    + rows.size());
              }
            }
          }
          layoutInfo.update(max, i);
        }
        if (LOG.isDebugEnabled()) {
          LOG.debug("set column " + i + " from fixed to with " + max);
        }
      }
    }
  }

  private int getMaxHeight(FacesContext facesContext, UIGridLayout.Row row, boolean minimum) {
    int maxHeight = 0;
    List cells = row.getElements();
    for (Object cell : cells) {
      if (cell instanceof UIComponent) {
        UIComponent component = (UIComponent) cell;
        int height = -1;
        if (minimum) {
          height = (int) LayoutUtil.getMinimumSize(facesContext, component).getHeight();
        } else {
          LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component);
          if (renderer != null) {
            height = renderer.getFixedHeight(facesContext, component);
          }
        }
        maxHeight = Math.max(maxHeight, height);
      } else if (cell instanceof UIGridLayout.Marker) {
        // ignore 
      } else {
        // TODO is this needed?
        LOG.error("Object is not instanceof UIComponent " + cell.getClass().getName());
      }
    }
    return maxHeight;
  }

  private int getMaxWidth(FacesContext facesContext, List<UIGridLayout.Row> rows, int column, boolean minimum) {
    int maxWidth = 0;

    for (UIGridLayout.Row row : rows) {
      if (column < row.getElements().size()) {
        Object object = row.getElements().get(column);

        if (object instanceof UIComponent) {
          UIComponent component = (UIComponent) object;
          if (component instanceof UICell) {
            if (((UICell) component).getSpanX() > 1) {
              continue;
            }
          }
          int max = -1;
          if (minimum) {
            max = (int) LayoutUtil.getMinimumSize(facesContext, component).getWidth();
          } else {
            LayoutInformationProvider renderer = ComponentUtil.getRenderer(facesContext, component);
            if (renderer != null) {
              max = renderer.getFixedWidth(facesContext, component);
            }
          }
          maxWidth = Math.max(maxWidth, max);
        } else if (object instanceof UIGridLayout.Marker) {
        // ignore 
        } else {
          // TODO is this needed?
          LOG.error("Object is not instanceof UIComponent " + object.getClass().getName());
        }
      }
    }
    return maxWidth;
  }

  private void setColumnWidths(UIGridLayout layout, LayoutInfo layoutInfo,
      FacesContext facesContext) {
    List<UIGridLayout.Row> rows = layout.ensureRows();

    for (Iterator iter = rows.iterator(); iter.hasNext();) {
      UIGridLayout.Row row = (UIGridLayout.Row) iter.next();
      List cells = row.getElements();
      int columnCount = layout.getColumnCount();
      for (int i = 0; i < columnCount; i++) {
        if (cells.get(i) instanceof UIComponent) {
          UIComponent cell = (UIComponent) cells.get(i);
          int spanX = UIGridLayout.getSpanX(cell);
          int cellWidth = 0;
          for (int j = i; j < i + spanX; j++) {
            cellWidth += layoutInfo.getSpaceForColumn(j);
          }
          if (spanX > 1 && getBorder(layout) > 0) {
            cellWidth += ((spanX - 1) * 2);
          }
          cellWidth += (spanX - 1) * getCellSpacing(facesContext, layout);
          LayoutUtil.maybeSetLayoutAttribute(cell,
              ATTR_LAYOUT_WIDTH, Integer.valueOf(cellWidth));
        }
      }
    }
  }

  private void setColumnHeights(UIGridLayout layout, LayoutInfo layoutInfo,
      FacesContext facesContext) {
    List<UIGridLayout.Row> rows = layout.ensureRows();

    for (int i = 0; i < rows.size(); i++) {
      UIGridLayout.Row row = rows.get(i);
      List cells = row.getElements();
      int columnCount = layout.getColumnCount();
      for (int j = 0; j < columnCount; j++) {
        if (cells.get(j) instanceof UIComponent) {
          UIComponent cell = (UIComponent) cells.get(j);
          int spanY = UIGridLayout.getSpanY(cell);
          int cellHeight = 0;
          for (int k = i; k < i + spanY; k++) {
            cellHeight += layoutInfo.getSpaceForColumn(k);
          }
          if (spanY > 1 && getBorder(layout) > 0) {
            cellHeight += ((spanY - 1) * 2);
          }
          cellHeight += (spanY - 1) * getCellSpacing(facesContext, layout);
          if (LOG.isDebugEnabled()) {
            LOG.debug("set height of " + cellHeight + "px to "
                + cell.getRendererType() + " layoutInfo " + layoutInfo.toString());
          }
          cell.getAttributes().put(ATTR_LAYOUT_HEIGHT, Integer.valueOf(cellHeight));
          cell.getAttributes().remove(ATTR_INNER_HEIGHT);
          if (cell instanceof UICell || cell instanceof UIForm) {
            List children = LayoutUtil.addChildren(new ArrayList<UIComponent>(), cell);
            for (Iterator childs = children.iterator(); childs.hasNext();) {
              UIComponent component = (UIComponent) childs.next();
              if (LOG.isDebugEnabled()) {
                LOG.debug("set height of " + cellHeight + "px to "
                    + component.getRendererType());
              }
              component.getAttributes().put(ATTR_LAYOUT_HEIGHT, Integer.valueOf(cellHeight));
              component.getAttributes().remove(ATTR_INNER_HEIGHT);

            }
          }
        }
      }
    }
  }


  public void layoutBegin(FacesContext facesContext, UIComponent component) {
    if (LOG.isInfoEnabled()) {
      LOG.info("############################## layoutBegin +++++++++++++++++++++++++++++++++++++++++");
    }
    HtmlRendererUtil.layoutSpace(facesContext, component, true);
    HtmlRendererUtil.layoutSpace(facesContext, component, false);

    if (component instanceof UIGridLayout) {
      layoutMargins((UIGridLayout) component);
    }
  }

  private void layoutMargins(UIGridLayout layout) {

    HtmlStyleMap style = (HtmlStyleMap) layout.getAttributes().get(ATTR_STYLE);

    if (style != null) {
      int marginTop = getMarginAsInt(layout.getMarginTop());
      int marginRight = getMarginAsInt(layout.getMarginRight());
      int marginBottom = getMarginAsInt(layout.getMarginBottom());
      int marginLeft = getMarginAsInt(layout.getMarginLeft());
      if(marginTop > 0) {
        style.put("margin-top", marginTop);
      }
      if(marginRight > 0) {
        style.put("margin-right", marginRight);
        Integer width = style.getInt("width");
        if (width != null) {
          width -= marginRight;
          style.put("width", width);
        }
      }
      if(marginBottom > 0) {
        style.put("margin-bottom", marginBottom);
      }
      if(marginLeft > 0) {
        style.put("margin-left", marginLeft);
        Integer width = style.getInt("width");
        if (width != null) {
          width -= marginLeft;
          style.put("width", width);
        }
      }

      //layout.getAttributes().put(ATTR_LAYOUT_TABLE_STYLE, style);
    }
  }

  public int getComponentExtraWidth(FacesContext facesContext,
      UIComponent component) {
    int extra = 0;
    UIGridLayout layout = (UIGridLayout) component;

    extra += getMarginAsInt(layout.getMarginRight());
    extra += getMarginAsInt(layout.getMarginLeft());

    return extra;
  }

  public int getComponentExtraHeight(FacesContext facesContext,
      UIComponent component) {
    int extra = 0;
    UIGridLayout layout = (UIGridLayout) component;

    extra += getMarginAsInt(layout.getMarginTop());
    extra += getMarginAsInt(layout.getMarginBottom());
    return extra;
  }

  private int getMarginAsInt(String margin) {
    if (margin != null) {
      margin = removePx(margin);
      try {
        return Integer.parseInt(margin);
      } catch (NumberFormatException e) {
        if (LOG.isWarnEnabled()) {
          LOG.warn("Illegal Margin : " + margin
              + " exception : " + e.getMessage(), e);
        }
      }
    }
    return 0;
  }

  private String removePx(String margin) {
    if (margin!=null&&margin.endsWith("px")) {
      margin = margin.substring(0, margin.length() - 2);
    }
    return margin;
  }
}

