/*
 * 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 ro.nextreports.server.web.core;

import org.apache.commons.lang.StringUtils;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.markup.html.TransparentWebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.time.Duration;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.wicketstuff.push.AbstractPushEventHandler;
import org.wicketstuff.push.IPushEventContext;
import org.wicketstuff.push.IPushEventHandler;
import org.wicketstuff.push.IPushNode;
import org.wicketstuff.push.IPushService;
import org.wicketstuff.push.timer.TimerPushService;

import ro.nextreports.engine.util.DateUtil;
import ro.nextreports.server.domain.ReportResultEvent;
import ro.nextreports.server.report.ReportConstants;
import ro.nextreports.server.service.ReportListener;
import ro.nextreports.server.service.ReportService;
import ro.nextreports.server.service.SecurityService;
import ro.nextreports.server.service.StorageService;
import ro.nextreports.server.util.AnalysisUtil;
import ro.nextreports.server.web.NextServerSession;
import ro.nextreports.server.web.common.behavior.FontAwesomeBehavior;
import ro.nextreports.server.web.common.jgrowl.JGrowlAjaxBehavior;
import ro.nextreports.server.web.common.slidebar.SlidebarBehavior;
import ro.nextreports.server.web.common.util.PreferencesHelper;
import ro.nextreports.server.web.core.section.Section;
import ro.nextreports.server.web.core.section.SectionManager;
import ro.nextreports.server.web.language.LanguageManager;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

/**
 * @author Decebal Suiu
 */
public class HomePage extends BasePage {

	private static final long serialVersionUID = 1L;

    public static final String surveyUrl = "http://www.next-reports.com/survey1";

	private Label growlLabel;
    private JGrowlAjaxBehavior growlBehavior;

    private Panel sectionPanel;

	@SpringBean
	private SectionManager sectionManager;

	@SpringBean
	private ReportService reportService;

	@SpringBean
	private StorageService storageService;

    @SpringBean
    private SecurityService securityService;

    @SpringBean
    private PasswordEncoder passwordEncoder;

    public HomePage(PageParameters parameters) {
        super(parameters);        

    	// clear search context
    	NextServerSession.get().setSearchContext(null);

    	growlLabel = new Label("growl", "");
    	growlLabel.setOutputMarkupId(true);
        growlBehavior = new JGrowlAjaxBehavior();
    	growlLabel.add(growlBehavior);
    	add(growlLabel);

        // add slidebar
        addSlidebar();

        // add css
        /*
        add(new Behavior() {

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

                response.render(CssReferenceHeaderItem.forUrl("css/style.css"));
                response.render(CssReferenceHeaderItem.forUrl("css/table.css"));
                response.render(CssReferenceHeaderItem.forUrl("css/layout.css"));
            }

        });
        */
    }

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

        // obtain a reference to a Push service implementation
        final IPushService pushService = TimerPushService.get();       
        ((TimerPushService)pushService).setDefaultPollingInterval(Duration.seconds(storageService.getSettings().getPollingInterval()));
        

        // instantiate push event handler
        IPushEventHandler<Message> handler = new AbstractPushEventHandler<Message>() {

            private static final long serialVersionUID = 1L;

            @Override
            public void onEvent(AjaxRequestTarget target, Message event, IPushNode<Message> node, IPushEventContext<Message> context) {
                int messageType = event.isError() ?	JGrowlAjaxBehavior.ERROR_STICKY : JGrowlAjaxBehavior.INFO_STICKY;
                getSession().getFeedbackMessages().add(new FeedbackMessage(null, event.getText(), messageType));
                target.add(growlLabel);

                boolean autoOpen = storageService.getSettings().isAutoOpen();
                String reportsUrl = storageService.getSettings().getReportsUrl();
                if (autoOpen && StringUtils.contains(event.getText(), reportsUrl)) {
                    growlBehavior.setAfterOpenJavaScript(getJGrowlAfterOpenJavaScript());
                }
            }

        };

        // install push node into this panel
        final IPushNode<Message> pushNode = pushService.installNode(this, handler);

        // push report result
    	initPushReportResult(pushService, pushNode);

