/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.apache.isis.viewer.wicket.ui.components.bookmarkedpages;

import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.head.CssHeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.AbstractLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.CssResourceReference;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.util.string.Strings;

import org.apache.isis.core.metamodel.adapter.oid.RootOid;
import org.apache.isis.core.metamodel.spec.ObjectSpecId;
import org.apache.isis.core.metamodel.spec.ObjectSpecification;
import org.apache.isis.core.runtime.persistence.ObjectNotFoundException;
import org.apache.isis.viewer.wicket.model.models.BookmarkTreeNode;
import org.apache.isis.viewer.wicket.model.models.BookmarkedPagesModel;
import org.apache.isis.viewer.wicket.model.models.ImageResourceCache;
import org.apache.isis.viewer.wicket.model.models.PageType;
import org.apache.isis.viewer.wicket.ui.pages.PageClassRegistry;
import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
import org.apache.isis.viewer.wicket.ui.util.Links;

public class BookmarkedPagesPanel extends PanelAbstract<BookmarkedPagesModel> {

    private static final long serialVersionUID = 1L;

    private static final String ID_BOOKMARK_LIST = "bookmarkList";
    private static final String ID_BOOKMARKS_HELP_TEXT = "helpText";
    private static final String ID_BOOKMARKED_PAGE_LINK = "bookmarkedPageLink";
    private static final String ID_CLEAR_BOOKMARK_LINK = "clearBookmarkLink";
    private static final String ID_BOOKMARKED_PAGE_ITEM = "bookmarkedPageItem";
    private static final String ID_BOOKMARKED_PAGE_TITLE = "bookmarkedPageTitle";

    private static final String ID_BOOKMARKED_PAGE_ICON = "bookmarkedPageImage";

    private static final String CLEAR_BOOKMARKS = "clearBookmarks";


    /**
     * A configuration setting which value determines whether the bookmarks should be available in the header
     */
    public static final String SHOW_BOOKMARKS_KEY = "isis.viewer.wicket.bookmarkedPages.showChooser";
    public static final boolean SHOW_BOOKMARKS_DEFAULT = true;

    private static final JavaScriptResourceReference SLIDE_PANEL_JS = new JavaScriptResourceReference(BookmarkedPagesPanel.class, "slide-panel.js");

    @com.google.inject.Inject
    private PageClassRegistry pageClassRegistry;

    public BookmarkedPagesPanel(final String id, final BookmarkedPagesModel bookmarkedPagesModel) {
        super(id, bookmarkedPagesModel);
        buildGui();
    }

    @Override
    public void renderHead(IHeaderResponse response) {
        super.renderHead(response);

        response.render(OnDomReadyHeaderItem.forScript("$('.bookmarkRibbon').height($('.navbar.navbar-fixed-top').height()-5);"));
    }

