/*
 * 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.controls.chart;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.LoadListener;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.queplix.core.client.app.vo.DateFieldData;
import com.queplix.core.client.app.vo.DateFieldMeta;
import com.queplix.core.client.app.vo.chart.ChartDataItem;
import com.queplix.core.client.app.vo.chart.ChartMeta;
import com.queplix.core.client.app.vo.chart.ChartType;
import com.queplix.core.client.common.ui.ImageMap;
import com.queplix.core.client.common.ui.ImageMapArea;
import com.queplix.core.client.common.ui.MappedImage;
import com.queplix.core.client.controls.QFormElement;
import com.queplix.core.client.controls.QFormLayoutElement;
import com.queplix.core.client.controls.datefield.QDateField;
import com.queplix.core.client.i18n.I18N;

/**
 * Chart view implementation
 * @author Michael Trofimov
 */
public class QChartViewImpl extends QFormLayoutElement implements QChartView {

    private QChartModel model;

    private ViewListener listener;

    private VerticalPanel chartPanel;
    private MappedImage chartImage;
    private ImageMap chartMap;

    private QDateField dateField;
    private CheckBox includeClosedField;

    // Contains RadioButton associated with its type name, @see ChartType#getName
    private Map typeButtons/*<RadioButton, String>*/
            = new HashMap/*<RadioButton, String>*/();

    private RadioButton currentTypeButton;

    // Provides uniqueness to image map name
    private static int uniqueId;

    public QChartViewImpl(QChartModel model) {
        this.model = model;

        model.addListener(new QChartModel.ModelListener() {
            public void onMetaChanged() {
                applyChartMeta();
            }

            public void onDataChanged() {
                applyChartData();
            }

        });

        initializeUI();
    }

    public void addListener(ViewListener listener) {
        this.listener = listener;
    }

    public Widget getWidget() {
        return this;
    }

    public static String getChartTypeTitle(ChartType type) {
        if(ChartType.BAR.equals(type)) {
            return I18N.getMessages().chartsBarChartLabel();
        }

        if(ChartType.PIE.equals(type)) {
            return I18N.getMessages().chartsPieChartLabel();
        }

        return null;
    }

    public DateFieldData getDateFilter(){
        return dateField.getModel().getData();
    }
    
    public boolean isIncludeClosed(){
        return includeClosedField.isChecked();
    }

    protected void onLoad(){
        if(model.getData() == null)
            fireChartRefreshEvent();
    }

    private void fireChangeTypeEvent(Widget sender) {
        if(sender instanceof RadioButton && !currentTypeButton.equals(sender)) {
            ChartType newType = ChartType.valueOf((String) typeButtons.get(
                    sender));
            if(newType == null) {
                throw new IllegalStateException(
                        "Cannot find ChartType by widget " + sender);
            }

            if(listener != null) {
                listener.onChartChangeType(newType);
            }

            currentTypeButton = (RadioButton) sender;
        }
    }

    private void fireChartRefreshEvent() {
        if(listener != null) {
            listener.onChartRefresh();
        }
    }

    private void fireClickChartEvent(ChartDataItem item) {
        if(listener != null) {
            listener.onChartClick(item);
        }
    }

    private void initializeUI() {
        super.initializeBaseUI(QFormElement.HORIZONTAL);

        ChartMeta chartMeta = model.getMeta();

        chartPanel = new VerticalPanel();
        chartPanel.setSpacing(2);
        chartPanel.addStyleName("chart_panel");
        addToPanel(chartPanel);

        HorizontalPanel row0 = new HorizontalPanel();
        row0.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
        row0.setWidth("100%");

        HorizontalPanel typePanel = new HorizontalPanel();
        for(int i = 0; i < ChartType.TYPES.length; i++) {
            ChartType type = ChartType.TYPES[i];

            RadioButton typeButton = new RadioButton(
                    "chartType" + chartMeta.getID());
            typeButton.setHTML(getChartTypeTitle(type) + "&nbsp;");
            typeButton.setChecked(type.equals(chartMeta.getType()));
            if(typeButton.isChecked()) {
                currentTypeButton = typeButton;
            }
            typeButton.addClickListener(new ClickListener() {
                public void onClick(Widget sender) {
                    fireChangeTypeEvent(sender);
                }
            });
            typePanel.add(typeButton);

            typeButtons.put(typeButton, type.getName());

        }
        row0.add(typePanel);
        row0.setCellHorizontalAlignment(
                typePanel, HasHorizontalAlignment.ALIGN_LEFT);

        // Prepare <Include closed> filter
        includeClosedField = new CheckBox(I18N.getMessages().chartsIncludeClosedFilterLabel());
        includeClosedField.setChecked(model.isIncludeClosed());
        row0.add(includeClosedField);
        row0.setCellHorizontalAlignment(
                includeClosedField, HasHorizontalAlignment.ALIGN_RIGHT);

        chartPanel.add(row0);

        HorizontalPanel row1 = new HorizontalPanel();
        row1.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
        row1.setWidth("100%");

        DateFieldMeta dateMeta = new DateFieldMeta(null, I18N.getMessages().chartsFilterByDateLabel());
        dateField = new QDateField(dateMeta, QFormElement.HORIZONTAL);
        dateField.getView().setEnabledForSearch();

        // Prepare date range filter
        DateFieldData dateData = model.getDateFilter();
        if (dateData != null) {
            dateField.getModel().setData(dateData);
            dateField.getController().forcedDateFormat();
        } else {
            dateField.getModel().setData(new DateFieldData());
        }

        row1.add(dateField.getView());

        HorizontalPanel refreshPanel = new HorizontalPanel();
        refreshPanel.setSpacing(2);
        refreshPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);

