/*
 * Copyright 2011 Marc Grue.
 *
 * 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 org.qi4j.sample.dcicargo.sample_b.communication.web.handling;

import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxFallbackButton;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.util.value.ValueMap;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.qi4j.sample.dcicargo.sample_b.communication.query.CommonQueries;
import org.qi4j.sample.dcicargo.sample_b.communication.query.HandlingQueries;
import org.qi4j.sample.dcicargo.sample_b.communication.web.BasePage;
import org.qi4j.sample.dcicargo.sample_b.context.interaction.handling.ProcessHandlingEvent;
import org.qi4j.sample.dcicargo.sample_b.data.structure.handling.HandlingEventType;
import org.qi4j.sample.dcicargo.sample_b.infrastructure.wicket.form.AbstractForm;
import org.qi4j.sample.dcicargo.sample_b.infrastructure.wicket.form.DateTextFieldWithPicker;

/**
 * Incident Logging Application mockup page
 *
 * This is a mockup of an Incident Logging Application interface that external handling
 * authorities would use to send us handling event data for cargos that they have handled.
 *
 * For simplicity we don't create a separate Incident Logging Application for now but instead
 * let it reside inside our booking application. We act as a handling authority entering handling
 * event data, and in the lower half of the page we mock the receipt of the data and show the
 * validation results from our own validation of the incoming data.
 *
 * We could also instead (in future versions?) implement a web service endpoint receiving
 * asynchronous messages from a separate incident logging application. We have prepared for
 * this by separating the processing in three steps, with ProcessHandlingEvent coordinating
 * the following steps:
 *
 * {@link ProcessHandlingEvent}
 * 1. parsedHandlingEventData = parse( completion, trackingId, eventType, unLocode, voyage )
 * 2. handlingEvent = register( parsedHandlingEventData )
 * 3. inspect( handlingEvent )
 *
 * The last step updates the delivery status of the cargo.
 *
 * On this page we act as a handling authority sending handling event data to our booking
 * application. We perform basic validation and parsing of incoming data, and in case of a
 * valid registration attempt, synchronously send the data to the booking application for
 * processing there.
 */
public class IncidentLoggingApplicationMockupPage extends BasePage
{
    public IncidentLoggingApplicationMockupPage()
    {
        super( "handling" ); // Selects the Handling tab
        add( new ReportHandlingEventForm() );
    }

    private final class ReportHandlingEventForm extends AbstractForm<Void>
    {
        FeedbackPanel feedback;

        // Form values
        Date completion;
        String trackingId, unLocode, voyageNumber, eventType;

        // Input
        TextField<String> trackingIdInput, eventTypeInput, voyageInput, locationInput;
        String trackingIdSelected, eventTypeSelected, voyageSelected, locationSelected;
        DropDownChoice<String> trackingIdSelector, eventTypeSelector, voyageSelector, locationSelector;

        // To avoid re-submitting same data
        String lastSubmittedData;

