package org.ovirt.engine.ui.webadmin.widget.table.column;

import java.util.ArrayList;
import java.util.List;

import org.ovirt.engine.ui.common.widget.table.column.SafeHtmlColumn;
import org.ovirt.engine.ui.webadmin.ApplicationTemplates;
import org.ovirt.engine.ui.webadmin.gin.ClientGinjectorProvider;

import com.google.gwt.cell.client.ActionCell;
import com.google.gwt.cell.client.ActionCell.Delegate;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.CompositeCell;
import com.google.gwt.cell.client.HasCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.IdentityColumn;

/**
 * A Column that shows one Text Cell, and one or more Action Cells
 *
 * @param <T>
 *            The Column Type
 */
public class ActionColumn<T> extends IdentityColumn<T> {

    /**
     * The delegate that is used when the Action Cell is rendered/clicked.<BR>
     * An Implementation must provide either a Button Text or a Button Image for the Action Cell.<BR>
     * {@link #showAction(Object)} can be overwritten to decide whether to show the action button.
     *
     * @param <C>
     *            The Action Column Type
     */
    public static abstract class ActionCellDelegate<C> implements Delegate<C> {

        @Override
        public final void execute(C object) {
            // not used
        };

        public abstract void execute(C object, NativeEvent event);

        /**
         * Gets the Button Image<BR>
         * Default implementation returns <code>null</code>
         *
         * @param object
         *            the object which is the target for this Action Cell
         */
        public ImageResource getButtonImage(C object) {
            return null;
        }

        /**
         * Gets the Button Text<BR>
         * Default implementation returns <code>null</code>
         *
         * @param object
         *            the object which is the target for this Action Cell
         */
        public String getButtonText(C object) {
            return null;
        }

        /**
         * Whether to show the Button for the specified object
         *
         * @param object
         *            the object which is the target for this Action Cell
         */
        public boolean showAction(C object) {
            return true;
        }
    }

    /**
     * Gets string values for text cells
     *
     * @param <C>
     *            The Column Type
     */
    public interface ValueGetter<C> {

        /**
         * Get String the value for the Text Cell
         */
        public SafeHtml getValue(C object);
    }

    /**
     * An Action Cell the is left floated, and uses an {@link ActionCellDelegate}
     *
     * @param <C>
     *            The Action Cell Type
     */
    private static class FloatingActionCell<C> extends ActionCell<C> {
        private final ActionCellDelegate<C> delegate;
        private final ApplicationTemplates templates = ClientGinjectorProvider.instance().getApplicationTemplates();

        public FloatingActionCell(ActionCellDelegate<C> delegate) {
            super("", delegate); //$NON-NLS-1$
            this.delegate = delegate;
        }

        @Override
        public void render(Context context, C value, SafeHtmlBuilder sb) {
            // show cell?
            if (delegate.showAction(value)) {
                String buttonText = delegate.getButtonText(value);
                ImageResource buttonImage = delegate.getButtonImage(value);
                // use text or image
                if (buttonText != null) {
                    sb.append(templates.actionButtonText(buttonText));
                } else if (buttonImage != null) {
                    sb.append(templates.actionButtonImage(buttonImage.getURL()));
                } else {
                    throw new IllegalArgumentException("Either getButtonText() or getButtonImage() must return non-null values"); //$NON-NLS-1$
                }
            }
        };

        @Override
        protected void onEnterKeyDown(Cell.Context context,
                Element parent,
                C value,
                NativeEvent event,
                ValueUpdater<C> valueUpdater) {
            delegate.execute(value, event);
        }
    }

    /**
     * Create the Column Cells from a single delegate
     */
    private static <S> List<HasCell<S, ?>> createCells(ValueGetter<S> getter, ActionCellDelegate<S> delegate) {
        List<ActionCellDelegate<S>> delegates = new ArrayList<ActionCellDelegate<S>>();
        delegates.add(delegate);
        return createCells(getter, delegates);
    }

    /**
     * Create the Column Cells from a a list of delegates
     */
    private static <S> List<HasCell<S, ?>> createCells(final ValueGetter<S> getter,
            List<ActionCellDelegate<S>> delegates) {
        List<HasCell<S, ?>> cells = new ArrayList<HasCell<S, ?>>();

        // text cell
        SafeHtmlColumn<S> textColumn = new SafeHtmlColumn<S>() {
            @Override
            public SafeHtml getValue(S object) {
                return getter.getValue(object);
            }
        };
        cells.add(textColumn);

        // action cells
        for (ActionCellDelegate<S> delegate : delegates) {
            Column<S, S> actionColumn = new IdentityColumn<S>(new FloatingActionCell<S>(delegate));
            cells.add(actionColumn);
        }

        return cells;
    }

    /**
     * Create an Action Column with the specified getter and delegate
     *
     * @param getter
     *            The getter for the Text Cell
     * @param delegate
     *            The delegate for the Action Cell
     */
    public ActionColumn(ValueGetter<T> getter, ActionCellDelegate<T> delegate) {
        super(new CompositeCell<T>((createCells(getter, delegate))));
    }

    /**
     * Create an Action Column with the specified getter and list of delegates
     *
     * @param getter
     *            The getter for the Text Cell
     * @param delegate
     *            The list of delegates for the Action Cells
     */
    public ActionColumn(ValueGetter<T> getter, List<ActionCellDelegate<T>> delegates) {
        super(new CompositeCell<T>((createCells(getter, delegates))));
    }
}