    private void buildGui() {

        final BookmarkedPagesModel bookmarkedPagesModel = getModel();

        Component helpText = addHelpText(bookmarkedPagesModel);
        addOrReplace(helpText);

        final WebMarkupContainer container = new WebMarkupContainer(ID_BOOKMARK_LIST) {
            private static final long serialVersionUID = 1L;
            @Override
            public void renderHead(IHeaderResponse response) {
                response.render(CssHeaderItem.forReference(new CssResourceReference(BookmarkedPagesPanel.class, "BookmarkedPagesPanel.css")));
                response.render(JavaScriptReferenceHeaderItem.forReference(SLIDE_PANEL_JS));
            }
        };
        // allow to be updated by AjaxLink
        container.setOutputMarkupId(true);
        add(container);

        final AjaxLink<Void> clearAllBookmarksLink = new AjaxLink<Void>(CLEAR_BOOKMARKS){

            private static final long serialVersionUID = 1L;

            @Override
            public void onClick(AjaxRequestTarget target) {
                BookmarkedPagesPanel.this.getModel().clear();
                setEnabled(false);
                target.add(container, this);
            }
        };
        clearAllBookmarksLink.setOutputMarkupId(true);
        add(clearAllBookmarksLink);
        clearAllBookmarksLink.setOutputMarkupId(true);

        if(getModel().isEmpty()) {
            clearAllBookmarksLink.setVisible(false);
        }


        final ListView<BookmarkTreeNode> listView = new ListView<BookmarkTreeNode>(ID_BOOKMARKED_PAGE_ITEM, bookmarkedPagesModel) {

            private static final long serialVersionUID = 1L;

            @Override
            protected void populateItem(ListItem<BookmarkTreeNode> item) {
                final BookmarkTreeNode node = item.getModelObject();
                try {
                    final PageType pageType = node.getPageType();
                    final Class<? extends Page> pageClass = pageClassRegistry.getPageClass(pageType);

                    final AjaxLink<Object> clearBookmarkLink = new AjaxLink<Object>(ID_CLEAR_BOOKMARK_LINK) {

                        private static final long serialVersionUID = 1L;

                        @Override
                        public void onClick(AjaxRequestTarget target) {
                            bookmarkedPagesModel.remove(node);
                            if(bookmarkedPagesModel.isEmpty()) {
                                permanentlyHide(CLEAR_BOOKMARKS);
                            }
                            target.add(container, clearAllBookmarksLink);
                        }

                    };
                    if(node.getDepth() == 0) {
                        clearBookmarkLink.add(new CssClassAppender("clearBookmark"));
                    } else {
                        clearBookmarkLink.setEnabled(true);
                    }
                    item.add(clearBookmarkLink);

                    PageParameters pageParameters = node.getPageParameters();
                    final AbstractLink link = Links.newBookmarkablePageLink(ID_BOOKMARKED_PAGE_LINK, pageParameters, pageClass);

                    ObjectSpecification objectSpec = null;
                    RootOid oid = node.getOidNoVer();
                    if(oid != null) {
                        ObjectSpecId objectSpecId = oid.getObjectSpecId();
                        objectSpec = getSpecificationLoader().lookupBySpecId(objectSpecId);
                    }
                    final ResourceReference imageResource = imageCache.resourceReferenceForSpec(objectSpec);
                    final Image image = new Image(ID_BOOKMARKED_PAGE_ICON, imageResource) {
                        private static final long serialVersionUID = 1L;
                        @Override
                        protected boolean shouldAddAntiCacheParameter() {
                            return false;
                        }
                    };
                    link.addOrReplace(image);

                    String title = node.getTitle();
                    final Label label = new Label(ID_BOOKMARKED_PAGE_TITLE, title);
                    link.add(label);
                    item.add(link);
                    if(bookmarkedPagesModel.isCurrent(pageParameters)) {
                        item.add(new CssClassAppender("disabled"));
                    }
                    item.add(new CssClassAppender("bookmarkDepth" + node.getDepth()));
                } catch(ObjectNotFoundException ex) {
                    // ignore
                    // this is a partial fix for an infinite redirect loop.
                    // should be a bit smarter here, though; see ISIS-596.
                }

            }
        };
        container.add(listView);
    }

    protected Component addHelpText(final BookmarkedPagesModel bookmarkedPagesModel) {

        IModel<String> helpTextModel = new AbstractReadOnlyModel<String>() {
            private static final long serialVersionUID = -2445813533787596379L;

            @Override
            public String getObject() {
                return bookmarkedPagesModel.isEmpty() ? "You have no bookmarks!" : "";
            }
        };

        Label helpText = new Label(ID_BOOKMARKS_HELP_TEXT, helpTextModel) {
            private static final long serialVersionUID = -8364098044839077580L;

            @Override
            protected void onConfigure() {
                super.onConfigure();

                setVisible(!Strings.isEmpty(getDefaultModelObjectAsString()));
            }
        };
        helpText.setOutputMarkupPlaceholderTag(true);
        return helpText;
    }

    // ///////////////////////////////////////////////
    // Dependency Injection
    // ///////////////////////////////////////////////

    @com.google.inject.Inject
    private ImageResourceCache imageCache;

}
