/*
 * Copyright 2011 JBoss 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.jbpm.formbuilder.client.form.items;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jbpm.formapi.client.CommonGlobals;
import org.jbpm.formapi.client.FormBuilderException;
import org.jbpm.formapi.client.bus.ui.NotificationEvent;
import org.jbpm.formapi.client.effect.FBFormEffect;
import org.jbpm.formapi.client.form.FBFormItem;
import org.jbpm.formapi.client.form.LayoutFormItem;
import org.jbpm.formapi.client.form.PhantomPanel;
import org.jbpm.formapi.shared.api.FormItemRepresentation;
import org.jbpm.formapi.shared.api.items.BorderPanelRepresentation;
import org.jbpm.formapi.shared.api.items.BorderPanelRepresentation.Position;
import org.jbpm.formbuilder.client.FormBuilderGlobals;
import org.jbpm.formbuilder.client.messages.I18NConstants;

import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.gwtent.reflection.client.Reflectable;

/**
 * UI form layout item. Represents a border layout
 */
@Reflectable
public class BorderLayoutFormItem extends LayoutFormItem {

    private EventBus bus = CommonGlobals.getInstance().getEventBus();
    private I18NConstants i18n = FormBuilderGlobals.getInstance().getI18n();
    
    private Map<Position, FBFormItem> locations = new HashMap<Position, FBFormItem>();
    
    private Position currentPosition = null;
    
    private Grid grid = new Grid(1,1) {
        @Override
        public boolean remove(Widget widget) {
            if (widget instanceof FBFormItem) {
                super.remove(widget);
                return BorderLayoutFormItem.this.remove(widget);
            } else {
                return super.remove(widget);
            }
        }
    };

    public BorderLayoutFormItem() {
        this(new ArrayList<FBFormEffect>());
    }
    
    public BorderLayoutFormItem(List<FBFormEffect> formEffects) {
        super(formEffects);
        add(grid);
        grid.setSize("180px", "180px");
        setSize("180px", "180px");
    }

    protected void setDropPosition(Position position) {
        this.currentPosition = position;
    }
    
    protected Position obtainPosition(int x, int y) {
        int xpos = x - grid.getAbsoluteLeft(); 
        int width = grid.getOffsetWidth();
        int ypos = y - grid.getAbsoluteTop();
        int height = grid.getOffsetHeight();
        boolean left = width / 3 > xpos;
        boolean right = width * 2 / 3 < xpos;
        boolean bottom = height * 2 / 3 < ypos;
        boolean top = height / 3 > ypos;
        if (!left && !right && !bottom && !top) {
            return Position.CENTER;
        } else if (!left && !right && top) {
            return Position.NORTH;
        } else if (!left && !right && bottom) {
            return Position.SOUTH;
        } else if (!bottom && !top && right) {
            return Position.EAST;
        } else if (!bottom && !top && left) {
            return Position.WEST;
        } else if (top && right) {
            return Position.NORTHEAST;
        } else if (bottom && right) {
            return Position.SOUTHEAST;
        } else if (top && left) {
            return Position.NORTHWEST;
        } else if (bottom && left) {
            return Position.SOUTHWEST;
        } else {
            return null;
        }
    }
    