        Image refreshIcon = new Image("chart/refresh.gif");
        refreshIcon.addClickListener(new ClickListener() {
            public void onClick(Widget sender) {
                fireChartRefreshEvent();
            }
        });
        refreshPanel.add(refreshIcon);

        HTML refreshLink = new HTML(
                "&nbsp;" + I18N.getMessages().chartsRefreshLabel(), false);
        refreshLink.addStyleName("chart_refresh_link");
        refreshLink.addClickListener(new ClickListener() {
            public void onClick(Widget sender) {
                fireChartRefreshEvent();
            }
        });
        refreshPanel.add(refreshLink);

        row1.add(refreshPanel);
        row1.setCellHorizontalAlignment(
                refreshPanel, HasHorizontalAlignment.ALIGN_RIGHT);

        chartPanel.add(row1);

        HorizontalPanel imagePanel = new HorizontalPanel();
        imagePanel.addStyleName("chart_image_panel");

        chartMap = new ImageMap("imagemap" + uniqueId++);
        imagePanel.add(chartMap);

        chartImage = new MappedImage();
        chartImage.addStyleName("chart_image");
        chartImage.setWidth(model.getMeta().getWidth() + "px");
        chartImage.setHeight(model.getMeta().getHeight() + "px");
        chartImage.setVisible(false);
        chartImage.addLoadListener(new LoadListener() {
            public void onLoad(Widget sender) {
                // TODO hide "Loading..." message
                if(!chartImage.isVisible()) {
                    chartImage.setVisible(true);
                }
            }

            public void onError(Widget sender) {
                // TODO display onerror message and broken chart picture (prefetch it)
                Window.alert("Unable load chart image " + model.getData().getUrl());
            }
        });
        chartImage.setMap(chartMap);
        imagePanel.add(chartImage);

        imagePanel.setCellHorizontalAlignment(
                chartImage, HasHorizontalAlignment.ALIGN_CENTER);
        imagePanel.setCellVerticalAlignment(
                chartImage, HasVerticalAlignment.ALIGN_MIDDLE);

        chartPanel.add(imagePanel);

        initPanel();
    }

    private void applyChartData() {
        String typeName = model.getMeta().getType().getName();
        // Sets checked corresponded chart type radio button
        for(Iterator it = typeButtons.keySet().iterator(); it.hasNext();) {
            RadioButton typeButton = (RadioButton) it.next();
            typeButton.setChecked(typeButtons.get(typeButton).equals(typeName));
        }

        setupChartImage();
    }

    private void applyChartMeta() {
        // TODO display "Loading..." message
    }

    private void setupChartImage() {
        // Adds areas to the image map
        chartMap.clear();
        List items = model.getData().getDataItems();
        for(int i = 0; i < items.size(); i++) {
            final ChartDataItem item = (ChartDataItem) items.get(i);

            ImageMapArea area = chartMap.addArea(item.getShape(),
                    item.getCoords());
            area.setTitle(item.getTitle());

            area.addClickListener(new ClickListener() {
                public void onClick(Widget sender) {
                    fireClickChartEvent(item);
                }
            });
        }

        // Sets up image's properties
        chartImage.setUrl(model.getData().getUrl());
    }

    protected void fireLinkedFormSelectionEvent() {
    }

    public int getClientWidth() {
        return panel.getOffsetWidth();
    }

    public int getFilledWidth() {
        return 0;
    }

    protected void setClientWidth(String clientWidth) {
        panel.setWidth(clientWidth);
    }

}
