/*
 * 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 org.apache.wicket.examples.forminput;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import org.apache.wicket.examples.WicketExamplePage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Check;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.CheckGroup;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponentUpdatingBehavior;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.form.ListMultipleChoice;
import org.apache.wicket.markup.html.form.Radio;
import org.apache.wicket.markup.html.form.RadioChoice;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.form.SimpleFormComponentLabel;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.util.convert.ConversionException;
import org.apache.wicket.util.convert.IConverter;
import org.apache.wicket.util.convert.MaskConverter;
import org.apache.wicket.validation.validator.RangeValidator;


/**
 * Example for form input.
 * 
 * @author Eelco Hillenius
 * @author Jonathan Locke
 */
public class FormInput extends WicketExamplePage
{
    /**
     * Form for collecting input.
     */
    private class InputForm extends Form<FormInputModel>
    {
        /**
         * Construct.
         * 
         * @param name
         *            Component name
         */
        @SuppressWarnings("serial")
        public InputForm(String name)
        {
            super(name, new CompoundPropertyModel<>(new FormInputModel()));

            // Dropdown for selecting locale
            add(new LocaleDropDownChoice("localeSelect"));

            // Link to return to default locale
            add(new Link<Void>("defaultLocaleLink") {
                public void onClick() {
                    WebRequest request = (WebRequest)getRequest();
                    setLocale(request.getLocale());
                }
            });

            add(new TextField<String>("stringProperty").setRequired(true).setLabel(
                new Model<>("String")));

            add(new TextField<>("integerProperty", Integer.class).setRequired(true).add(
                new RangeValidator<>(1, Integer.MAX_VALUE)));

            add(new TextField<>("doubleProperty", Double.class).setRequired(true));

            add(new TextField<Integer>("integerInRangeProperty").setRequired(true).add(
                new RangeValidator<>(0, 100)));

            add(new CheckBox("booleanProperty"));
            add(new Multiply("multiply"));
            // display the multiply result
            Label multiplyLabel = new Label("multiplyLabel", new PropertyModel<Integer>(
                getDefaultModel(), "multiply"));
            // just for fun, add a border so that our result will be displayed as '[ x ]'
            multiplyLabel.add(new BeforeAndAfterBorder());
            add(multiplyLabel);
            RadioChoice<String> rc = new RadioChoice<>("numberRadioChoice", NUMBERS).setSuffix("");
            rc.setLabel(new Model<>("number"));
            rc.setRequired(true);
            add(rc);

            RadioGroup<String> group = new RadioGroup<>("numbersGroup");
            add(group);
            ListView<String> persons = new ListView<String>("numbers", NUMBERS)
            {
                @Override
                protected void populateItem(ListItem<String> item)
                {
                    Radio<String> radio = new Radio<>("radio", item.getModel());
                    radio.setLabel(item.getModel());
                    item.add(radio);
                    item.add(new SimpleFormComponentLabel("number", radio));
                }
            }.setReuseItems(true);
            group.add(persons);

            CheckGroup<String> checks = new CheckGroup<>("numbersCheckGroup");
            add(checks);
            ListView<String> checksList = new ListView<String>("numbers", NUMBERS)
            {
                @Override
                protected void populateItem(ListItem<String> item)
                {
                    Check<String> check = new Check<>("check", item.getModel());
                    check.setLabel(item.getModel());
                    item.add(check);
                    item.add(new SimpleFormComponentLabel("number", check));
                }
            }.setReuseItems(true);
            checks.add(checksList);

            add(new ListMultipleChoice<>("siteSelection", SITES));

            // TextField using a custom converter.
            add(new TextField<URL>("urlProperty", URL.class)
            {
                @Override
                protected IConverter<?> createConverter(Class<?> type)
                {
                    if (URL.class.isAssignableFrom(type))
                    {
                        return URLConverter.INSTANCE;
                    }
                    return null;
                }
            });

            // TextField using a mask converter
            add(new TextField<UsPhoneNumber>("phoneNumberUS", UsPhoneNumber.class)
            {
                @Override
                protected IConverter<?> createConverter(Class<?> type)
                {
                    if (UsPhoneNumber.class.isAssignableFrom(type))
                    {
                        // US telephone number mask
                        return new MaskConverter<>("(###) ###-####", UsPhoneNumber.class);
                    }
                    return null;
                }
            });

            // and this is to show we can nest ListViews in Forms too
            add(new LinesListView("lines"));

            add(new Button("saveButton"));

            add(new Button("resetButton") {
                @Override
                public void onSubmit()
                {
                    setResponsePage(FormInput.class);
                }
            }.setDefaultFormProcessing(false));
        }