    @Override
    public Map<String, Object> getFormItemPropertiesMap() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("width", getWidth());
        map.put("height", getHeight());
        return map;
    }

    @Override
    public void saveValues(Map<String, Object> asPropertiesMap) {
        setHeight(extractString(asPropertiesMap.get("height")));
        setWidth(extractString(asPropertiesMap.get("width")));
        populate(this.grid);
    }

    private void populate(Grid myGrid) {
        if (getHeight() != null && !"".equals(getHeight())) {
            this.grid.setHeight(getHeight());
        }
        if (getWidth() != null && !"".equals(getWidth())) {
            this.grid.setWidth(getWidth());
        }
    }

    @Override
    public FormItemRepresentation getRepresentation() {
        BorderPanelRepresentation rep = super.getRepresentation(new BorderPanelRepresentation());
        for (Map.Entry<Position, FBFormItem> entry : this.locations.entrySet()) {
            Position key = entry.getKey();
            FormItemRepresentation value = entry.getValue().getRepresentation();
            rep.putItem(key, value);
        }
        return rep;
    }
    
    @Override
    public void populate(FormItemRepresentation rep) throws FormBuilderException {
        if (!(rep instanceof BorderPanelRepresentation)) {
            throw new FormBuilderException(i18n.RepNotOfType(rep.getClass().getName(), "BorderPanelRepresentation"));
        }
        super.populate(rep);
        BorderPanelRepresentation brep = (BorderPanelRepresentation) rep;
        Map<Position, FormItemRepresentation> repItems = brep.getItems();
        if (repItems != null) {
            for (Map.Entry<Position, FormItemRepresentation> entry : repItems.entrySet()) {
                Position key = entry.getKey();
                FBFormItem value = super.createItem(entry.getValue());
                this.currentPosition = key;
                this.add(value);
            }
        }
    }

    @Override
    public FBFormItem cloneItem() {
        BorderLayoutFormItem clone = super.cloneItem(new BorderLayoutFormItem(getFormEffects()));
        clone.currentPosition = this.currentPosition;
        clone.grid = (Grid) cloneDisplay(null);
        clone.locations = new HashMap<Position, FBFormItem>(this.locations);
        return clone;
    }

    @Override
    public Widget cloneDisplay(Map<String, Object> data) {
        int rows = this.grid.getRowCount();
        int columns = this.grid.getColumnCount();
        Grid g = new Grid(rows, columns);
        populate(g);
        for (int index = 0; index < columns * rows; index++) {
            int column = index%columns;
            int row = index/columns;
            FBFormItem item = (FBFormItem) this.grid.getWidget(row, column);
            if (item != null) {
                g.setWidget(row, column, item.cloneDisplay(data));
            }
        }
        super.populateActions(g.getElement());
        return g;
    }

    @Override
    public HasWidgets getPanel() {
        return grid;
    }
    
    private int getRow(Position pos) {
        int row = 0;
        switch (pos) {
        case NORTH:
        case NORTHEAST:
        case NORTHWEST:
            ensureRows();
            row = 0;
            break;
        case SOUTH:
        case SOUTHEAST:
        case SOUTHWEST:
            ensureRows();
            row = 2;
            break;
        default: //CENTER
            row = getMiddleRow();
            break;
        }
        return row;
    }
    
    private int getColumn(Position pos) {
        int col = 0;
        switch (pos) {
        case EAST:
        case NORTHEAST:
        case SOUTHEAST:
            ensureColumns();
            col = 2;
            break;
        case WEST:
        case SOUTHWEST:
        case NORTHWEST:
            ensureColumns();
            col = 0;
            break;
        default: //CENTER
            col = getMiddleColumn();
            break;
        }
        return col;
    }
    
    @Override
    public boolean add(FBFormItem item) {
        currentPosition = obtainPosition(item.getDesiredX(), item.getDesiredY());
        int row = getRow(currentPosition);
        int col = getColumn(currentPosition);
        if (locations.get(currentPosition) == null) {
            grid.setWidget(row, col, item);
            locations.put(currentPosition, item);
            currentPosition = null;
            return super.add(item);
        } else {
            bus.fireEvent(new NotificationEvent(NotificationEvent.Level.WARN, i18n.BorderLayoutPositionPopulated()));
            return false;
        }
    }
    
    @Override
    public void add(PhantomPanel phantom, int x, int y) {
        Position pos = obtainPosition(x, y);
        if (locations.get(pos) == null) {
            grid.setWidget(getRow(pos), getColumn(pos), phantom);
        }
    }
    
    @Override
    public void replacePhantom(FBFormItem item) {
        currentPosition = obtainPosition(item.getDesiredX(), item.getDesiredY());
        int row = getRow(currentPosition);
        int col = getColumn(currentPosition);
        if (locations.get(currentPosition) == null) {
            grid.setWidget(row, col, item);
            locations.put(currentPosition, item);
            currentPosition = null;
            super.add(item);
        } else {
            bus.fireEvent(new NotificationEvent(NotificationEvent.Level.WARN, i18n.BorderLayoutPositionPopulated()));
        }
    }
    
    private int getMiddleRow() {
        int row = 0;
        if (grid.getRowCount() == 3) {
            row = 1;
        }
        return row;
    }

    private int getMiddleColumn() {
        int col = 0;
        if (grid.getColumnCount() == 3) {
            col = 1;
        }
        return col;
    }
    private void ensureRows() {
        if (grid.getRowCount() < 3) {
            grid.resizeRows(3);
            for (int i = 0; i < 2; i++) {
                if (grid.getColumnCount() > i) {
                    Widget widget = grid.getWidget(0, i);
                    if (widget != null) {
                        grid.remove(widget);
                        grid.setWidget(1, i, widget);
                    }
                }
            }
            grid.setSize(getWidth(), getHeight());
        }
    }
    
    private void ensureColumns() {
        if (grid.getColumnCount() < 3) {
            grid.resizeColumns(3);
            for (int i = 0; i < 2; i++) {
                if (grid.getRowCount() > i) {
                    Widget widget = grid.getWidget(i, 0);
                    if (widget != null) {
                        grid.remove(widget);
                        grid.setWidget(i, 1, widget);
                    }
                }
            }
            grid.setSize(getWidth(), getHeight());
        }
    }

    @Override
    public boolean remove(Widget child) {
        boolean removed = false;
        if (child instanceof FBFormItem) {
            Position pos = null;
            for (Map.Entry<Position, FBFormItem> entry : this.locations.entrySet()) {
                if (entry.getValue().equals(child)) {
                    pos = entry.getKey();
                    break;
                }
            }
            this.locations.remove(pos);
            removeItem((FBFormItem) child);
        } else {
            removed = super.remove(child);
        }
        return removed;
    }
    
}
