/*
 * Copyright (c) 2009 The openGion Project.
 *
 * 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.opengion.hayabusa.servlet;

import org.opengion.fukurou.util.StringUtil;
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.Base64;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.annotation.WebInitParam;

/**
 * クライアントからBase64でエンコードして送信された画像ファイルを、
 * ファイルに変換してセーブするサーブレットです。
 *
 * 想定される使い方は、クライアント側で、カメラ等で撮影された映像を
 * canvasに書き込み、それを、Base64でPOSTする感じです。
 *
 * 一般的なサーブレットと同様に、デプロイメント・ディスクリプタ WEB-INF/web.xml に、
 * servlet 要素と そのマッピング(servlet-mapping)を定義する必要があります。
 *
 *     &lt;servlet&gt;
 *         &lt;servlet-name&gt;imageSave&lt;/servlet-name&gt;
 *         &lt;servlet-class&gt;org.opengion.hayabusa.servlet.ImageSave&lt;/servlet-class&gt;
 *       &lt;init-param&gt;
 *           &lt;param-name&gt;saveDir</param-name&gt;
 *           &lt;param-value&gt;jsp/snapshot/</param-value&gt;
 *       &lt;/init-param&gt;
 *       &lt;init-param&gt;
 *           &lt;param-name&gt;debug</param-name&gt;
 *           &lt;param-value&gt;false</param-value&gt;
 *       &lt;/init-param&gt;
 *     &lt;/servlet&gt;
 *
 *     &lt;servlet-mapping&gt;
 *         &lt;servlet-name&gt;imageSave&lt;/servlet-name&gt;
 *         &lt;url-pattern&gt;/jsp/imageSave&lt;/url-pattern&gt;
 *     &lt;/servlet-mapping&gt;
 *
 * 一般には、http://サーバー:ポート/システムID/jsp/imageSave
 *    引数；img=イメージファイル
 *　　　　　dir=ディレクトリ  (初期値は、saveDir="jsp/snapshot/")
 *　　　　　file=ファイル名   (初期値は、filePtn="yyyyMMddHHmmssSSS" + ".png"(固定))
 * 形式のURL でPOSTします。
 *
 * @og.rev 7.4.2.1 (2021/05/21) 新規追加
 * @og.group その他機能
 *
 * @version  7.4
 * @author   Kazuhiko Hasegawa
 * @since    JDK11,
 */
@WebServlet(
	urlPatterns = "/jsp/imageSave" ,
	initParams  = {
		@WebInitParam( name="saveDir" , value="jsp/snapshot/" )
	}
)
public class ImageSave extends HttpServlet {
	private static final long serialVersionUID = 742120210521L ;

	private final static DateTimeFormatter YMDH = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS" );

	private String 	saveDir	= "jsp/snapshot/";
	private boolean isDebug	= false;

	/**
	 * Servlet の 初期値設定を行います。
	 *
	 * WEB-INF/web.xml ファイルで、&lt;servlet&gt; タグ内で初期値設定を行います。
	 *       &lt;init-param&gt;
	 *           &lt;param-name&gt;saveDir</param-name&gt;
	 *           &lt;param-value&gt;jsp/snapshot/</param-value&gt;
	 *       &lt;/init-param&gt;
	 *
	 * @param	config	ServletConfigオブジェクト
	 */
	@Override
	public void init( final ServletConfig config ) throws ServletException {
		super.init( config );

		saveDir = StringUtil.nval( config.getInitParameter("saveDir") , saveDir );

		// boolean の StringUtil.nval は厳密ﾁｪｯｸするので使わない。
		isDebug = Boolean.parseBoolean( config.getInitParameter( "debug" ) );
	}

	/**
	 * GET メソッドが呼ばれたときに実行します。
	 *
	 * 処理は、doPost へ振りなおしています。
	 *
	 * @param	request	HttpServletRequestオブジェクト
	 * @param	response	HttpServletResponseオブジェクト
	 *
	 * @og.rev 7.4.2.1 (2021/05/21) 新規追加
	 *
	 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	public void doGet( final HttpServletRequest request, final HttpServletResponse response )
							throws ServletException, IOException {
		doPost( request,response );
	}

	/**
	 * POST メソッドが呼ばれたときに実行します。
	 *
	 * @param	request	HttpServletRequestオブジェクト
	 * @param	response	HttpServletResponseオブジェクト
	 *
	 * @og.rev 7.4.2.1 (2021/05/21) 新規追加
	 *
	 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
	 * @throws IOException 入出力エラーが発生したとき
	 */
	@Override
	public void doPost( final HttpServletRequest request, final HttpServletResponse response )
							throws ServletException, IOException {

		// boolean の StringUtil.nval は厳密ﾁｪｯｸするので使わない。
		final String debugPrm = request.getParameter( "debug" ) ;
		final boolean debug = StringUtil.isNull( debugPrm )
									? isDebug								// 未指定なら、初期値を使う
									: Boolean.parseBoolean( debugPrm );		// "true"以外はfalse になる。

		String data = request.getParameter( "img" );
		if( StringUtil.isNotNull( data ) ) {
			// Javascriptのcanvas.toDataURL()関数から派生したデータを保存したい場合は、
			// 空白をプラスに変換する必要があります。そうしないと、デコードされたデータが破損します。
			data = data.replace(' ','+');

			// 相対パスを絶対パスに変換。ファイルセパレータも正規化されています。
			final String dir = HybsSystem.url2dir( StringUtil.nval( request.getParameter( "dir" ),saveDir ) );
			if( debug ) { System.out.println( "dir=" + dir ); }

			// ファイル名が無ければ、現在時刻.png
			String file = request.getParameter( "file" );
			if( StringUtil.isNull( file ) ) {
				final LocalDateTime nowDateTime = LocalDateTime.now();
				file = nowDateTime.format( YMDH ) + ".png";
			}

			if( debug ) { System.out.println( "file=" + file ); }

			// Base64をデコードしてファイルに戻す。
			final byte[] bytes = Base64.getDecoder().decode(data);
			try( ByteArrayInputStream input = new ByteArrayInputStream(bytes) ) {
				final BufferedImage image = ImageIO.read(input);
				final File output = new File( dir, file );
				final File parent = output.getParentFile();
				if( parent != null && !parent.exists() ) {			// parent は null があり得る。存在しない場合のみ。
					parent.mkdirs();
				}
				ImageIO.write(image, "png", output);
				if( debug ) { System.out.println( "output=" + output.getAbsolutePath() ); }
			}
			catch( final Throwable th ) {
				final String errMsg = "Base64 デコード処理が失敗しました。" ;
				throw new HybsSystemException( errMsg,th );
			}
		}
	}
}
