/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
 *
 * 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 com.android.ide.common.layout;

import static com.android.SdkConstants.FQCN_TABLE_ROW;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.api.DropFeedback;
import com.android.ide.common.api.IClientRulesEngine;
import com.android.ide.common.api.IMenuCallback;
import com.android.ide.common.api.INode;
import com.android.ide.common.api.INodeHandler;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.InsertType;
import com.android.ide.common.api.RuleAction;
import com.android.ide.common.api.SegmentType;

import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * An {@link IViewRule} for android.widget.TableLayout.
 */
public class TableLayoutRule extends LinearLayoutRule {
    // A table is a linear layout, but with a few differences:
    // the default is vertical, not horizontal
    // The fill of all children should be wrap_content

    private static final String ACTION_ADD_ROW = "_addrow"; //$NON-NLS-1$
    private static final String ACTION_REMOVE_ROW = "_removerow"; //$NON-NLS-1$
    private static final URL ICON_ADD_ROW =
        TableLayoutRule.class.getResource("addrow.png"); //$NON-NLS-1$
    private static final URL ICON_REMOVE_ROW =
        TableLayoutRule.class.getResource("removerow.png"); //$NON-NLS-1$

    @Override
    protected boolean isVertical(INode node) {
        // Tables are always vertical
        return true;
    }

    @Override
    protected boolean supportsOrientation() {
        return false;
    }

    @Override
    public void onChildInserted(@NonNull INode child, @NonNull INode parent,
            @NonNull InsertType insertType) {
        // Overridden to inhibit the setting of layout_width/layout_height since
        // it should always be match_parent
    }

    /**
     * Add an explicit "Add Row" action to the context menu
     */
    @Override
    public void addContextMenuActions(@NonNull List<RuleAction> actions,
            final @NonNull INode selectedNode) {
        super.addContextMenuActions(actions, selectedNode);

        IMenuCallback addTab = new IMenuCallback() {
            @Override
            public void action(
                    @NonNull RuleAction action,
                    @NonNull List<? extends INode> selectedNodes,
                    final @Nullable String valueId,
                    @Nullable Boolean newValue) {
                final INode node = selectedNode;
                INode newRow = node.appendChild(FQCN_TABLE_ROW);
                mRulesEngine.select(Collections.singletonList(newRow));
            }
        };
        actions.add(RuleAction.createAction("_addrow", "Add Row", addTab, null, 5, false)); //$NON-NLS-1$
    }

    @Override
    public void addLayoutActions(
            @NonNull List<RuleAction> actions,
            final @NonNull INode parentNode,
            final @NonNull List<? extends INode> children) {
        super.addLayoutActions(actions, parentNode, children);
        addTableLayoutActions(mRulesEngine, actions, parentNode, children);
    }

    /**
     * Adds layout actions to add and remove toolbar items
     */
    static void addTableLayoutActions(final IClientRulesEngine rulesEngine,
            List<RuleAction> actions, final INode parentNode,
            final List<? extends INode> children) {
        IMenuCallback actionCallback = new IMenuCallback() {
            @Override
            public void action(
                    final @NonNull RuleAction action,
                    @NonNull List<? extends INode> selectedNodes,
                    final @Nullable String valueId,
                    final @Nullable Boolean newValue) {
                parentNode.editXml("Add/Remove Table Row", new INodeHandler() {
                    @Override
                    public void handle(@NonNull INode n) {
                        if (action.getId().equals(ACTION_ADD_ROW)) {
                            // Determine the index of the selection, if any; if there is
                            // a selection, insert the row before the current row, otherwise
                            // append it to the table.
                            int index = -1;
                            INode[] rows = parentNode.getChildren();
                            if (children != null) {
                                findTableIndex:
                                for (INode child : children) {
                                    // Find direct child of table layout
                                    while (child != null && child.getParent() != parentNode) {
                                        child = child.getParent();
                                    }
                                    if (child != null) {
                                        // Compute index of direct child of table layout
                                        for (int i = 0; i < rows.length; i++) {
                                            if (rows[i] == child) {
                                                index = i;
                                                break findTableIndex;
                                            }
                                        }
                                    }
                                }
                            }
                            INode newRow;
                            if (index == -1) {
                                newRow = parentNode.appendChild(FQCN_TABLE_ROW);
                            } else {
                                newRow = parentNode.insertChildAt(FQCN_TABLE_ROW, index);
                            }
                            rulesEngine.select(Collections.singletonList(newRow));
                        } else if (action.getId().equals(ACTION_REMOVE_ROW)) {
                            // Find the direct children of the TableLayout to delete;
                            // this is necessary since TableRow might also use
                            // this implementation, so the parentNode is the true
                            // TableLayout but the children might be grand children.
                            Set<INode> targets = new HashSet<INode>();
                            for (INode child : children) {
                                while (child != null && child.getParent() != parentNode) {
                                    child = child.getParent();
                                }
                                if (child != null) {
                                    targets.add(child);
                                }
                            }
                            for (INode target : targets) {
                                parentNode.removeChild(target);
                            }
                        }
                    }
                });
            }
        };

        // Add Row
        actions.add(RuleAction.createSeparator(150));
        actions.add(RuleAction.createAction(ACTION_ADD_ROW, "Add Table Row", actionCallback,
                ICON_ADD_ROW, 160, false));

        // Remove Row (if something is selected)
        if (children != null && children.size() > 0) {
            actions.add(RuleAction.createAction(ACTION_REMOVE_ROW, "Remove Table Row",
                    actionCallback, ICON_REMOVE_ROW, 170, false));
        }
    }

    @Override
    public void onCreate(@NonNull INode node, @NonNull INode parent,
            @NonNull InsertType insertType) {
        super.onCreate(node, parent, insertType);

        if (insertType.isCreate()) {
            // Start the table with 4 rows
            for (int i = 0; i < 4; i++) {
                node.appendChild(FQCN_TABLE_ROW);
            }
        }
    }

    @Override
    public DropFeedback onResizeBegin(@NonNull INode child, @NonNull INode parent,
            @Nullable SegmentType horizontalEdge, @Nullable SegmentType verticalEdge,
            @Nullable Object childView, @Nullable Object parentView) {
        // Children of a table layout cannot set their widths (it is controlled by column
        // settings on the table). They can set their heights (though for TableRow, the
        // height is always wrap_content).
        if (horizontalEdge == null) { // Widths are edited by vertical edges.
            // The user is not editing a vertical height so don't allow resizing at all
            return null;
        }
        if (child.getFqcn().equals(FQCN_TABLE_ROW)) {
            // TableRows are always WRAP_CONTENT
            return null;
        }

        // Allow resizing heights only
        return super.onResizeBegin(child, parent, horizontalEdge, null /*verticalEdge*/,
                childView, parentView);
    }
}
