ReservationApplication.java
/* 
 * Copyright 2009 IT Mill 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.itmill.toolkit.demo.reservation;

import java.awt.geom.Point2D;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import com.itmill.toolkit.Application;
import com.itmill.toolkit.data.Container;
import com.itmill.toolkit.data.Item;
import com.itmill.toolkit.data.Property.ValueChangeEvent;
import com.itmill.toolkit.data.Property.ValueChangeListener;
import com.itmill.toolkit.ui.Alignment;
import com.itmill.toolkit.ui.Button;
import com.itmill.toolkit.ui.Label;
import com.itmill.toolkit.ui.OrderedLayout;
import com.itmill.toolkit.ui.Panel;
import com.itmill.toolkit.ui.TabSheet;
import com.itmill.toolkit.ui.Table;
import com.itmill.toolkit.ui.TextField;
import com.itmill.toolkit.ui.Window;
import com.itmill.toolkit.ui.TabSheet.SelectedTabChangeEvent;
import com.itmill.toolkit.ui.Window.Notification;

public class ReservationApplication extends Application {

    private SampleDB db;

    ResourceSelectorPanel resourcePanel;

    private CalendarField reservedFrom;

    private static final long DEFAULT_GAP_MILLIS = 3600000// (almost) one

    // hour
    private long currentGapMillis = DEFAULT_GAP_MILLIS; // current length of

    // reservation
    private CalendarField reservedTo;

    private Label resourceName;

    private TextField description;

    private Button reservationButton;

    private Table allTable;

    private GoogleMap map;

    @Override
    public void init() {
        //
        db = new SampleDB(getProperty("jdbcUrl"));

        final Window mainWindow = new Window("Reservr ");
        setMainWindow(mainWindow);
        setTheme("reservr");
        mainWindow.getLayout().setWidth("100%");

        Label logo = new Label("Reservr");
        logo.setStyleName("logo");
        mainWindow.addComponent(logo);

        Label slogan = new Label("Representational vehicles on-the-road");
        slogan.setStyleName("slogan");
        mainWindow.addComponent(slogan);

        final TabSheet mainTabs = new TabSheet();
        mainWindow.addComponent(mainTabs);

        final OrderedLayout reservationTab = new OrderedLayout();
        reservationTab.setWidth("100%");
        mainTabs.addTab(reservationTab, "Make reservation"null);

        resourcePanel = new ResourceSelectorPanel("Resources");
        resourcePanel.setResourceContainer(db.getResources(null));
        resourcePanel.addListener(
                ResourceSelectorPanel.SelectedResourcesChangedEvent.class,
                this, "selectedResourcesChanged");
        reservationTab.addComponent(resourcePanel);

        final Panel reservationPanel = new Panel("Reservation",
                new OrderedLayout(OrderedLayout.ORIENTATION_HORIZONTAL));
        reservationPanel.addStyleName(Panel.STYLE_LIGHT);
        reservationPanel.getLayout().setMargin(true);
        reservationTab.addComponent(reservationPanel);

        final OrderedLayout infoLayout = new OrderedLayout();
        infoLayout.setSpacing(true);
        infoLayout.setSizeUndefined();
        infoLayout.setMargin(false, true, false, false);
        reservationPanel.addComponent(infoLayout);
        resourceName = new Label("From the list above");
        resourceName.setCaption("Choose resource");
        infoLayout.addComponent(resourceName);
        description = new TextField();
        description.setWidth("250px");
        description.setRows(5);
        infoLayout.addComponent(description);
        reservationButton = new Button("Make reservation", this,
                "makeReservation");
        infoLayout.addComponent(reservationButton);
        infoLayout.setComponentAlignment(reservationButton, Alignment.MIDDLE_CENTER);

        map = new GoogleMap();
        map.setWidth("250px");
        map.setHeight("250px");
        map.setItemMarkerHtmlPropertyId(SampleDB.Resource.PROPERTY_ID_NAME);
        map.setItemMarkerXPropertyId(SampleDB.Resource.PROPERTY_ID_LOCATIONX);
        map.setItemMarkerYPropertyId(SampleDB.Resource.PROPERTY_ID_LOCATIONY);
        map.setContainerDataSource(db.getResources(null));
        infoLayout.addComponent(map);

        final Calendar from = Calendar.getInstance();
        from.add(Calendar.HOUR, 1);
        from.set(Calendar.MINUTE, 0);
        from.set(Calendar.SECOND, 0);
        from.set(Calendar.MILLISECOND, 0);
        reservedFrom = new CalendarField("From");
        reservedFrom.setMinimumDate(from.getTime());
        reservedFrom.setValue(from.getTime());
        reservedFrom.setImmediate(true);
        initCalendarFieldPropertyIds(reservedFrom);
        reservationPanel.addComponent(reservedFrom);

        final Label arrowLabel = new Label("»");
        arrowLabel.setContentMode(Label.CONTENT_XHTML);
        arrowLabel.setStyleName("arrow");
        reservationPanel.addComponent(arrowLabel);

        final Calendar to = Calendar.getInstance();
        to.setTime(from.getTime());
        to.add(Calendar.MILLISECOND, (intDEFAULT_GAP_MILLIS);
        reservedTo = new CalendarField("To");
        reservedTo.setMinimumDate(to.getTime());
        reservedTo.setValue(to.getTime());
        reservedTo.setImmediate(true);
        initCalendarFieldPropertyIds(reservedTo);
        reservationPanel.addComponent(reservedTo);

        reservedFrom.addListener(new ValueChangeListener() {
            public void valueChange(ValueChangeEvent event) {
                final Date fd = (DatereservedFrom.getValue();
                if (fd == null) {
                    reservedTo.setValue(null);
                    reservedTo.setEnabled(false);
                    refreshSelectedResources(true);
                    return;
                else {
                    reservedTo.setEnabled(true);
                }
                reservedTo.setMinimumDate(new Date(fd.getTime()
                        + DEFAULT_GAP_MILLIS));
                final Calendar to = Calendar.getInstance();
                to.setTime(fd);
                to.add(Calendar.MILLISECOND, (intcurrentGapMillis);
                reservedTo.setValue(to.getTime());
                refreshSelectedResources(true);
            }
        });
        reservedTo.addListener(new ValueChangeListener() {
            public void valueChange(ValueChangeEvent event) {
                final Date from = (DatereservedFrom.getValue();
                final Date to = (DatereservedTo.getValue();
                currentGapMillis = to.getTime() - from.getTime();
                if (currentGapMillis <= 0) {
                    final Calendar t = Calendar.getInstance();
                    t.setTime(from);
                    t.add(Calendar.MILLISECOND, (intDEFAULT_GAP_MILLIS);
                    reservedTo.setValue(t.getTime());
                }
                refreshSelectedResources(true);
            }
        });

        allTable = new Table();
        allTable.setWidth("100%");
        allTable.setHeight("450px");
        allTable.setColumnCollapsingAllowed(true);
        allTable.setColumnReorderingAllowed(true);
        mainTabs.addTab(allTable, "All reservations"null);
        mainTabs.addListener(new TabSheet.SelectedTabChangeListener() {
            public void selectedTabChange(SelectedTabChangeEvent event) {
                refreshReservations();
            }
        });

        resourcePanel.selectFirstCategory();
        refreshReservations();
        refreshSelectedResources(true);
    }

    public void makeReservation() {
        try {
            final Item resource = getActiveResource();
            if (resource != null) {
                db.addReservation(resource, 0(DatereservedFrom.getValue(),
                        (DatereservedTo.getValue()(Stringdescription
                                .getValue());
                getMainWindow()
                        .showNotification(
                                "Success!",
                                "You have reserved the resource for the selected period.",
                                Notification.TYPE_WARNING_MESSAGE);
                refreshReservations();
                refreshSelectedResources(false);
            else {
                getMainWindow().showNotification("Oops!",
                        "Please select a resource (or category) to reserve.",
                        Notification.TYPE_WARNING_MESSAGE);
            }
        catch (final ResourceNotAvailableException e) {
            getMainWindow()
                    .showNotification(
                            "Not available!",
                            "The selected resource is already reserved for the selected period.",
                            Notification.TYPE_ERROR_MESSAGE);
            refreshReservations();
        }
    }

    private Item getActiveResource() throws ResourceNotAvailableException {
        final List rids = resourcePanel.getSelectedResources();
        if (rids != null && rids.size() 0) {
            for (final Iterator it = rids.iterator(); it.hasNext();) {
                final Item resource = (Itemit.next();
                final int id = ((Integerresource.getItemProperty(
                        SampleDB.Resource.PROPERTY_ID_ID).getValue())
                        .intValue();
                if (db.isAvailableResource(id, (DatereservedFrom.getValue(),
                        (DatereservedTo.getValue())) {
                    return resource;
                }
            }
            throw new ResourceNotAvailableException("No available resources");
        else {
            return null;
        }
    }

    private void refreshReservations() {
        final Container reservations = db.getReservations(resourcePanel
                .getSelectedResources());
        reservedFrom.setContainerDataSource(reservations);
        reservedTo.setContainerDataSource(reservations);
        final Container allReservations = db.getReservations(null);
        allTable.setContainerDataSource(allReservations);
        if (allReservations != null && allReservations.size() 0) {
            allTable.setVisibleColumns(new Object[] {
                    SampleDB.Reservation.PROPERTY_ID_RESERVED_FROM,
                    SampleDB.Reservation.PROPERTY_ID_RESERVED_TO,
                    SampleDB.Resource.PROPERTY_ID_NAME,
                    SampleDB.Resource.PROPERTY_ID_DESCRIPTION,
                    SampleDB.Reservation.PROPERTY_ID_DESCRIPTION });
            allTable.setColumnHeaders(new String[] { "From""To""Resource",
                    "Description""Message" });
        }
    }

    private void refreshSelectedResources(boolean alertIfNotAvailable) {
        Item resource = null;
        try {
            resource = getActiveResource();
        catch (final ResourceNotAvailableException e) {
            if (alertIfNotAvailable) {
                getMainWindow().showNotification("Not available",
                        "Please choose another resource or time period.",
                        Notification.TYPE_HUMANIZED_MESSAGE);
            }
            refreshReservations();
            return;
        }
        map.clear();
        if (resource == null) {
            resourceName.setCaption("Choose resource above");
            resourceName.setValue("");
            map.setContainerDataSource(db.getResources(null));
            map.setZoomLevel(1);

        else {
            // Display active resource name + desc
            String name = (Stringresource.getItemProperty(
                    SampleDB.Resource.PROPERTY_ID_NAME).getValue();
            String desc = (Stringresource.getItemProperty(
                    SampleDB.Resource.PROPERTY_ID_DESCRIPTION).getValue();
            resourceName.setCaption(name);
            resourceName.setValue(desc);
            // Put all resources on map (may be many if category was selected)
            final LinkedList srs = resourcePanel.getSelectedResources();
            for (final Iterator it = srs.iterator(); it.hasNext();) {
                resource = (Itemit.next();
                name = (Stringresource.getItemProperty(
                        SampleDB.Resource.PROPERTY_ID_NAME).getValue();
                desc = (Stringresource.getItemProperty(
                        SampleDB.Resource.PROPERTY_ID_DESCRIPTION).getValue();
                final Double x = (Doubleresource.getItemProperty(
                        SampleDB.Resource.PROPERTY_ID_LOCATIONX).getValue();
                final Double y = (Doubleresource.getItemProperty(
                        SampleDB.Resource.PROPERTY_ID_LOCATIONY).getValue();
                if (x != null && y != null) {
                    map.addMarker(name + "<br/>" + desc, new Point2D.Double(x
                            .doubleValue(), y.doubleValue()));

                }

            }
            map.setZoomLevel((srs.size() == 14 9));
        }

    }

    private void initCalendarFieldPropertyIds(CalendarField cal) {
        cal.setItemStyleNamePropertyId(SampleDB.Resource.PROPERTY_ID_STYLENAME);
        cal
                .setItemStartPropertyId(SampleDB.Reservation.PROPERTY_ID_RESERVED_FROM);
        cal.setItemEndPropertyId(SampleDB.Reservation.PROPERTY_ID_RESERVED_TO);
        cal.setItemTitlePropertyId(SampleDB.Resource.PROPERTY_ID_NAME);
        cal
                .setItemDescriptionPropertyId(SampleDB.Reservation.PROPERTY_ID_DESCRIPTION);
    }

    public void selectedResourcesChanged(
            ResourceSelectorPanel.SelectedResourcesChangedEvent event) {
        refreshReservations();
        refreshSelectedResources(true);
    }

}