/*
 * 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.taglib;

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

import org.opengion.fukurou.util.QrcodeImage;
import static org.opengion.fukurou.util.QrcodeImage.ErrCrct;
//import static org.opengion.fukurou.util.QrcodeImage.EncMode;					// 8.4.1.0 (2023/02/10) Delete
import static org.opengion.fukurou.util.StringUtil.nval;

import java.util.Locale;

/**
 * QRｺｰﾄﾞに対応したｲﾒｰｼﾞﾌｧｲﾙを作成するﾀｸﾞです。
 *
 * QRｺｰﾄﾞで表示できる文字数は、ﾊﾞｰｼﾞｮﾝ、ｴﾝｺｰﾄﾞﾓｰﾄﾞ、ｴﾗｰ訂正ﾚﾍﾞﾙ に依存します。
 * また、ｲﾒｰｼﾞの大きさは、文字数と1ｾﾙ辺りのﾋﾟｸｾﾙに依存します。
 * fileURLは、通常、ｼｽﾃﾑﾘｿｰｽのFILE_URL(=filetemp)なので、fileURL="{&#064;USER.ID}" と
 * するのが一般的です。
 * ﾌｧｲﾙ名は、初期値:rqcode ですが、拡張子はimageType(初期値:PNG)を小文字化して追加します。
 * 拡張子が付いている場合は、そのまま使用されます。
 * また、同一ﾌｧｲﾙ名の場合、ﾌﾞﾗｳｻﾞｷｬｯｼｭのため、画像が更新されないことがあるため
 * imgﾀｸﾞのsrc属性に、ｷｬｯｼｭの無効化のための引数を追加しています。
 *
 * @og.formSample
 * ●形式：&lt;og:qrCode fileURL="{&#064;USER.ID}" &gt;
 *             ｴﾝｺｰﾄﾞする文字列
 *         &lt;/og:qrCode &gt;
 * ●body：あり(EVAL_BODY_BUFFERED:BODYを評価し、{&#064;XXXX} を解析します)
 *
 * または、
 *
 * ●形式：&lt;og:qrCode value="ｴﾝｺｰﾄﾞする文字列" /&gt;
 *
 * ●Tag定義：
 *   &lt;og:qrCode
 *       value              【TAG】ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述)
 *       version            【TAG】ﾊﾞｰｼﾞｮﾝ (2から40の整数)(初期値:5)
 *  <del>encodeMode         【TAG】ｴﾝｺｰﾄﾞﾓｰﾄﾞ('N':数字ﾓｰﾄﾞ 'A':英数字ﾓｰﾄﾞ 'B':8bit byteﾓｰﾄﾞ)(初期値:B)</del> 8.4.1.0 (2023/02/10) Delete
 *       errCorrect         【TAG】ｴﾗｰ訂正ﾚﾍﾞﾙ ('L','M','Q','H')(初期値:M)
 *       imageType          【TAG】ｲﾒｰｼﾞﾌｧｲﾙ形式(PNG/JPEG)(初期値:PNG)
 *       pixel              【TAG】1ｾﾙ辺りの塗りつぶしﾋﾟｸｾﾙ(初期値:3)
 *       fileURL            【TAG】QRｲﾒｰｼﾞﾌｧｲﾙを出力するﾃﾞｨﾚｸﾄﾘ(初期値:FILE_URL)
 *       filename           【TAG】QRｲﾒｰｼﾞﾌｧｲﾙ名 (初期値:rqcode)
 *       textEncode         【TAG】ﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ(初期値:Charset.defaultCharset()) 7.2.3.0 (2020/04/10)
 *       caseKey            【TAG】このﾀｸﾞ自体を利用するかどうかの条件ｷｰを指定します(初期値:null)
 *       caseVal            【TAG】このﾀｸﾞ自体を利用するかどうかの条件値を指定します(初期値:null)
 *       caseNN             【TAG】指定の値が、null/ｾﾞﾛ文字列 でない場合(Not Null=NN)は、このﾀｸﾞは使用されます(初期値:判定しない)
 *       caseNull           【TAG】指定の値が、null/ｾﾞﾛ文字列 の場合は、このﾀｸﾞは使用されます(初期値:判定しない)
 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このﾀｸﾞは使用されます(初期値:判定しない)
 *       debug              【TAG】ﾃﾞﾊﾞｯｸﾞ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   &gt;   ... Body ...
 *   &lt;/og:qrCode&gt;
 *
 * @og.rev 7.2.1.0 (2020/03/13) 新規作成
 * @og.group 画面表示
 *
 * @version	7.2
 * @author	 Kazuhiko Hasegawa
 * @since	JDK11.0,
 */