        @Override
        public void onSubmit()
        {
            // Form validation successful. Display message showing edited model.
            info("Saved model " + getDefaultModelObject());
        }
    }

    /** list view to be nested in the form. */
    private static final class LinesListView extends ListView<String>
    {
        /**
         * Construct.
         * 
         * @param id
         */
        public LinesListView(String id)
        {
            super(id);
            // always do this in forms!
            setReuseItems(true);
        }

        @Override
        protected void populateItem(ListItem<String> item)
        {
            // add a text field that works on each list item model (returns
            // objects of type FormInputModel.Line) using property text.
            item.add(new TextField<>("lineEdit", new PropertyModel<String>(
                item.getDefaultModel(), "text")));
        }
    }

    /**
     * Choice for a locale.
     */
    private final class LocaleChoiceRenderer implements IChoiceRenderer<Locale>
    {
        @Override
        public Object getDisplayValue(Locale locale)
        {
            return locale.getDisplayName(getLocale());
        }
    }

    /**
     * Dropdown with Locales.
     */
    private final class LocaleDropDownChoice extends DropDownChoice<Locale>
    {
        /**
         * Construct.
         * 
         * @param id
         *            component id
         */
        public LocaleDropDownChoice(String id)
        {
            super(id, FormInputApplication.LOCALES, new LocaleChoiceRenderer());

            // set the model that gets the current locale, and that is used for
            // updating the current locale to property 'locale' of FormInput
            setModel(new PropertyModel<>(FormInput.this, "locale"));
            
            add(new FormComponentUpdatingBehavior() {
                @Override
                protected void onUpdate()
                {
                    // note that we don't have to do anything here, as our property
                    // model already calls FormInput.setLocale when the model is
                    // updated

                    // force re-render by setting the page to render to the bookmarkable
                    // instance, so that the page will be rendered from scratch,
                    // re-evaluating the input patterns etc
                    setResponsePage(FormInput.class);
                }
            });
        }
    }

    /** available sites for the multiple select. */
    private static final List<String> SITES = Arrays.asList("The Server Side", "Java Lobby",
        "Java.Net");

    /** available numbers for the radio selection. */
    static final List<String> NUMBERS = Arrays.asList("1", "2", "3");

    /**
     * Constructor
     */
    public FormInput()
    {
        // Construct form and feedback panel and hook them up
        final FeedbackPanel feedback = new FeedbackPanel("feedback");
        add(feedback);
        add(new InputForm("inputForm"));
    }

    /**
     * Sets locale for the user's session (getLocale() is inherited from Component)
     * 
     * @param locale
     *            The new locale
     */
    public void setLocale(Locale locale)
    {
        if (locale != null)
        {
            getSession().setLocale(locale);
        }
    }

    private static class URLConverter implements IConverter<URL>
    {
        public static final URLConverter INSTANCE = new URLConverter();

        @Override
        public URL convertToObject(String value, Locale locale)
        {
            try
            {
                return new URL(value);
            }
            catch (MalformedURLException e)
            {
                throw new ConversionException("'" + value + "' is not a valid URL");
            }
        }

        @Override
        public String convertToString(URL value, Locale locale)
        {
            return value != null ? value.toString() : null;
        }
    }
}