        public ReportHandlingEventForm()
        {
            final FeedbackPanel feedback = new FeedbackPanel( "feedback" );
            add( feedback.setOutputMarkupId( true ) );

            // Completion time

            final DateTextFieldWithPicker completionDateInput = new DateTextFieldWithPicker( "completion", "Completion", this );
            completionDateInput.earliestDate( new LocalDate() );
            add( completionDateInput.setLabel( Model.of( "Completion" ) ) );

            HandlingQueries fetch = new HandlingQueries();

            // Tracking id

            trackingIdInput = new TextField<String>( "trackingIdInput", new PropertyModel<String>( this, "trackingId" ) );
            add( trackingIdInput.setRequired( true ).setLabel( Model.of( "Cargo" ) ).setOutputMarkupId( true ) );

            trackingIdSelector = new DropDownChoice<String>( "trackingIdSelector",
                                                             new PropertyModel<String>( this, "trackingIdSelected" ),
                                                             fetch.cargoIds() );
            trackingIdSelector.add( new AjaxFormComponentUpdatingBehavior( "onchange" )
            {
                @Override
                protected void onUpdate( AjaxRequestTarget target )
                {
                    trackingId = trackingIdSelected;
                    target.add( feedback, trackingIdInput, trackingIdSelector );
                }
            } );
            add( trackingIdSelector.setOutputMarkupId( true ) );

            // Event Type

            eventTypeInput = new TextField<String>( "eventTypeInput", new PropertyModel<String>( this, "eventType" ) );
            add( eventTypeInput.setRequired( true ).setLabel( Model.of( "Event Type" ) ).setOutputMarkupId( true ) );

            eventTypeSelector = new DropDownChoice<String>( "eventTypeSelector",
                                                            new PropertyModel<String>( this, "eventTypeSelected" ),
                                                            fetch.eventTypes() );
            eventTypeSelector.add( new AjaxFormComponentUpdatingBehavior( "onchange" )
            {
                @Override
                protected void onUpdate( AjaxRequestTarget target )
                {
                    eventType = eventTypeSelected;
                    target.add( feedback, eventTypeInput, eventTypeSelector );
                }
            } );
            add( eventTypeSelector.setOutputMarkupId( true ) );

            // Voyage (optional in some cases)

            voyageInput = new TextField<String>( "voyageInput", new PropertyModel<String>( this, "voyageNumber" ) );
            add( voyageInput.setLabel( Model.of( "Voyage" ) ).setOutputMarkupId( true ) );

            voyageSelector = new DropDownChoice<String>( "voyageSelector",
                                                         new PropertyModel<String>( this, "voyageSelected" ),
                                                         fetch.voyages() );
            voyageSelector.add( new AjaxFormComponentUpdatingBehavior( "onchange" )
            {
                @Override
                protected void onUpdate( AjaxRequestTarget target )
                {
                    voyageNumber = voyageSelected;
                    target.add( feedback, voyageInput, voyageSelector );
                }
            } );
            add( voyageSelector.setOutputMarkupId( true ) );

            // Location

            locationInput = new TextField<String>( "locationInput", new PropertyModel<String>( this, "unLocode" ) );
            add( locationInput.setRequired( true ).setLabel( Model.of( "Location" ) ).setOutputMarkupId( true ) );

            locationSelector = new DropDownChoice<String>( "locationSelector",
                                                           new PropertyModel<String>( this, "locationSelected" ),
                                                           new CommonQueries().unLocodes() );
            locationSelector.add( new AjaxFormComponentUpdatingBehavior( "onchange" )
            {
                @Override
                protected void onUpdate( AjaxRequestTarget target )
                {
                    unLocode = locationSelected;
                    target.add( feedback, locationInput, locationSelector );
                }
            } );
            add( locationSelector.setOutputMarkupId( true ) );

            // Submit and process

            add( new AjaxFallbackButton( "register", this )
            {
                @Override
                protected void onSubmit( AjaxRequestTarget target, Form<?> form )
                {
                    try
                    {
                        // We want to allow making multiple _unique_ handling event registrations
                        if( sameDataIsSubmitted() )
                        {
                            throw new Exception( "Can't re-submit the same data." );
                        }

                        // We simulate receiving raw text data from incident logging applications
                        // Add current time to date to have same-dates in processing order (would register full time in real app)
                        Date adjustedCompletion = new Date( completion.getTime() + new DateTime().getMillisOfDay() );
                        String completionTimeString = new SimpleDateFormat( "yyyy-MM-dd HH:mm" ).format( adjustedCompletion );

                        // Parse "incoming" data (step 1 of ProcessHandlingEvent use case)
                        tbf.newTransient( ProcessHandlingEvent.class ).parse(
                            completionTimeString, trackingId, eventType, unLocode, voyageNumber );

                        /**
                         * We could redirect to Details, but it's more fun to update details in a separate
                         * window to follow the successive handling event registrations you make...
                         * */
//                        setResponsePage( CargoDetailsPage.class, new PageParameters().set( 0, trackingId ) );

                        try
                        {
                            HandlingEventType.valueOf( eventType );
                        }
                        catch( Exception e )
                        {
                            throw new Exception( "'" + eventType + "' is not a valid handling event type" );
                        }

                        ValueMap map = new ValueMap();
                        map.put( "type", eventType );
                        map.put( "location", unLocode );
                        if( voyageNumber != null )
                        {
                            map.put( "voyage", voyageNumber );
                        }
                        String msg = new StringResourceModel( "handlingEvent.${type}", this, new Model<ValueMap>( map ) )
                            .getObject();

                        feedback.info( "Registered handling event for cargo '" + trackingId + "': " + msg );
                        target.add( feedback );
                    }
                    catch( Exception e )
                    {
                        logger.warn( "Problem registering handling event: " + e.getMessage() );
                        feedback.error( e.getMessage() );
                        target.add( feedback );
                    }
                }

                @Override
                protected void onError( final AjaxRequestTarget target, Form<?> form )
                {
                    target.add( feedback );
                    focusFirstError( target );
                }
            } );
        }

        private boolean sameDataIsSubmitted()
        {
            String submittedData = completion.toString() + trackingId + unLocode + voyageNumber + eventType;

            if( submittedData.equals( lastSubmittedData ) )
            {
                return true;
            }

            // Valid new data submitted
            lastSubmittedData = submittedData;

            return false;
        }
    }
}