/*
 * Copyright 2000-2016 Vaadin Ltd.
 *
 * 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 com.vaadin.tests.components.table;

import java.lang.reflect.InvocationTargetException;

import com.vaadin.event.ListenerMethod.MethodException;
import com.vaadin.server.ErrorEvent;
import com.vaadin.server.ErrorHandler;
import com.vaadin.server.ServerRpcManager.RpcInvocationException;
import com.vaadin.server.VaadinSession;
import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.Notification;
import com.vaadin.v7.data.Item;
import com.vaadin.v7.data.Property;
import com.vaadin.v7.data.util.IndexedContainer;
import com.vaadin.v7.ui.Table;
import com.vaadin.v7.ui.Table.CacheUpdateException;
import com.vaadin.v7.ui.Table.ColumnGenerator;

public class TableWithBrokenGeneratorAndContainer extends TestBase {

    private CheckBox brokenContainer = new CheckBox("Broken container");
    private CheckBox brokenGenerator = new CheckBox("Broken generator");
    private CheckBox clearTableOnError = new CheckBox("Clear Table on Error");

    /**
     * Container which throws an exception on every fifth call to
     * {@link #getContainerProperty(Object, Object)}.
     *
     * @author Vaadin Ltd
     * @version @VERSION@
     * @since 7.0
     *
     */
    public class BrokenContainer extends IndexedContainer {
        private int counter = 0;

        public BrokenContainer() {
            super();
        }

        @Override
        public Property getContainerProperty(Object itemId, Object propertyId) {
            if (counter++ % 5 == 0
                    && Boolean.TRUE.equals(brokenContainer.getValue())) {
                throw new RuntimeException(getClass().getSimpleName()
                        + " cannot fetch the property for " + itemId + "/"
                        + propertyId + " right now");
            }
            return super.getContainerProperty(itemId, propertyId);
        }
    }

    public class BrokenColumnGenerator implements ColumnGenerator {
        private int brokenInterval;
        private int counter = 0;

        public BrokenColumnGenerator(int brokenInterval) {
            this.brokenInterval = brokenInterval;
        }

        @Override
        public Object generateCell(Table source, Object itemId,
                Object columnId) {
            if (counter++ % brokenInterval == 0
                    && Boolean.TRUE.equals(brokenGenerator.getValue())) {
                throw new IllegalArgumentException(
                        "Broken generator for " + itemId + "/" + columnId);
            } else {
                return "Generated " + itemId + "/" + columnId;
            }
        }

    }

    @Override
    protected void setup() {
        clearTableOnError.addValueChangeListener(event -> {
            Boolean value = clearTableOnError.getValue();
            setErrorHandler(value != null ? value : false);
        });
        final Table table = new Table("Semi-broken table");
        table.setContainerDataSource(createBrokenContainer(10, 4));
        table.addGeneratedColumn("Gen", new BrokenColumnGenerator(4));
        table.setPageLength(20);

        Button refreshTableCache = new Button("Refresh table cache",
                new Button.ClickListener() {

                    @Override
                    public void buttonClick(ClickEvent event) {
                        table.markAsDirty();
                        table.refreshRowCache();
                    }
                });
        addComponent(refreshTableCache);
        addComponent(brokenContainer);
        addComponent(brokenGenerator);
        addComponent(clearTableOnError);
        addComponent(table);
    }

    protected void setErrorHandler(boolean enabled) {
        if (enabled) {
            VaadinSession.getCurrent().setErrorHandler(new ErrorHandler() {

                @Override
                public void error(ErrorEvent event) {
                    Throwable t = event.getThrowable();
                    if (t instanceof RpcInvocationException) {
                        t = t.getCause();
                        if (t instanceof InvocationTargetException) {
                            t = t.getCause();
                            if (t instanceof MethodException) {
                                t = t.getCause();
                                if (t instanceof CacheUpdateException) {
                                    Table table = ((CacheUpdateException) t)
                                            .getTable();
                                    table.removeAllItems();
                                    Notification.show(
                                            "Problem updating table. Please try again later",
                                            Notification.Type.ERROR_MESSAGE);
                                }
                            }
                        }
                    }
                }
            });
        } else {
            VaadinSession.getCurrent().setErrorHandler(this);
        }
    }

    private BrokenContainer createBrokenContainer(int rows, int cols) {
        BrokenContainer container = new BrokenContainer();
        for (int j = 1; j <= cols; j++) {
            container.addContainerProperty("prop" + j, String.class, null);
        }
        for (int i = 1; i <= rows; i++) {
            Item item = container.addItem("item" + i);
            for (int j = 1; j <= cols; j++) {
                item.getItemProperty("prop" + j)
                        .setValue("item" + i + "/prop" + j);
            }
        }
        return container;
    }

    @Override
    protected Integer getTicketNumber() {
        return 10312;
    }

    @Override
    protected String getDescription() {
        return "A Table should not show 'Internal Error' just because a column generator or container throws an exception during filling of the cache";
    }

}
