/*
 * 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.fukurou.taglet;		// 7.4.4.0 (2021/06/30) openGionV8事前準備(taglet2→taglet)

import jdk.javadoc.doclet.DocletEnvironment	 ;
// import jdk.javadoc.doclet.Doclet  ;
// import jdk.javadoc.doclet.Reporter ;
// import javax.lang.model.element.Element	;
import javax.lang.model.element.Modifier ;
import javax.lang.model.element.TypeElement;
// import javax.lang.model.element.ElementKind	;
import javax.lang.model.element.VariableElement;
// import javax.lang.model.SourceVersion ;
import javax.lang.model.util.ElementFilter ;
// import javax.lang.model.util.Elements ;
import javax.tools.Diagnostic.Kind ;
import com.sun.source.doctree.DocCommentTree  ;
import com.sun.source.util.DocTrees  ;
// import com.sun.source.doctree.DocTree  ;

// import java.util.Locale ;
import java.util.Set;
import java.util.List;
import java.util.HashSet;
import java.util.Arrays;
import java.util.Map;

// import java.io.IOException;
// import java.io.File;
// import java.io.PrintWriter;

// import org.opengion.fukurou.util.FileUtil;
// import org.opengion.fukurou.util.StringUtil;

/**
 * ｿｰｽｺﾒﾝﾄから、ﾊﾟﾗﾒｰﾀ情報を取り出す Doclet ｸﾗｽです。
 * og.paramLevel ﾀｸﾞと og.cryptography ﾀｸﾞを切り出します。
 * これらは、ｼｽﾃﾑﾊﾟﾗﾒｰﾀとしてGE12ﾃｰﾌﾞﾙに設定される値をｸﾗｽより抽出する
 * のに使用します。
 *
 * @version  7.3
 * @author	Kazuhiko Hasegawa
 * @since	 JDK11.0,
 */
public class DocTreeParam extends AbstractDocTree {
	private static final String OG_PARAM_LVL    = "og.paramLevel";
	private static final String OG_CRYPTOGRAPHY = "og.cryptography";

	private static final int    CNST   = 1000;

	private String systemId = "**" ;
	private String outfile;

//	private DocTrees docUtil;

	/**
	 * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
	 *
	 * @og.rev 7.3.0.0 (2021/01/06) PMD refactoring. Each class should declare at least one constructor.
	 */
	public DocTreeParam() { super(); }		// これも、自動的に呼ばれるが、空のﾒｿｯﾄﾞを作成すると警告されるので、明示的にしておきます。

	/**
	 * Doclet のｴﾝﾄﾘﾎﾟｲﾝﾄﾒｿｯﾄﾞです(昔の startﾒｿｯﾄﾞ)。
	 *
	 * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
	 *
	 * @param docEnv ﾄﾞｯｸﾚｯﾄを1回呼び出す操作環境
	 *
	 * @return 正常実行時 true
	 */
	@Override
	public boolean run( final DocletEnvironment docEnv ) {
		try( DocTreeWriter writer = new DocTreeWriter( outfile,ENCODE ) ) {
			writer.printTag( "<?xml version=\"1.0\" encoding=\"", ENCODE , "\" ?>" );
			writer.printTag( "<javadoc>" );
			writer.printTag( " <systemId>" , systemId , "</systemId>" );
			writeContents( docEnv,writer );
			writer.printTag( "</javadoc>" );
		}
		catch( final Throwable th ) {
			reporter.print(Kind.ERROR, th.getMessage());
		}

		return true;
	}