public class QRcodeTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "8.4.1.0 (2023/02/10)" ;
	private static final long serialVersionUID = 841020230210L ;

	/** ｲﾒｰｼﾞに表示させる alt属性の文字数制限 */
	private static final int MAX_ALT_SIZE = 50;

	/** ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述) */
	private String	value		;
	/** ﾊﾞｰｼﾞｮﾝ (2から40の整数) 初期値:5 */
	private int		version		= QrcodeImage.DEF_VERSION;
//	private EncMode	encMode		= EncMode.DEF;							// ｴﾝｺｰﾄﾞﾓｰﾄﾞ('N':数字ﾓｰﾄﾞ 'A':英数字ﾓｰﾄﾞ 'B':8bit byteﾓｰﾄﾞ)(初期値:B) 8.4.1.0 (2023/02/10) Delete
	/** ｴﾗｰ訂正ﾚﾍﾞﾙ ('L','M','Q','H') 初期値:M */
	private ErrCrct	errCrct		= ErrCrct.DEF;
	/** ｲﾒｰｼﾞﾌｧｲﾙ形式(PNG/JPEG) 初期値:PNG */
	private String	imgType		= QrcodeImage.IMAGE_TYPE;
	/** 1ｾﾙ辺りの塗りつぶしﾋﾟｸｾﾙ 初期値:3 */
	private int		pixel		= QrcodeImage.PIXEL;
	/** QRｲﾒｰｼﾞﾌｧｲﾙを出力するﾃﾞｨﾚｸﾄﾘ 初期値:FILE_URL/{&#064;USER.ID} */
	private String	fileURL		= HybsSystem.sys( "FILE_URL" );
	/** QRｲﾒｰｼﾞﾌｧｲﾙ名 (初期値:rqcode) */
	private String	filename	= "rqcode";
	/** ﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ 初期値:Charset.defaultCharset() */
	private String	textEncode	;										// 7.2.3.0 (2020/04/10)

	/**
	 * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
	 *
	 * @og.rev 7.2.1.0 (2020/03/13) 新規作成
	 */
	public QRcodeTag() { super(); }			// これも、自動的に呼ばれるが、空のﾒｿｯﾄﾞを作成すると警告されるので、明示的にしておきます。

	/**
	 * Taglibの開始ﾀｸﾞが見つかったときに処理する doStartTag() を ｵｰﾊﾞｰﾗｲﾄﾞします。
	 *
	 * @return	後続処理の指示( EVAL_BODY_BUFFERED )
	 */
	@Override
	public int doStartTag() {
		// caseKey、caseVal 属性対応
		if( useTag() ) {
			if( value == null || value.length() <= 0 ) {
				return EVAL_BODY_BUFFERED ;		// Body を評価する
			}
		}
		return SKIP_BODY ;						// Body を評価しない
	}

	/**
	 * Taglibのﾀｸﾞ本体を処理する doAfterBody() を ｵｰﾊﾞｰﾗｲﾄﾞします。
	 *
	 * @return	後続処理の指示(SKIP_BODY)
	 */
	@Override
	public int doAfterBody() {
		value = getBodyString();

		return SKIP_BODY ;
	}

	/**
	 * Taglibの終了ﾀｸﾞが見つかったときに処理する doEndTag() を ｵｰﾊﾞｰﾗｲﾄﾞします。
	 *
	 * @og.rev 7.2.3.0 (2020/04/10) textEncode byteﾓｰﾄﾞ時のﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ追加
	 * @og.rev 8.4.1.0 (2023/02/10) QRｺｰﾄﾞを swetake から ZXing への置換(encodeMode廃止)
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();
		// caseKey、caseVal 属性対応
		if( useTag() ) {
			if( filename.indexOf( '.' ) < 0 ) {				// 拡張子が存在しない
				filename = filename + "." + imgType.toLowerCase(Locale.JAPAN);
			}

			try {
				final String saveFile = HybsSystem.url2dir( fileURL,filename );

				final QrcodeImage qrcode = new QrcodeImage();
//				qrcode.init( value,saveFile,version,encMode,errCrct,imgType,pixel );
//				qrcode.init( value,saveFile,version,encMode,errCrct,imgType,pixel,textEncode );	// 7.2.3.0 (2020/04/10)
				qrcode.init( value,saveFile,version,errCrct,imgType,pixel,textEncode );			// 8.4.1.0 (2023/02/10)
				qrcode.saveImage();
			}
			catch( final Throwable th ) {
				final String errMsg = "QRコードが作成できませんでした。"					+ CR
								+ "指定のパラメータが正しいかどうかご確認ください。"		+ CR
								+ "  version    = [" + version	 + "] バージョン (2から40の整数)"						+ CR
//								+ "  encodeMode = [" + encMode	 + "] エンコードモード('N':数字 'A':英数字'B':byte)"	+ CR	// 8.4.1.0 (2023/02/10) Delete
								+ "  errCorrect = [" + errCrct	 + "] エラー訂正レベル ('L','M','Q','H')"	+ CR
								+ "  imageType  = [" + imgType	 + "] イメージファイル形式(PNG/JPEG)"		+ CR
								+ "  pixel      = [" + pixel	 + "] 1セル辺りの塗りつぶしピクセル"		;
				throw new HybsSystemException( errMsg,th );
			}

			filename = filename + "?val=" + System.currentTimeMillis() ;		// 同じﾌｧｲﾙ名の場合のｷｬｯｼｭの無効化
			final String src = StringUtil.urlAppend( getContextPath() , fileURL , filename );
			final String alt = value.substring( 0,Math.min( value.length() , MAX_ALT_SIZE ) );

			// 作成された QRｺｰﾄﾞのｲﾒｰｼﾞを表示します。
			final String img = XHTMLTag.img(
					new Attributes()
						.set( "src"		, src )
						.set( "alt"		, alt )
						.set( "title"	, alt )
			);

			jspPrint( img );
		}
		return EVAL_PAGE ;
	}

	/**
	 * ﾀｸﾞﾘﾌﾞｵﾌﾞｼﾞｪｸﾄをﾘﾘｰｽします。
	 * ｷｬｯｼｭされて再利用されるので、ﾌｨｰﾙﾄﾞの初期設定を行います。
	 *
	 * @og.rev 7.2.3.0 (2020/04/10) textEncode byteﾓｰﾄﾞ時のﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ追加
	 * @og.rev 8.4.1.0 (2023/02/10) QRｺｰﾄﾞを swetake から ZXing への置換(encodeMode廃止)
	 */
	@Override
	protected void release2() {
		super.release2();
		value		= null;										// ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述)
		version		= QrcodeImage.DEF_VERSION;					// ﾊﾞｰｼﾞｮﾝ (2から40の整数)(初期値:5)
//		encMode		= EncMode.DEF;								// ｴﾝｺｰﾄﾞﾓｰﾄﾞ('N':数字ﾓｰﾄﾞ 'A':英数字ﾓｰﾄﾞ 'B':8bit byteﾓｰﾄﾞ)(初期値:B)	// 8.4.1.0 (2023/02/10) Delete
		errCrct		= ErrCrct.DEF;								// ｴﾗｰ訂正ﾚﾍﾞﾙ ('L','M','Q','H')(初期値:M)
		imgType		= QrcodeImage.IMAGE_TYPE;					// ｲﾒｰｼﾞﾌｧｲﾙ形式(PNG/JPEG)(初期値:PNG)
		pixel		= QrcodeImage.PIXEL;						// 1ｾﾙ辺りの塗りつぶしﾋﾟｸｾﾙ(初期値:3)
		fileURL		= HybsSystem.sys( "FILE_URL" );				// QRｲﾒｰｼﾞﾌｧｲﾙを出力するﾃﾞｨﾚｸﾄﾘ(初期値:FILE_URL/{@USER.ID})
		filename	= "rqcode";									// QRｲﾒｰｼﾞﾌｧｲﾙ名 (初期値:rqcode)
		textEncode	= null;										// 7.2.3.0 (2020/04/10) byteﾓｰﾄﾞ時のﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ
	}

	/**
	 * 【TAG】ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述)を指定します。
	 *
	 * @og.tag
	 * ｴﾝｺｰﾄﾞする文字列のﾊﾞｲﾄ数は、ﾊﾞｰｼﾞｮﾝ、ｴﾝｺｰﾄﾞﾓｰﾄﾞ、ｴﾗｰ訂正ﾚﾍﾞﾙに依存します。
	 * また、ｲﾒｰｼﾞの大きさは、それらﾌﾟﾗｽ1ｾﾙ辺りのﾋﾟｸｾﾙも影響します。
	 *
	 * @param	val	ｴﾝｺｰﾄﾞする文字列(または、BODY部に記述)
	 */
	public void setValue( final String val ) {
		value = nval( getRequestParameter( val ),value );
	}

	/**
	 * 【TAG】ﾊﾞｰｼﾞｮﾝ (2から40の整数)を指定します(初期値:5)。
	 *
	 * @og.tag
	 * ｴﾝｺｰﾄﾞする文字列のﾊﾞｲﾄ数は、ｴﾗｰ訂正ﾚﾍﾞﾙ、ﾊﾞｰｼﾞｮﾝ に依存します。
	 * 文字列のﾊﾞｲﾄ数を増やす場合は、ﾊﾞｰｼﾞｮﾝを適切に設定します。
	 *
	 * @param	ver	ﾊﾞｰｼﾞｮﾝ
	 */
	public void setVersion( final String ver ) {
		version = nval( getRequestParameter( ver ),version );
	}

