/*
 * 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.
 */
// 7.4.4.0 (2021/06/30) openGionV8事前準備(taglet2→taglet)
//package org.opengion.fukurou.taglet2;
package org.opengion.fukurou.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対応
	 *
	 * @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 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( "   <contents>"	,cmnt		,"</contents>"		);
					writer.printTag( "   <paramLevel>"	,paramLvl	,"</paramLevel>"	);
					writer.printTag( "   <fgcrypt>"		,fgcrypt	,"</fgcrypt>"		);
					writer.printTag( " </fieldDoc>" );

					cnt++;
				}
			}
		}
	}

	/**
	 * サポートされているすべてのオプションを返します。
	 *
	 * @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));
	}
}
