/*
 * Copyright 2002-2008 the original author or authors.
 *
 * 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.springframework.samples.petclinic.web;

import static org.springframework.samples.petclinic.web.PageType.FIND;
import static org.springframework.samples.petclinic.web.PageType.FORM;
import static org.springframework.samples.petclinic.web.PageType.LIST;
import static org.springframework.samples.petclinic.web.ViewUtils.OWNER;
import static org.springframework.samples.petclinic.web.ViewUtils.buildViewName;
import static org.springframework.samples.petclinic.web.ViewUtils.getRedirectForOwner;

import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.Clinic;
import org.springframework.samples.petclinic.Owner;
import org.springframework.samples.petclinic.validation.OwnerValidator;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.RequestToViewNameTranslator;


/**
 * Controller that handles all {@link Owner}-related requests,
 * i.e.: all requests for /petclinic/main/owner/*
 *
 * @author Juergen Hoeller
 * @author Ken Krebs
 * @author Chris Beams
 */
@Controller
public class OwnerController {

    static final String OWNER_FIND_VIEW = buildViewName(OWNER, FIND);
    static final String OWNER_LIST_VIEW = buildViewName(OWNER, LIST);
    static final String OWNER_FORM_VIEW = buildViewName(OWNER, FORM);

    private final Clinic clinic;

    @Autowired
    public OwnerController(Clinic clinic) {
        this.clinic = clinic;
    }
    
    /**
     * A factory method for creating or loading an existing owner.
     * <p/> 
     * Note that this method is annotated with @ModelAttribute, which means
     * it will be invoked before every request handling method in this class
     * in order to pre-create the Owner object either by loading it or by
     * creating it from scratch.
     * 
     * @param id an optional parameter specifying the id of the owner. If
     *      left unspecified a new owner is created.
     *      
     * @return an instance of an Owner object
     */
    @ModelAttribute
    public Owner createOwner(@RequestParam(required=false) Integer id) {
        return (id != null) ? this.clinic.loadOwner(id) : new Owner();
    }

    /**
     * Custom handler for displaying an individual owner,
     * i.e.: GET requests for /petclinic/main/owner/show.
     * <p/>
     * Note that this handler returns void instead of
     * a ModelAndView, thus leveraging convention-based logical view name selection.
     * It relies on the {@link RequestToViewNameTranslator} to determine the
     * logical view name based on the request URL: "/owner/show" -&gt; "owner/show".
     *
     * @see WEB-INF/jsp/owner/show.jsp
     */
    @RequestMapping
    public void show() {
    }

    /**
     * Custom handler for displaying the 'Edit Owner' form,
     * i.e.: GET requests for /petclinic/main/owner/form.
     * <p/>
     * Note that this handler returns void instead of a {@link ModelAndView},
     * signifying a default logical view name should be selected by Spring MVC.  
     * The ui {@link Model} is supplied as a parameter to the method and 
     * populated with the new Order object to be rendered.
     *
     * @see #form(Owner, BindingResult, SessionStatus)
     * @see WEB-INF/jsp/owner/form.jsp
     */
    @RequestMapping(method = RequestMethod.GET)
    public void form() {
    }

    /**
     * Custom handler for processing the submission of the 'Add Owner' and
     * 'Edit Owner' forms, i.e.: POST requests for /petclinic/main/owner/form, 
     * which may be to add or to edit an owner depending on whether an ownerId
     * was provided with the request.
     * <p/>
     * Note that this handler returns a string instead of a {@link ModelAndView},
     * signifying the logical view name that should be rendered.
     *
     * @param owner the newly-bound Order object containing values from the form
     * @param result the result of the form-to-object binding process, including
     *        any errors
     * @param status allows the controller to signify that the session-scoped
     *        owner object should may be cleaned up
     *        
     * @return the name of the view to be rendered
     * 
     * @see #form()
     * @see OwnerValidator
     * @see SessionStatus
     * @see BindingResult
     * @see ViewUtils#getRedirectForOwner(Owner)
     * @see WEB-INF/jsp/owner/form.jsp
     * @see WEB-INF/jsp/owner/show.jsp
     */
    @RequestMapping(method = RequestMethod.POST)
    public String form(@ModelAttribute Owner owner, BindingResult result, SessionStatus status) {
        new OwnerValidator().validate(owner, result);
        if (result.hasErrors()) {
            return OWNER_FORM_VIEW;
        } else {
            this.clinic.storeOwner(owner);
            status.setComplete();
            return getRedirectForOwner(owner);
        }
    }

    /**
     * Custom handler for displaying the 'Find Owner' form,
     * handles GET requests for /petclinic/main/owner/find
     * <p/>
     * Note that this handler returns void instead of a
     * {@link ModelAndView}. The view name will also be determined 
     * by using the default {@link RequestToViewNameTranslator}.  
     * "/owner/find" -&gt; "owner/find".
     *
     * @see #find(Owner, BindingResult, Model)
     * @see OwnerController#createOwner(Integer)
     */
    @RequestMapping(method = RequestMethod.GET)
    public void find() {
    }

    /**
     * Custom handler for processing the submission of the 'Find Owner' form,
     * i.e.: POST requests for /petclinic/main/owner/find.
     * <p/>
     * Note that this handler returns a string instead of a {@link ModelAndView},
     * signifying the logical view name that should be rendered.
     *
     * @param owner the newly-bound Order object containing the 'last name' value
     *        from the form
     * @param result the result of the form-to-object binding process, including
     *        any errors
     * @param status allows the controller to signify that the session-scoped
     *        owner object should may be cleaned up
     *        
     * @return the name of the view to be rendered
     * 
     * @see #find()
     * @see #createOwner(Integer)
     * @see SessionStatus
     * @see BindingResult
     * @see ViewUtils#getRedirectForOwner(Owner)
     * @see WEB-INF/jsp/owner/find.jsp
     * @see WEB-INF/jsp/owner/list.jsp
     * @see WEB-INF/jsp/owner/show.jsp
     * @see src/main/resources/messages.properties
     * @see src/main/resources/messages_en.properties
     * @see src/main/resources/messages_de.properties
     */
    @RequestMapping(method = RequestMethod.POST)
    public String find(Owner owner, BindingResult result, Model model) {
        // find owners by last name
        Collection<Owner> results = this.clinic.findOwners(owner.getLastName());
        if (results.size() < 1) {
            // no owners found
            result.rejectValue("lastName", "notFound", "not found");
            return OWNER_FIND_VIEW;
        }
        if (results.size() > 1) {
            // multiple owners found
            model.addAttribute(results);
            return OWNER_LIST_VIEW;
        } else {
            // 1 owner found
            owner = results.iterator().next();
            return getRedirectForOwner(owner);
        }
    }

}