//	/**
//	 * 【TAG】ｴﾝｺｰﾄﾞﾓｰﾄﾞ('N':数字ﾓｰﾄﾞ 'A':英数字ﾓｰﾄﾞ　'B':8bit byteﾓｰﾄﾞ)を指定します(初期値:B)。
//	 *
//	 * @og.tag
//	 * ｴﾝｺｰﾄﾞする文字列の種類に応じて設定します。
//	 * 日本語等を含む場合は、'B':8bit byteﾓｰﾄﾞ にしてください。
//	 *
//	 * @og.rev 8.4.1.0 (2023/02/10) QRｺｰﾄﾞを swetake から ZXing への置換(encodeMode廃止)
//	 *
//	 * @param	mode	ｴﾝｺｰﾄﾞﾓｰﾄﾞ
//	 */
//	public void setEncodeMode( final String mode ) {
//		final String em = nval( getRequestParameter( mode ),null );
//		if( em != null ) {
//			encMode = EncMode.get( em.charAt(0) );
//		}
//	}

	/**
	 * 【TAG】ｴﾗｰ訂正ﾚﾍﾞﾙ ('L','M','Q','H')を指定します(初期値:M)。
	 *
	 * @og.tag
	 * ｴﾝｺｰﾄﾞする文字列のﾊﾞｲﾄ数は、ｴﾗｰ訂正ﾚﾍﾞﾙ、ﾊﾞｰｼﾞｮﾝ に依存します。
	 * 通常、初期値のままで問題ありません。
	 *
	 * @param	crct	ｴﾗｰ訂正ﾚﾍﾞﾙ
	 */
	public void setErrCorrect( final String crct ) {
		final String ec = nval( getRequestParameter( crct ),null );
		if( ec != null ) {
			errCrct = ErrCrct.get( ec.charAt(0) );
		}
	}

	/**
	 * 【TAG】ｲﾒｰｼﾞﾌｧｲﾙ形式(PNG/JPEG)を指定します(初期値:PNG)。
	 *
	 * @og.tag
	 * QRｺｰﾄﾞのｲﾒｰｼﾞﾌｧｲﾙの形式を指定します。
	 * 拡張子は自動的にｲﾒｰｼﾞﾌｧｲﾙ形式(の小文字)がｾｯﾄされます。
	 *
	 * @param	type	ｲﾒｰｼﾞﾌｧｲﾙ形式
	 */
	public void setImageType( final String type ) {
		imgType = nval( getRequestParameter( type ),imgType );
	}

	/**
	 * 【TAG】1ｾﾙ辺りの塗りつぶしﾋﾟｸｾﾙを指定します(初期値:3)。
	 *
	 * @og.tag
	 * QRｺｰﾄﾞのｲﾒｰｼﾞﾌｧｲﾙの形式を指定します。
	 * 拡張子は自動的にｲﾒｰｼﾞﾌｧｲﾙ形式(の小文字)がｾｯﾄされます。
	 *
	 * @param	px	ﾋﾟｸｾﾙ数
	 */
	public void setPixel( final String px ) {
		pixel = nval( getRequestParameter( px ),pixel );
	}

	/**
	 * 【TAG】QRｲﾒｰｼﾞﾌｧｲﾙを出力するﾃﾞｨﾚｸﾄﾘを指定します(初期値:FILE_URL)。
	 *
	 * @og.tag
	 * この属性で指定されるﾃﾞｨﾚｸﾄﾘに、QRｲﾒｰｼﾞﾌｧｲﾙをｾｰﾌﾞします。
	 * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、２文字目が、
	 * ":" (Windows)の場合は、指定のURLそのままのﾃﾞｨﾚｸﾄﾘに、そうでない場合は、
	 * FILE_URL 属性で指定のﾌｫﾙﾀﾞの下に、ﾌｫﾙﾀﾞを作成します。
	 * 初期値は、FILE_URL になるため、通常は{&#064;USER.ID}を指定する必要があります。
	 *
	 * @param	url	保存先ﾃﾞｨﾚｸﾄﾘ名
	 * @see		org.opengion.hayabusa.common.SystemData#FILE_URL
	 */
	public void setFileURL( final String url ) {
		final String furl = nval( getRequestParameter( url ),null );
		if( furl != null ) {
			fileURL = StringUtil.urlAppend( fileURL,furl );
		}
	}

	/**
	 * 【TAG】QRｲﾒｰｼﾞﾌｧｲﾙ名をｾｯﾄします(初期値:rqcode)。
	 *
	 * @og.tag
	 * ﾌｧｲﾙを作成するときのﾌｧｲﾙ名をｾｯﾄします。
	 * ﾌｧｲﾙ名の拡張子は、imageType属性の小文字を追加します。
	 * 拡張子付きで指定した場合(ﾌｧｲﾙ名に、ﾋﾟﾘｵﾄﾞを含む場合)は、
	 * そのままの値を使用します。
	 *
	 * @param	fname	ﾌｧｲﾙ名
	 */
	public void setFilename( final String fname ) {
		filename = nval( getRequestParameter( fname ),filename );
	}

	/**
	 * 【TAG】ﾃｷｽﾄ文字ｴﾝｺｰﾄﾞをｾｯﾄします(初期値:環境依存)。
	 *
	 * @og.tag
	 * ﾃｷｽﾄのｴﾝｺｰﾄﾞの指定がない場合は、ﾌﾟﾗｯﾄﾌｫｰﾑ依存のﾃﾞﾌｫﾙﾄの Charset です。
	 * java.nio.charset.Charset#defaultCharset()
	 * QRｺｰﾄﾞで、機種依存文字(①など)は、Windows-31J を指定しても読み取り側が対応していません。
	 * その場合は、UTF-8 を指定します。(必要なﾊﾞｲﾄ数は当然増えます)
	 *
	 * 通常、何も指定しないか、UTF-8 を指定するかのどちらかになります。
	 *
	 * @og.rev 7.2.3.0 (2020/04/10) textEncode byteﾓｰﾄﾞ時のﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ追加
	 *
	 * @param	txtEnc	ﾃｷｽﾄ文字ｴﾝｺｰﾄﾞ
	 */
	public void setTextEncode( final String txtEnc ) {
		textEncode = nval( getRequestParameter( txtEnc ),textEncode );
	}

	/**
	 * このｵﾌﾞｼﾞｪｸﾄの文字列表現を返します。
	 * 基本的にﾃﾞﾊﾞｯｸﾞ目的に使用します。
	 *
	 * @return	このｸﾗｽの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"			, VERSION			)
				.println( "value"			, value				)
				.println( "version"			, version			)
//				.println( "encMode"			, encMode			)	// 8.4.1.0 (2023/02/10) Delete
				.println( "errCrct"			, errCrct			)
				.println( "imgType"			, imgType			)
				.println( "pixel"			, pixel				)
				.println( "fileURL"			, fileURL			)
				.println( "filename"		, filename			)
				.fixForm().toString() ;
	}
}