	/**
	 * DocletEnvironmentよりｺﾝﾃﾝﾂを作成します。
	 *
	 * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
	 * @og.rev 8.0.1.0 (2021/10/29) titleを改行か『。』までの文字列を切り出す。
	 * @og.rev 8.0.2.1 (2021/12/10) ｺﾒﾝﾄ分割で『。』と半角の『。』の両方対応しておく。
	 *
	 * @param docEnv	ﾄﾞｯｸﾚｯﾄの最上位
	 * @param writer	DocTreeWriterｵﾌﾞｼﾞｪｸﾄ
	 */
	private void writeContents( final DocletEnvironment docEnv, final DocTreeWriter writer ) {
		// get the DocTrees utility class to access document comments
//		docUtil = docEnv.getDocTrees();
		final DocTrees docUtil = docEnv.getDocTrees();
//		final Elements eleUtil  = docEnv.getElementUtils();

		// ｸﾗｽ単位にﾙｰﾌﾟする。
		for( final TypeElement typEle : ElementFilter.typesIn(docEnv.getIncludedElements())) {
			int cnt = 0;
			final String fullName = String.valueOf( typEle.getQualifiedName() ) ;
//			final String fullName = String.valueOf(typEle);
//			System.out.println(typEle.getKind() + ":" + fullName);
			writer.setClassName( fullName );

			// ﾌｨｰﾙﾄﾞのみﾌｨﾙﾀﾘﾝｸﾞして取得する
			for( final VariableElement varEle : ElementFilter.fieldsIn(typEle.getEnclosedElements())) {		// ﾌｨｰﾙﾄﾞだけに絞る
				if( varEle.getModifiers().contains( Modifier.PUBLIC )) {										// public だけに絞る
					final DocCommentTree docTree = docUtil.getDocCommentTree(varEle);		// ﾄﾞｷｭﾒﾝﾃｰｼｮﾝ・ｺﾒﾝﾄが見つからない場合、null が返る。

					final String paramId				= String.valueOf(varEle);
					final String seq					= String.valueOf(cnt*10 + CNST);
	//				final List<? extends DocTree> title	= docTree == null ? EMPTY_LIST : docTree.getFirstSentence();
	//				final List<? extends DocTree> cmnt	= docTree == null ? EMPTY_LIST : docTree.getFullBody();
					final String[] cmnts = getTitleCmnt( docTree );		// 8.0.1.0 (2021/10/29)

					final String param = String.valueOf( varEle.getConstantValue() );

					final Map<String,List<String>> blkTagMap = blockTagsMap(docTree);
					final String paramLvl = getBlockTag( OG_PARAM_LVL   , blkTagMap, "" ).split(":")[0];	// 取得した値を ':' で分割した最初の文字列。
					final String fgcrypt  = getBlockTag( OG_CRYPTOGRAPHY, blkTagMap, "" ).split(":")[0];

//					String paramLvl = "";
//					String fgcrypt  = "";
//					if( docTree != null ) {
//						for( final DocTree dt : docTree.getBlockTags() ) {
//							final String tag = String.valueOf(dt);
//							if( tag.contains( OG_PARAM_LVL ) ) {
//								paramLvl = cut( tag,' ',':' );
//							}
//							else if( tag.contains( OG_CRYPTOGRAPHY ) ) {
//								fgcrypt = cut( tag,' ',':' );
//							}
//						}
//					}

					writer.printTag( " <fieldDoc>" );
					writer.printTag( "   <paramId>"		,paramId	,"</paramId>"		);
					writer.printTag( "   <seq>"			,seq		,"</seq>"			);
					writer.printTag( "   <param>"		,param		,"</param>"			);
//					writer.printTag( "   <title>"		,title		,"</title>"			);
					writer.printTag( "   <title>"		,cmnts[0]	,"</title>"			);	// 8.0.1.0 (2021/10/29)
//					writer.printTag( "   <contents>"	,cmnt		,"</contents>"		);
					writer.printTag( "   <contents>"	,cmnts[1]	,"</contents>"		);	// 8.0.1.0 (2021/10/29)
					writer.printTag( "   <paramLevel>"	,paramLvl	,"</paramLevel>"	);
					writer.printTag( "   <fgcrypt>"		,fgcrypt	,"</fgcrypt>"		);
					writer.printTag( " </fieldDoc>" );

					cnt++;
				}
			}
		}
	}

//	/**
//	 * DocTreeﾘｽﾄから、最初の1文(改行か『。』まで)と本文の文字列を切り出します。
//	 *
//	 * title が、docTree.getFirstSentence(); でうまく取れていないようです。
//	 * docTree.getFullBody() で取ってきた最初の1文と、残りの本文を切り出します。
//	 *
//	 * @og.rev 8.0.1.0 (2021/10/29) titleを改行か『。』までの文字列を切り出す。
//	 * @og.rev 8.0.2.1 (2021/12/10) ｺﾒﾝﾄ分割で『。』と半角の『。』の両方対応しておく。
//	 * @og.rev 8.0.2.1 (2021/12/10) DocTreeParam → AbstractDocTree に移動
//	 *
//	 * @param docTree	DocCommentTreeｵﾌﾞｼﾞｪｸﾄ
//	 * @return 最初の1文(改行か『。』まで)と本文の文字列を分割した配列
//	 */
//	private String[] getTitleCmnt( final DocCommentTree docTree ) {
//		final String[] rtns = { "","" };		// 初期値
//
//		if( docTree == null ) { return rtns; }
//		final List<? extends DocTree> doc = docTree.getFullBody();
//
//		if( doc.isEmpty() ) { return rtns; }
//
//		final String body = String.valueOf( doc.get(0) );
////		int st = body.indexOf( '。' );
//		int st = Math.max( body.indexOf( '。' ) , body.indexOf( '。' ) );	// 8.0.2.1 (2021/12/10)
//		if( st < 0 ) {
//			st = body.indexOf( '\n' );
//		}
//
//		if( st >= 0 ) {
//			rtns[0] = body.substring( 0,st ).trim();	// 一応、ｽﾍﾟｰｽは削除する。
//			rtns[1] = body.substring( st ).trim();		//
//		}
//		else {
//			rtns[1] = body.trim();
//		}
//
//		return rtns ;
//	}

	/**
	 * ｻﾎﾟｰﾄされているすべてのｵﾌﾟｼｮﾝを返します。
	 *
	 * @return ｻﾎﾟｰﾄされているすべてのｵﾌﾟｼｮﾝを含むｾｯﾄ、存在しない場合は空のｾｯﾄ
	 */
	@Override
	public Set<? extends Option> getSupportedOptions() {
		final Option[] options = {
			new AbstractOption( "-outfile", "-systemId" ) {

				/**
				 * 必要に応じてｵﾌﾟｼｮﾝと引数を処理します。
				 *
				 * @param  opt ｵﾌﾟｼｮﾝ名
				 * @param  arguments 引数をｶﾌﾟｾﾙ化したﾘｽﾄ
				 * @return 操作が成功した場合はtrue、そうでない場合はfalse
				 */
				@Override
				public boolean process(final String opt, final List<String> arguments) {
					if( "-outfile".equalsIgnoreCase(opt) ) {
						outfile = arguments.get(0);
					}
					else if( "-systemId".equalsIgnoreCase(opt) ) {
						systemId = arguments.get(0);
					}
					return true;
				}
			}
		};
		return new HashSet<>(Arrays.asList(options));
	}
}
