package me.chyxion.tigon.webmvc;

import java.io.File;
import org.slf4j.Logger;
import java.util.Arrays;
import java.net.URLEncoder;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.apache.commons.lang3.CharEncoding;
import org.springframework.core.MethodParameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor;

/**
 * @author Shaun Chyxion <br>
 * chyxion@163.com <br>
 * Mar 19, 2017 11:32:22
 */
public class TigonReturnValueHandler
    implements HandlerMethodReturnValueHandler {
    private static final Logger log = 
        LoggerFactory.getLogger(TigonReturnValueHandler.class);

    @Autowired
    private JSONViewConfig jsonViewConfig;
    private HttpEntityMethodProcessor httpEntityProcessor =
        new HttpEntityMethodProcessor(Arrays.<HttpMessageConverter<?>>asList(
            new ResourceHttpMessageConverter()));
    
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return true;
    }
   
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleReturnValue(Object returnValue,
            MethodParameter returnType, 
            ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest) throws Exception {
        log.debug("Return Value Handler, Handle Return Value [{}], Return Type [{}].", 
                returnValue, returnType);
        if (!handleResource(returnValue, 
                returnType, mavContainer, webRequest)) {
            JSONViewDataModel model = null;
            if (returnValue instanceof JSONViewDataModel) {
                log.debug("Controller Return Value Is JSONViewDataModel, Return.");
                model = (JSONViewDataModel) returnValue;
            }
            else {
                log.debug("Wrap Controller Return Value To Data Model.");
                model = new JSONViewDataModel(returnValue);
            }
            mavContainer.setView(new JSONView(model, jsonViewConfig));
        }
    }

    /**
     * handle resource
     * @param returnValue return value
     * @param returnType return type
     * @param mavContainer mvc container
     * @param webRequest web request
     * @return true if handle resource
     * @throws Exception on error caused
     */
    private boolean handleResource(Object returnValue,
        MethodParameter returnType, 
        ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest) throws Exception {

        ResourceModel resourceModel = null;
        HttpEntity<?> entity = null;
        if (returnValue instanceof ResourceModel) {
            log.debug("Found Download Model Return.");
            resourceModel = (ResourceModel) returnValue;
        }
        else if (returnValue instanceof HttpEntity) {
            log.debug("Found HttpEntity Return.");
            entity = (HttpEntity<?>) returnValue;
        }
        else if (returnValue instanceof File) {
            log.debug("Found File [{}] Return.", returnValue);
            resourceModel = new ResourceModel((File) returnValue, null, null);
        }
         else if (returnValue instanceof Resource) {
             log.debug("Found Resouce [{}] Return.", returnValue);
             resourceModel = new ResourceModel((Resource) returnValue, null, 0, null);
         }
        else if (returnValue instanceof byte[]) {
            log.debug("Found Byte Array Return.");
            resourceModel = new ResourceModel((byte[]) returnValue, null, null);
        }
        // assemble http entity
        if (resourceModel != null) {
            HttpHeaders headers = new HttpHeaders();
            String name = resourceModel.getName();
            if (StringUtils.isNotBlank(name)) {
                log.info("Found Resource Name [{}], Set Content-Disposition Header.", name);
                name = URLEncoder.encode(name, CharEncoding.UTF_8);
                StringBuilder sbDispo = 
                    new StringBuilder("attachment; filename=\"")
                    .append(name)
                    .append("\"; filename*=utf-8''")
                    .append(name);
                headers.set(HttpHeaders.CONTENT_DISPOSITION, 
                    sbDispo.toString());
            }
            headers.setContentType(
                MediaType.valueOf(resourceModel.getContentType()));
            headers.setContentLength(resourceModel.getContentLength());
            entity = new ResponseEntity<Resource>(
                resourceModel.getResource(), headers, HttpStatus.OK);
        }
        boolean handled = false;
        if (entity != null) {
            httpEntityProcessor.handleReturnValue(entity,
                returnType, mavContainer, webRequest);
            handled = true;
        }
        return handled;
    }
}
