/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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.queplix.core.client.common.ui;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.EventPreview;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.queplix.core.client.common.StringUtil;
import com.queplix.core.client.common.event.Event;
import com.queplix.core.client.common.event.EventListener;


/**
 * Modal dialog, designed to display error messages with an optional stack trace
 *
 * @author Vasily Mikhailitchenko
 * @since 13 Dec 2006
 */
public class DetailedModalDialog extends ButtonSet implements EventListener {

    // -------------------- public events ------------------------
    public static interface Events {
        Event CLOSE = new Event();
        Event DETAILS = new Event();
    }
    // ----------------- end of public events --------------------

    public static final char ESC_CODE = '\u001b';

    private static final int DEFAULT_WIDTH = 712;
    private static final int BUTTONS_AND_IMAGE_WIDTH = 128;

    private static final String ERROR_ICON_PATH = "common/error_icon.gif";

    private static final String CLOSE_TEXT = "Close";
    private static final String DETAILS_TEXT_OPEN = "Details <<";
    private static final String DETAILS_TEXT_CLOSE = "Details >>";

    private DialogBox dialog;
    private VerticalPanel vp;
    private Image errorIcon;
    private Label errorMessage;
    private TextArea stackTraceBox;
    private VerticalPanel buttonsPanel;
    private Button close;
    private Button details;

    private EventPreview escapePreview;

    private int normalWidth;
    private int expandedWidth;

    public DetailedModalDialog(String message, String stackTrace) {
        super(true);
        dialog = new DialogBox();
        dialog.setText("Error");
        dialog.setTitle("Error");

        errorIcon = new Image(ERROR_ICON_PATH);
        errorMessage = new Label(message);
        errorMessage.setWordWrap(true);

        stackTraceBox = new TextArea();
        stackTraceBox.setText(stackTrace);
        stackTraceBox.setWidth("100%");
        stackTraceBox.setCharacterWidth(70);
        stackTraceBox.setVisibleLines(20);

        close = new Button(CLOSE_TEXT);
        details = new Button(DETAILS_TEXT_CLOSE);
        addButton(Events.CLOSE, close);
        addButton(Events.DETAILS, details);
        buttonsPanel = new VerticalPanel();
        buttonsPanel.add(close);
        buttonsPanel.add(details);
        buttonsPanel.setSpacing(2);

        HorizontalPanel hp = new HorizontalPanel();
        hp.add(errorIcon);
        hp.add(errorMessage);
        hp.add(buttonsPanel);
        hp.setCellHorizontalAlignment(errorIcon, HorizontalPanel.ALIGN_LEFT);
        hp.setCellVerticalAlignment(errorIcon, VerticalPanel.ALIGN_MIDDLE);
        hp.setCellHorizontalAlignment(errorMessage,
                HorizontalPanel.ALIGN_CENTER);
        hp.setCellVerticalAlignment(errorMessage, VerticalPanel.ALIGN_MIDDLE);
        hp.setCellHorizontalAlignment(buttonsPanel,
                HorizontalPanel.ALIGN_RIGHT);
        hp.setSpacing(2);
        hp.setCellWidth(errorMessage, "95%");
        hp.setWidth("100%");

        vp = new VerticalPanel();
        vp.add(hp);
        vp.add(stackTraceBox);
        vp.setVerticalAlignment(VerticalPanel.ALIGN_TOP);
        vp.setSpacing(2);

        dialog.setWidget(vp);
        setExpanded(false);

        errorMessage.addStyleName("ocp_caption");
        close.addStyleName("styled_button");
        details.addStyleName("styled_button");
        dialog.addStyleName("ocp_border");
        dialog.addStyleName("ocp_area");
        getEventSource().addEventListener(this);
        escapePreview = new EventPreview() {
            public boolean onEventPreview(
                    com.google.gwt.user.client.Event event) {
                if(DOM.eventGetType(event) == com.google.gwt.user.client.Event
                        .ONKEYUP) {
                    char keyCode = (char) DOM.eventGetKeyCode(event);
                    if(keyCode == ESC_CODE) {
                        hide();
                        return false;
                    }
                }
                return true;
            }
        };
    }

    public void show() {
        dialog.show();
        normalWidth = errorMessage.getOffsetWidth() + BUTTONS_AND_IMAGE_WIDTH;
        normalWidth = Math.max(DEFAULT_WIDTH / 2, normalWidth);
        expandedWidth = Math.max(DEFAULT_WIDTH, normalWidth);
        vp.setWidth(StringUtil.pixelToSize(normalWidth));
        dialog.setWidth(StringUtil.pixelToSize(normalWidth));
        DOM.addEventPreview(escapePreview);
        reposition();
    }

    private void reposition() {
        int dialogHeight = dialog.getOffsetHeight();
        int dialogWidth = dialog.getOffsetWidth();
        vp.setVisible(false);
        dialog.setPopupPosition(Window.getClientWidth() / 2 - dialogWidth / 2,
                Window.getClientHeight() / 2 - dialogHeight / 2);
        vp.setVisible(true);
    }

    public void hide() {
        DOM.removeEventPreview(escapePreview);
        dialog.hide();
    }

    public void setExpanded(boolean isExpanded) {
        stackTraceBox.setVisible(isExpanded);
        details.setText(isExpanded ? DETAILS_TEXT_OPEN:DETAILS_TEXT_CLOSE);
        dialog.setWidth(StringUtil.pixelToSize(
                isExpanded ? expandedWidth:normalWidth));
        vp.setWidth(StringUtil.pixelToSize(
                isExpanded ? expandedWidth:normalWidth));
        reposition();
    }

    public void toggleExpanded() {
        setExpanded(!stackTraceBox.isVisible());
    }

    public void onEvent(Event event, Widget sender) {
        if(Events.CLOSE.equals(event)) {
            hide();
        } else if(Events.DETAILS.equals(event)) {
            toggleExpanded();
        }
    }
}
