package thumb;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jomora.io.File;
import jomora.io.image.ThumbnailFactory;

import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;

/**
 * 指定された画像の縮小版（サムネイル画像）を生成し、返却するサーブレット。
 * @author jomora@jomora.info
 * @version 2006.05.06 コメント追加(CheckStyle,FindBugs対応)
 */
public class ThumbServlet extends HttpServlet {

    /**
     * 直列化ID。
     */
    private static final long serialVersionUID = -496429029995188148L;

    /**
     * サムネイル画像のデフォルト幅(pixel)。
     */
    private static final int MAX_WIDTH = 120;
    /**
     * サムネイル画像のデフォルト高さ(pixel)。
     */
    private static final int MAX_HEIGHT = 90;

    /**
     * ファイルアップロード用のユーティリティクラス。
     */
    private transient DiskFileUpload dfu = new DiskFileUpload();
    /**
     * アップロードファイルの最大サイズ
     */
    private static final int SIZE_MAX = 30000000;
    /**
     * アップロードファイルをオンメモリで処理するか、
     * ディスクに書き込むかの境界サイズ。
     */
    private static final int SIZE_THRESHOLD = 5000000;
    /**
     * アップロードファイルの一時保管ディレクトリ
     */
    private static final String REPOSITORY_PATH =
        System.getProperty("java.io.tmpdir");
    /**
     * アップロードファイルのヘッダ文字コード
     */
    private static final String HEADER_ENCODING = "Windows-31J";

    /**
     * 初期処理。
     *  ファイルアップロード用のユーティリティクラスに対して、
     *  最大ファイルサイズ、一時保管ディレクトリ、ヘッダ文字コード等を設定する。
     */
    @Override
    public final void init() {
        dfu.setSizeMax(SIZE_MAX);
        dfu.setSizeThreshold(SIZE_THRESHOLD);
        dfu.setRepositoryPath(REPOSITORY_PATH);
        dfu.setHeaderEncoding(HEADER_ENCODING);
    }

