package com.vaadin.components.grid.table;

import java.util.Arrays;
import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.TextOverflow;
import com.google.gwt.query.client.js.JsUtils;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.HTML;
import com.vaadin.client.widgets.Grid.Column;
import com.vaadin.components.common.js.JS;
import com.vaadin.components.common.js.JS.Setter;
import com.vaadin.components.common.js.JSArray;
import com.vaadin.components.grid.GridComponent;
import com.vaadin.components.grid.config.JSCell;
import com.vaadin.components.grid.config.JSColumn;
import com.vaadin.components.grid.config.JSStaticCell;
import com.vaadin.components.grid.data.GridDataSource;
import com.vaadin.components.grid.data.GridDomTableDataSource;
import com.vaadin.shared.ui.grid.GridConstants;

public final class GridColumn extends Column<Object, Object> {

    private final JSColumn jsColumn;
    private final GridComponent gridComponent;

    public static GridColumn addColumn(JSColumn jsColumn,
            GridComponent gridComponent) {
        GridColumn result = new GridColumn(jsColumn, gridComponent);
        gridComponent.getGrid().addColumn(result);
        result.bindProperties();
        return result;
    }

    private GridColumn(JSColumn jsColumn, GridComponent gridComponent) {
        this.jsColumn = jsColumn;
        this.gridComponent = gridComponent;

        // Default renderer
        setRenderer((cell, data) -> {
            Element element = cell.getElement();
            Element leafElement;
            String content = JS.isUndefinedOrNull(data) ? "" : data.toString();

            // No need for wrapper if it's an HTML element #52
            if(new HTML(content).getElement().getFirstChildElement() != null) {
                leafElement = element;
            }else {
                if (!element.hasChildNodes()) {
                    // Need to create a new wrapper
                    Element wrapper = DOM.createSpan();
                    wrapper.getStyle().setOverflow(Overflow.HIDDEN);
                    wrapper.getStyle().setTextOverflow(TextOverflow.ELLIPSIS);
                    element.appendChild(wrapper);
                }
                leafElement = element.getFirstChildElement();
            }

            if (gridComponent.getDataSource() instanceof GridDomTableDataSource) {
                leafElement.setInnerHTML(content);
            } else {
                leafElement.setInnerText(content);
            }
        });
    }

    private JSStaticCell getDefaultHeaderCellReference() {
        GridStaticSection staticSection = gridComponent.getStaticSection();
        return staticSection.getHeaderCellByColumn(
                staticSection.getDefaultHeader(), this);
    }

    private void bindProperties() {
        JS.definePropertyAccessors(jsColumn, "headerContent", v -> {
            getDefaultHeaderCellReference().setContent(v);
            gridComponent.updateWidth();
        }, () -> getDefaultHeaderCellReference().getContent());

        bind("flex", v -> setExpandRatio(((Double) v).intValue()));
        bind("sortable", v -> setSortable((Boolean) v));
        bind("readOnly", v -> setEditable(!(boolean) v));
        bind("renderer", v -> setRenderer((cell, data) -> {
            JSCell jsCell = JSCell.create(cell, gridComponent.getContainer());
            JS.exec(v, jsCell);
        }));
        bind("minWidth",
                v -> setMinimumWidth(JS.isUndefinedOrNull(v) ? GridConstants.DEFAULT_MIN_WIDTH
                        : (double) v));
        bind("maxWidth",
                v -> setMaximumWidth(JS.isUndefinedOrNull(v) ? GridConstants.DEFAULT_MAX_WIDTH
                        : (double) v));
        bind("width",
                v -> setWidth(JS.isUndefinedOrNull(v) ? GridConstants.DEFAULT_COLUMN_WIDTH_PX
                        : (double) v));
    }

    private void bind(String propertyName, final Setter setter) {
        JS.definePropertyAccessors(jsColumn, propertyName, v -> {
            setter.setValue(v);
            gridComponent.updateWidth();
        }, null);
    }

    public JSColumn getJsColumn() {
        return jsColumn;
    }

    @Override
    public Object getValue(Object dataItem) {
        dataItem = GridDataSource.extractDataItem(dataItem);

        Object result = null;
        if (JS.isPrimitiveType(dataItem)) {
            if (getColumnIndex() == 0) {
                result = dataItem;
            }
        } else {
            if (JsUtils.isArray((JavaScriptObject) dataItem)) {
                result = ((JSArray<Object>) dataItem).get(getColumnIndex());
            } else {
                result = getNestedProperty(dataItem,
                        Arrays.asList(jsColumn.getName().split("\\.")));
            }
        }
        return result;
    }

    private Object getNestedProperty(Object o, List<String> props) {
        Object result = null;
        if (props.isEmpty()) {
            result = o;
        } else if (JS.isObject(o)) {
            result = getNestedProperty(
                    JsUtils.prop((JavaScriptObject) o, props.get(0)),
                    props.subList(1, props.size()));
        }
        return result;
    }

    private int getColumnIndex() {
        return gridComponent.getColumns().indexOf(jsColumn);
    }
}

@@ -1672,41 +1672,8 @@
 ();%0A
-            Element leafElement;%0A
     
@@ -1765,59 +1765,80 @@
     
-// No need for wrapper if it's an HTML element #52%0A
+if (gridComponent.getDataSource() instanceof GridDomTableDataSource%0A    
     
@@ -1849,11 +1849,15 @@
     
-if(
+    && 
 new 
@@ -1933,37 +1933,45 @@
         
-leafE
+e
 lement
- = elem
+.setInnerHTML(cont
 ent
+)
 ;%0A      
@@ -1977,16 +1977,17 @@
        %7D
+ 
 else %7B%0A 
@@ -2005,35 +2005,136 @@
     
-if (!element.hasChildNodes(
+Element wrapper = element.getFirstChildElement();%0A                if (wrapper == null %7C%7C !wrapper.getPropertyBoolean(%22iswrapper%22
 )) %7B
@@ -2202,32 +2202,24 @@
             
-Element 
 wrapper = DO
@@ -2406,35 +2406,52 @@
     
-element.appendChild(wrapper
+wrapper.setPropertyBoolean(%22iswrapper%22, true
 );%0A 
@@ -2455,32 +2455,24 @@
 ;%0A          
-      %7D%0A
             
@@ -2473,56 +2473,33 @@
     
-      leafElement = element.getFirst
+element.removeAll
 Child
-Elem
+r
 en
-t
 ();%0A
@@ -2514,19 +2514,16 @@
     
-%7D%0A%0A
         
     
@@ -2522,154 +2522,55 @@
     
-    if (gridComponent.getDataSource() instanceof GridDomTableDataSource) %7B%0A                leafElement.setInnerHTML(content);%0A            %7D else %7B
+element.appendChild(wrapper);%0A                %7D
 %0A   
@@ -2586,19 +2586,15 @@
     
-leafElement
+wrapper
 .set

Fix for default renderer