        // push survey
    	initPushSurvey(pushService, pushNode);
	}

    private void addSlidebar() {
        add(new SlidebarBehavior());
        add(new FontAwesomeBehavior());

//        add(new Label("currentUser", NextServerSession.get().getUsername()));
//        add(new Label("realName", NextServerSession.get().getRealName()));

//        Link<String> logoutLink = new Link<String>("logout") {
//
//            private static final long serialVersionUID = 1L;
//
//            @Override
//            public void onClick() {
//                NextServerSession.get().signOut();
//
//                if (CasUtil.isCasUsed()) {
//                    setResponsePage(new RedirectPage(CasUtil.getLogoutUrl()));
//                } else {
//                    setResponsePage(getApplication().getHomePage());
//                }
//            }
//
//        };
//        add(logoutLink);
//
//        AjaxLink<String> changePassword = new AjaxLink<String>("changePassword") {
//
//            private static final long serialVersionUID = 1L;
//
//            @Override
//            public void onClick(AjaxRequestTarget target) {
//                dialog.setTitle(getString("ChangePassword.change"));
//                dialog.setInitialWidth(350);
//                dialog.setUseInitialHeight(false);
//                dialog.setContent(new ChangePasswordPanel(dialog.getContentId()) {
//
//                    private static final long serialVersionUID = 1L;
//
//                    @Override
//                    public void onChange(AjaxRequestTarget target) {
//                        ModalWindow.closeCurrent(target);
//                        try {
//                            User loggedUser = securityService.getUserByName(NextServerSession.get().getUsername());
//                            loggedUser.setPassword(passwordEncoder.encodePassword(confirmPassword, null));
//                            storageService.modifyEntity(loggedUser);
//                        } catch (Exception e) {
//                            e.printStackTrace();
//                            add(new AlertBehavior(e.getMessage()));
//                            target.add(this);
//                        }
//                    }
//
//                    @Override
//                    public void onCancel(AjaxRequestTarget target) {
//                        ModalWindow.closeCurrent(target);
//                    }
//
//                });
//                dialog.show(target);
//            }
//        };
//        add(changePassword);
//
//        if (NextServerSession.get().isDemo()) {
//            changePassword.setEnabled(false);
//        }
//
//        ExternalLink surveyLink = new ExternalLink("survey", HomePage.surveyUrl) {
//
//            protected void onComponentTag(ComponentTag componentTag) {
//                super.onComponentTag(componentTag);
//                componentTag.put("target", "_blank");
//            }
//
//        };
//        add(surveyLink);

        List<String> sections = new ArrayList<String>();
        for (Section section : sectionManager.getSections()) {
        	if (section.isVisible()) {
        		sections.add(section.getId());
        	}
        }
        ListView<String> view = new ListView<String>("section", sections) {

            @Override
            protected void populateItem(final ListItem<String> item) {
//                AbstractLink link = new ExternalLink("link", "http://wwww.google.com");
                AjaxFallbackLink link = new AjaxFallbackLink("link") {

                    @Override
                    public void onClick(AjaxRequestTarget target) {
                        onSlidebarClick(target, item.getModelObject());
                    }

                };
                Section section = sectionManager.getSection(item.getModelObject());
                link.add(new TransparentWebMarkupContainer("icon").add(AttributeModifier.append("class", "fa-" + section.getIcon())));

                String key = "Section." + section.getTitle() + ".name";
                String title = new StringResourceModel(key, null).getString();
//                link.add(new Label("label", title).setRenderBodyOnly(true));
                link.add(new Label("label", title));

                item.setOutputMarkupId(true);
                item.add(link);

                item.add(new AttributeAppender("class", "active") {

                    @Override
                    public boolean isEnabled(Component component) {
                        return sectionManager.getSelectedSectionId().equals(item.getModelObject());
                    }

                }.setSeparator(" "));
            }

        };
        add(view);

        Section selectedSection = sectionManager.getSelectedSection();
        sectionPanel = selectedSection.createView("sectionPanel");
        sectionPanel.setOutputMarkupId(true);
        add(sectionPanel);
    }

    private void onSlidebarClick(AjaxRequestTarget target, String sectionId) {
        String oldSectionId = sectionManager.getSelectedSectionId();
        Section section = sectionManager.getSection(sectionId);
        sectionManager.setSelectedSectionId(sectionId);
        Panel newPanel = section.createView("sectionPanel");
        newPanel.setOutputMarkupId(true);
        sectionPanel.replaceWith(newPanel);
        target.add(newPanel);
        sectionPanel = newPanel;

        // close slidebar
        target.appendJavaScript("closeSlidebar();");

        // refresh active class
        ListView<String> view = (ListView<String>) get("section");
        Iterator<Component> it= view.iterator();
        while (it.hasNext()) {
            ListItem<String> item = (ListItem<String>) it.next();
            String itemId = item.getModelObject();
            if (itemId.equals(sectionId) || itemId.equals(oldSectionId)) {
                target.add(item);
            }
        }
    }

    private void initPushReportResult(final IPushService pushService, final IPushNode<Message> pushNode) {
        reportService.addReportListener(new ReportListener() {

            @Override
            public void onFinishRun(ReportResultEvent result) {
                if (pushService.isConnected(pushNode)) {
                    pushService.publish(pushNode, createReportResultMessage(result));
                }
            }

        });
	}

	private Message createReportResultMessage(ReportResultEvent event) {
		StringBuilder sb = new StringBuilder();

		Locale locale = LanguageManager.getInstance().getLocale(storageService.getSettings().getLanguage());
		ResourceBundle bundle = ResourceBundle.getBundle("ro.nextreports.server.web.NextServerApplication", locale);
		String s = bundle.getString("ActionContributor.Run.finish");
		String message = MessageFormat.format(s, event.getReportName());

		sb.append(message);
		sb.append("<br>");
        boolean error = false;
		if ("".equals(event.getReportUrl())) {
			sb.append(event.getResultMessage());
            error = true;
		} else if (ReportConstants.ETL_FORMAT.equals(event.getReportUrl())) {
			sb.append(event.getResultMessage());
		} else if (AnalysisUtil.FREEZE_ACTION.equals(event.getReportUrl())) {
			if (event.getResultMessage().startsWith(AnalysisUtil.FREEZE_FAILED)) {
				error = true;
				s = bundle.getString("Analysis.freezed.failed");
				message = MessageFormat.format(s, event.getReportName());
				sb = new StringBuilder(message + " " + event.getResultMessage().substring(AnalysisUtil.FREEZE_FAILED.length()));
			} else {
				sb = new StringBuilder(event.getResultMessage());
			}
		} else if (AnalysisUtil.ANY_ACTION.equals(event.getReportUrl())) {
			if (event.getResultMessage().startsWith(AnalysisUtil.ANY_ACTION_FAILED)) {
				error = true;
				message = bundle.getString("Analysis.error");
				sb = new StringBuilder(message + " " + event.getResultMessage().substring(AnalysisUtil.ANY_ACTION_FAILED.length()));
			}
		} else if (!event.getReportUrl().endsWith("/report")) {
			// indicator and alarm schedule alerts do not have a resulting report (url ends with /report)
			sb.append("<a href=\"").
			   append(event.getReportUrl()).
			   append("\" target=\"_blank\">").
			   append(bundle.getString("ActionContributor.Run.result")).
			   append("</a>");
		} else {
			sb.append(event.getResultMessage());
		}

		return new Message(sb.toString(), error);
	}

    private String getJGrowlAfterOpenJavaScript() {
        // programatically click on link
        // http://stackoverflow.com/questions/1694595/can-i-call-jquery-click-to-follow-an-a-link-if-i-havent-bound-an-event-hand
        StringBuilder javaScript = new StringBuilder();
        javaScript.append("function(e,m,o) {");
        javaScript.append("var link = e.find('a');");
        javaScript.append("if (link.size() > 0) {");
//        javaScript.append("window.open(link.attr('href'), '_blank');");
        javaScript.append("link[0].click();");
        javaScript.append("}");
        javaScript.append("}");

        return javaScript.toString();
    }

	private void initPushSurvey(IPushService pushService, IPushNode<Message> pushNode) {
		int[] surveyDays = new int[]{30, 180}; // show survey at 30, 180 days
        Map<String, String> preferences = NextServerSession.get().getPreferences();
        int surveyDay = PreferencesHelper.getInt("survey.day", preferences);
        int surveyTestDay = surveyDay;
        if (surveyDay == 0) {
        	surveyTestDay = surveyDays[0];
        } else {
        	for (int i=0, size=surveyDays.length; i<size; i++) {
        		if (surveyDay == surveyDays[i]) {
        			if (i == size-1) {
        				return;
        			} else {
        				surveyTestDay = surveyDays[i+1];
        			}
        		}
        	}
        }
        Date firstUsageDate = PreferencesHelper.getDate("firstUsage.date", preferences);
        if (firstUsageDate == null) {
        	firstUsageDate = new Date();
        	preferences.put("firstUsage.date", PreferencesHelper.sdf.format(firstUsageDate));
        }
        int array[] = DateUtil.getElapsedTime(firstUsageDate, new Date());
		boolean showSurvey = (array != null) && (array[0] >= surveyTestDay);
		if (showSurvey) {
			if (pushService.isConnected(pushNode)) {
				pushService.publish(pushNode, createSurveyMessage());
				preferences.put("survey.day", String.valueOf(surveyTestDay));
			}
		}
        NextServerSession.get().setPreferences(preferences);
    }

    private Message createSurveyMessage() {
        StringBuilder sb = new StringBuilder();
        Locale locale = LanguageManager.getInstance().getLocale(storageService.getSettings().getLanguage());
        ResourceBundle bundle = ResourceBundle.getBundle("ro.nextreports.server.web.NextServerApplication", locale);
        String s = bundle.getString("survey.message");
        sb.append(s);
        sb.append("<br>").
            append("<a class=\"go\" href=\"").
            append(surveyUrl).
            append("\" target=\"_blank\">").
            append(bundle.getString("survey.go")).
            append("</a>");

        return new Message(sb.toString(), false);
    }

    private class Message  {

        private String text;
        private boolean error;

        public Message(String text, boolean error) {
            this.text = text;
            this.error = error;
        }

        public String getText() {
            return text;
        }

        public boolean isError() {
            return error;
        }

    }

}