    /**
     * upload.jspからポストされた画像ファイルのサムネイルを作成する。
     * @param request HTTPリクエスト
     * @param response HTTPレスポンス
     * @throws ServletException サーブレット例外
     * @throws IOException 入出力例外
     */
    @Override
    public final void doPost(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {

        // POSTが enctype="multipart/form-data" かどうかチェックする。
        if (!FileUploadBase.isMultipartContent(request)) {
            String message = "request does not consist of mutipartContent.";
            log(message);
            throw new ServletException(message);
        }

        // デフォルトのサムネイル画像サイズ
        int maxThumbWidth = MAX_WIDTH;
        int maxThumbHeight = MAX_HEIGHT;

        // パラメータ取り出し処理
        FileItem uploadFileItem = null;
        Map<String, String> paramMap = new HashMap<String, String>();
        try {
            List itemList = dfu.parseRequest(request);
            Iterator itemIter = itemList.iterator();
            while (itemIter.hasNext()) {
                FileItem item = (FileItem)itemIter.next();
                if (!item.isFormField()) {
                    String name = item.getName();
                    if (name != null && !name.equals("")) {
                        uploadFileItem = item;
                    }
                } else {
                    paramMap.put(item.getFieldName(), item.getString());
                }
            }
        } catch (FileUploadException fue) {
            throw new ServletException(fue);
        }

        // アップロードされたファイルがない場合は例外とする。
        if (uploadFileItem == null) {
            String message = "upload file is not specified.";
            log(message);
            throw new ServletException(message);
        }

        // サムネイル画像の最大幅を決定する。
        String mtwParam = paramMap.get("mtw");
        if (mtwParam != null) {
            try {
                maxThumbWidth = Integer.parseInt(mtwParam);
            } catch (NumberFormatException nfe) {
                String message = "param:mtw needs number format.";
                log(message);
                throw new ServletException(message);
            }
        }
        // サムネイル画像の最大高さを決定する。
        String mthParam = paramMap.get("mth");
        if (mthParam != null) {
            try {
                maxThumbHeight = Integer.parseInt(mthParam);
            } catch (NumberFormatException nfe) {
                String message = "param:mth needs number format.";
                log(message);
                throw new ServletException(message);
            }
        }

        // アップロードされた画像ファイルをサーバに保管する。
        File restoreFile = new File(System.getProperty("java.io.tmpdir")
                + java.io.File.separator
                + System.currentTimeMillis()
                + "_" + new File(uploadFileItem.getName()).getName());
        FileOutputStream fos = new FileOutputStream(restoreFile);
        fos.write(uploadFileItem.get());
        fos.close();

        // プラットフォームに画像読み込みサポートがあるかどうかチェック。
        String extension = File.getExtension(uploadFileItem.getName());
        if (!ThumbnailFactory.isSupportedReaderFormat(extension)) {
            String message = "this image format is not supported.";
            throw new ServletException(message);
        }

        InputStream iStrm = null;
        ByteArrayOutputStream baos = null;
        byte[] thumbnailBytes;
        try {
            iStrm = uploadFileItem.getInputStream();
            baos = new ByteArrayOutputStream();

            ThumbnailFactory.createThumbnail(iStrm, baos,
                maxThumbWidth, maxThumbHeight, extension);
            thumbnailBytes = baos.toByteArray();
        } finally {
            if (iStrm != null) {
                try {
                    iStrm.close();
                } catch (Exception e) {
                    // do nothing
                }
            }
            if (baos != null) {
                try {
                    baos.close();
                } catch (Exception e) {
                    // do nothing
                }
            }
            uploadFileItem.delete();
        }

        // サムネイルをクライアントへ返す方式
        String outputType = "attachment";
        if (paramMap.get("type").equals("inline")) {
            outputType = "inline";
        }
        response.addHeader("Content-Disposition", outputType
                + "; filename=\"tn_"
                + File.getName(uploadFileItem.getName()) + "\"");

        response.getOutputStream().write(thumbnailBytes);
    }

    /**
     * URLクエリ「path」でURL指定された画像ファイルのサムネイルを作成する。
     * @param request HTTPリクエスト
     * @param response HTTPレスポンス
     * @throws ServletException サーブレット例外
     * @throws IOException 入出力例外
     */
    @Override
    protected final void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        // デフォルトのサムネイル画像サイズ
        int maxThumbWidth = MAX_WIDTH;
        int maxThumbHeight = MAX_HEIGHT;

        // サムネイル画像の最大幅を決定する。
        if (request.getParameter("mtw") != null) {
            try {
                maxThumbWidth = Integer.parseInt(request.getParameter("mtw"));
            } catch (NumberFormatException nfe) {
                String message = "param:mtw needs number format.";
                log(message);
                throw new ServletException(message, nfe);
            }
        }
        // サムネイル画像の最大高さを決定する。
        if (request.getParameter("mth") != null) {
            try {
                maxThumbHeight = Integer.parseInt(request.getParameter("mth"));
            } catch (NumberFormatException nfe) {
                String message = "param:mth needs number format.";
                log(message);
                throw new ServletException(message, nfe);
            }
        }

        // 画像ファイルのURL文字列
        String pathStr = request.getParameter("path");
        String extension = File.getExtension(pathStr);

        InputStream is = null;
        ByteArrayOutputStream baos = null;
        byte[] thumbnailBytes;
        try {
            URL path = new URL(pathStr);
            is = path.openStream();
            baos = new ByteArrayOutputStream();

            ThumbnailFactory.createThumbnail(is, baos,
                    maxThumbWidth, maxThumbHeight, extension);
            thumbnailBytes = baos.toByteArray();
        } catch (MalformedURLException murle) {
            String message = "param:path needs a correct URL.";
            log(message);
            throw new ServletException(message, murle);
        } catch (FileNotFoundException fnfe) {
            String message = "param:path needs a correct URL.";
            log(message);
            throw new ServletException(message, fnfe);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (Exception e) {
                    // do nothing
                }
            }
            if (baos != null) {
                try {
                    baos.close();
                } catch (Exception e) {
                    // do nothing
                }
            }
        }

        response.getOutputStream().write(thumbnailBytes);
    }

}
