/*
 * Copyright 2004-2005 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.codehaus.groovy.grails.scaffolding;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.grails.web.binding.GrailsDataBinder;
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequest;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.io.Serializable;

/**
 * Default implementation of the ScaffoldRequestHandler interface. Uses a ScaffoldDomain to handle
 * basic CRUD operations.
 * 
 * @author Graeme Rocher
 * @since 0.1
 *
 * 30 Nov 2005
 */
public class DefaultScaffoldRequestHandler implements ScaffoldRequestHandler {

    private static final Log LOG = LogFactory.getLog(DefaultScaffoldRequestHandler.class);

    private static final String PARAM_MAX = "max";
    private static final String PARAM_OFFSET = "offset";
    private static final String PARAM_ID = "id";
    private static final String PARAM_SORT = "sort";
    private static final String PARAM_ORDER = "order";

    private ScaffoldDomain domain;

    public void setScaffoldDomain(ScaffoldDomain domain) {
        this.domain = domain;
    }

    public ScaffoldDomain getScaffoldDomain() {
        return this.domain;
    }

    public Map handleList(HttpServletRequest request,
                          HttpServletResponse response) {
        int max = 10;
        int offset = 0;
        String maxParam = request.getParameter(PARAM_MAX);
        String offsetParam = request.getParameter(PARAM_OFFSET);

        if(!StringUtils.isBlank(maxParam)) {
            try {
                max = Integer.parseInt(maxParam);
            }
            catch(NumberFormatException nfe) {
                LOG.warn("[ScaffoldRequestHandler] Error parsing max parameter ["+maxParam+"] for request", nfe);
            }
        }
        if(!StringUtils.isBlank(offsetParam)) {
            try {
                offset = Integer.parseInt(offsetParam);
            }
            catch(NumberFormatException nfe) {
                LOG.warn("[ScaffoldRequestHandler] Error parsing offset parameter ["+offsetParam+"] for request", nfe);
            }
        }
        if(LOG.isTraceEnabled()) {
            LOG.trace("[ScaffoldRequestHandler] Executing [list] for max ["+max+"] and offset ["+offset+"]");
        }
        Map model = new HashMap();
        model.put( domain.getPluralName(), domain.list(max, offset, request.getParameter(PARAM_SORT), request.getParameter(PARAM_ORDER)) );

        if(LOG.isTraceEnabled()) {
            LOG.trace("[ScaffoldRequestHandler] Returned model ["+model+"] from domain method [list]");
        }

        return model;
    }

    public Map handleShow(HttpServletRequest request,
                          HttpServletResponse response, ScaffoldCallback callback) {

        Serializable id = getRequestId();
        if(id==null) {
            LOG.debug("[ScaffoldRequestHandler] No ID parameter ["+id+"] for request [show]");
            callback.setInvoked(false);
            return Collections.EMPTY_MAP;
        }


        Map model = new HashMap();
        Object domainObject = domain.get(id);
        model.put(domain.getSingularName(), domainObject);
        callback.setInvoked(true);

        return model;
    }

    private Serializable getRequestId() {
        GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.currentRequestAttributes();
        return (Serializable)webRequest.getParams().get(PARAM_ID);
    }

    public Map handleDelete(HttpServletRequest request,
                            HttpServletResponse response, ScaffoldCallback callback) {

        Serializable id = getRequestId();
        if(id==null) {
            LOG.debug("[ScaffoldRequestHandler] No ID parameter ["+id+"] for request [delete]");
            callback.setInvoked(false);
            return Collections.EMPTY_MAP;
        }


        Map model = new HashMap();
        Object domainObject = domain.get(id);
        model.put(domain.getSingularName(), domainObject);

        if(domainObject != null) {
            domain.delete(id);
            callback.setInvoked(true);
        }

        return model;
    }

    public Map handleCreate(HttpServletRequest request, HttpServletResponse reponse, ScaffoldCallback callback) {
       Object domainObject = domain.newInstance();
        ServletRequestDataBinder dataBinder = GrailsDataBinder.createBinder(domainObject, domain.getName(),request);
        dataBinder.bind(request);

        Map model = new HashMap();
        model.put( domain.getSingularName(), domainObject);

        return model;
    }

    public Map handleSave(HttpServletRequest request,
                          HttpServletResponse reponse, ScaffoldCallback callback) {

        Object domainObject = domain.newInstance();
        ServletRequestDataBinder dataBinder = GrailsDataBinder.createBinder(domainObject, domain.getName(),request);
        dataBinder.bind(request);

        Map model = new HashMap();
        model.put( domain.getSingularName(), domainObject);

        if( this.domain.save(domainObject,callback) ) {
            BeanWrapper domainBean = new BeanWrapperImpl(domainObject);
            Object identity = domainBean.getPropertyValue(domain.getIdentityPropertyName());
            model.put( PARAM_ID, identity );
            callback.setInvoked(true);
        }
        return model;
    }


    public Map handleUpdate(HttpServletRequest request, HttpServletResponse reponse, ScaffoldCallback callback) {
        Serializable id = getRequestId();
        if(id==null) {
            LOG.debug("[ScaffoldRequestHandler] No ID parameter ["+id+"] for request [update]");
            callback.setInvoked(false);
            return Collections.EMPTY_MAP;
        }

        Object domainObject = this.domain.get(id);
        ServletRequestDataBinder dataBinder = GrailsDataBinder.createBinder(domainObject, domain.getName(),request);
        dataBinder.bind(request);

        Map model = new HashMap();
        model.put(	this.domain.getSingularName(), domainObject);
        model.put( PARAM_ID, id );
        // execute update
        if(this.domain.update(domainObject,callback))
        	callback.setInvoked(true);

        return model;
    }

    public Map handleFind(HttpServletRequest request,
                          HttpServletResponse reponse) {
        // TODO Auto-generated method stub
        return null;
    }

}
