/*
 * $Id: FileDownloadResponse.java 220 2007-07-16 10:32:15Z sugimotokenichi $
 * Copyright (C) 2004-2006 SUGIMOTO Ken-ichi
 * 作成日: 2006/05/16
 */
package feat2.impl;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.BCodec;
import org.apache.commons.codec.net.URLCodec;

import feat2.ApplicationException;
import feat2.CommandContext;
import feat2.FeatRuntimeException;
import feat2.FileDownload;
import feat2.FileUploadException;
import feat2.Processor;
import feat2.PropertyAccessException;
import feat2.Response;
import feat2.TemplateParsingException;
import feat2.config.FileConfig;

/**
 * ファイルダウンロードのレスポンス。
 * @author SUGIMOTO Ken-ichi
 */
public class FileDownloadResponse implements Response {

    private static final int
        MSIE = 0,
        FIREFOX = 1,
        MOZILLA = 2,
        OPERA = 3,
        SAFARI = 4,
        MSIE_MAC = 5;

    private FileDownload downloadSource;
    private FileConfig config;


    public FileDownloadResponse(FileDownload downloadSource, FileConfig config) {
        this.downloadSource = downloadSource;
        this.config = config;
    }


    public String output(CommandContext ctx) throws IOException,
            PropertyAccessException, TemplateParsingException,
            ServletException, ApplicationException, FileUploadException {

        // 入力値

        Map inputValues = Processor.processInputs(config, ctx);

        // バリデーション

        if ( Processor.processValidations(downloadSource, inputValues, config, ctx) ) {

            // インジェクション

            Processor.processInjection(downloadSource, inputValues);

            // 出力

            setHeader(ctx);
            write(ctx);

        }

        else {

            return config.getErrorResponse().getValue();

        }

        return null;

    }


    private int getUaType(CommandContext ctx) {

        String ua = ctx.getRequest().getHeader("User-Agent");
        int uaType = MSIE;

        if ( ua != null ) {

            if ( ua.indexOf("Opera") > -1 )
                uaType = OPERA;
            else if ( ua.indexOf("Firefox") > -1 )
                uaType = FIREFOX;
            else if ( ua.indexOf("Safari") > -1 )
                uaType = SAFARI;
            else if ( ua.indexOf("Gecko") > -1 )
                uaType = MOZILLA;
            else if ( ua.indexOf("MSIE") > -1 ) {
                uaType = MSIE;
                if ( ua.indexOf("Mac") > -1 )
                    uaType = MSIE_MAC;
            }

        }

        return uaType;

    }

    private void setHeader(CommandContext ctx) {

        HttpServletResponse resp = ctx.getResponse();

        String fileName = downloadSource.getFileName();
        if ( fileName == null )
            fileName = config.getFilename();

        String mimeType = downloadSource.getMimeType();
        if ( mimeType == null )
            mimeType = config.getContentType();

        if ( fileName != null ) {

            try {

                int ua = getUaType(ctx);

                if ( ua == MSIE ) {

                    URLCodec enc = new URLCodec("UTF-8");
                    String encodedFileName = enc.encode(fileName);
                    resp.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName +"\"");

                }

                else if ( ua == FIREFOX || ua == MOZILLA ) {

                    BCodec enc = new BCodec("UTF-8");
                    String encodedFileName = enc.encode(fileName);
                    resp.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName +"\"");

                }

                else {

                    resp.setHeader("Content-Disposition", "attachment;filename=\"" + fileName +"\"");

                }

            }
            catch (EncoderException ex) {
                throw new FeatRuntimeException(ex);
            }

        }

        if ( mimeType != null )
            resp.setContentType(mimeType);

    }

    private void write(CommandContext ctx) throws IOException, ApplicationException {

        try {
            InputStream is = new BufferedInputStream(downloadSource.getInputStream(ctx));

            try {

                OutputStream os = ctx.getResponse().getOutputStream();

                byte[] buf = new byte[512];
                int size;
                while( (size = is.read(buf)) > 0 ) {

                    os.write(buf, 0, size);

                }

            }
            finally {
                is.close();
            }

        }
        catch (IOException ex) {
            throw ex;
        }
        catch (Exception ex) {
            throw new ApplicationException(ex);
        }

    }


}
