/*
 * Copyright 2015 JBoss, by Red Hat, Inc
 *
 * Licensed 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.uberfire.ext.widgets.common.client.tables;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.AbstractCellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;

public class ResizableHeader<T> extends Header<String> {

    private static final int width = 20;
    private Column<T, ?> column = null;
    private AbstractCellTable<T> cellTable;
    private String title = "";

    public ResizableHeader(String title,
                           AbstractCellTable<T> cellTable,
                           Column<T, ?> column) {
        super(new HeaderCell());
        this.title = title;
        this.cellTable = cellTable;
        this.column = column;
    }

    @Override
    public String getValue() {
        return title;
    }

    @Override
    public void onBrowserEvent(Context context,
                               Element target,
                               NativeEvent event) {
        String eventType = event.getType();
        if (eventType.equals("mousemove")) {
            new ColumnResizeHelper<T>(cellTable,
                                      column,
                                      target);
        } else {
            return;
        }
    }

    private void setCursor(Element element,
                           Cursor cursor) {
        element.getStyle().setCursor(cursor);
    }

    static class HeaderCell extends AbstractCell<String> {

        public HeaderCell() {
            super("click",
                  "mousedown",
                  "mousemove",
                  "dblclick");
        }

        @Override
        public void render(Context context,
                           String value,
                           SafeHtmlBuilder sb) {
            sb.append(SafeHtmlUtils.fromString(value));
        }
    }

    class ColumnResizeHelper<E> implements NativePreviewHandler {

        private HandlerRegistration handler;
        private AbstractCellTable<E> table;
        private Column<E, ?> col;
        private Element el;
        private boolean mousedown;
        private Element measuringElement;

        public ColumnResizeHelper(AbstractCellTable<E> table,
                                  Column<E, ?> col,
                                  Element el) {
            this.el = el;
            this.table = table;
            this.col = col;
            handler = Event.addNativePreviewHandler(this);
        }

        @SuppressWarnings("unchecked")
        @Override
        public void onPreviewNativeEvent(NativePreviewEvent event) {
            NativeEvent nativeEvent = event.getNativeEvent();
            nativeEvent.preventDefault();
            nativeEvent.stopPropagation();

            String eventType = nativeEvent.getType();
            int clientX = nativeEvent.getClientX();
            if (eventType.equals("mousemove") && mousedown) {
                int absoluteLeft = el.getAbsoluteLeft();
                int newWidth = clientX - absoluteLeft;
                newWidth = newWidth < width ? width : newWidth;
                table.setColumnWidth(col,
                                     newWidth + "px");
                return;
            }

            if (eventType.equals("mousemove") || eventType.equals("mousedown")) {
                Element eventTargetEl = nativeEvent.getEventTarget().cast();
                int absoluteLeft = eventTargetEl.getAbsoluteLeft();
                int offsetWidth = eventTargetEl.getOffsetWidth();
                if (clientX > absoluteLeft + offsetWidth - width) {
                    if (eventType.equals("mousedown")) {
                        mousedown = true;
                    } else {
                        setCursor(el,
                                  Cursor.COL_RESIZE);
                    }
                } else {
                    removeHandler();
                    return;
                }
            } else if (eventType.equals("mouseup")) {
                mousedown = false;
            } else if (eventType.equals("mouseout") && !mousedown) {
                removeHandler();
                return;
            }

            if (eventType.equals("dblclick")) {
                // Get column
                nativeEvent.preventDefault();
                nativeEvent.stopPropagation();
                double max = 0;
                startMeasuring();
                for (E t : table.getVisibleItems()) {
                    Object value = col.getValue(t);
                    SafeHtmlBuilder sb = new SafeHtmlBuilder();
                    Cell<Object> cell = (Cell<Object>) col.getCell();
                    cell.render(null,
                                value,
                                sb);
                    max = Math.max(measureText(sb.toSafeHtml().asString()),
                                   max);
                }
                finishMeasuring();
                table.setColumnWidth(col,
                                     (max + width) + "px");
                removeHandler();
            }
        }

        private void removeHandler() {
            handler.removeHandler();
            setCursor(el,
                      Cursor.DEFAULT);
        }

        private void startMeasuring() {
            Document document = Document.get();
            measuringElement = document.createElement("div");
            measuringElement.getStyle().setPosition(Position.ABSOLUTE);
            measuringElement.getStyle().setLeft(-1000,
                                                Unit.PX);
            measuringElement.getStyle().setTop(-1000,
                                               Unit.PX);
            document.getBody().appendChild(measuringElement);
        }

        private double measureText(String text) {
            measuringElement.setInnerHTML(text);
            return measuringElement.getOffsetWidth();
        }

        private void finishMeasuring() {
            Document.get().getBody().removeChild(measuringElement);
        }
    }
}
