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

import java.util.Map;
import java.util.HashMap;

// 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 abstract class AbstractDocTree implements Doclet {
	/** ｴﾝｺｰﾄﾞ {@value} */
	public static final String ENCODE = "UTF-8";

	/** 情報の出力 */
	protected Reporter reporter;									// JavaDocの情報、警告、エラー情報の出力

	/** 空DocTreeﾘｽﾄ */
	protected static final List<DocTree> EMPTY_LIST = List.of();	// ゼロ要素を含む変更不可能なリスト

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 7.3.0.0 (2021/01/06) PMD refactoring. Each class should declare at least one constructor.
	 */
	public AbstractDocTree() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * 指定されたロケールとエラー・レポータでこのドックレットを初期化します。
	 *
	 * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
	 *
	 * @param locale 使用されるロケール
	 * @param reporter 使用するレポータ
	 */
	@Override
	public void init(final Locale locale, final Reporter reporter) {
		reporter.print(Kind.NOTE, getName() + " Start!: ");		// NOTE:情報 WARNING:警告 ERROR:エラー
		this.reporter = reporter;
	}

	/**
	 * ドックレットを識別する名前を返します。
	 *
	 * @return 名前
	 */
	@Override
	public String getName() {
		return getClass().getSimpleName();
	}

	/**
	 * Doclet.Option を継承し、共通メソッドを実装したabstractクラス
	 *
	 * 単純に、メソッドのOverrideで共通化しているだけです。
	 */
	protected static abstract class AbstractOption implements Option {
		private final List<String> someOption ;

		/**
		 * コンストラクター。
		 *
		 * @param prm 引数に対応するキーの可変引数
		 */
		AbstractOption( final String... prm ) {
			super();
			someOption = Arrays.asList( prm );
		}

		/**
		 * このオプションが消費する引数の数を返します。
		 *
		 * @return 消費された引数の数
		 */
		@Override
		public int getArgumentCount() {
			return 1;
		}

		/**
		 * オプションの説明を返します。
		 *
		 * @return 設定されている場合はdescription、そうでない場合は空のString
		 */
		@Override
		public String getDescription() {
			return "an option with aliases";
		}

		/**
		 * オプションの種類を返します。
		 *
		 * @return このオプションの種類
		 */
		@Override
		public Option.Kind getKind() {
			return Option.Kind.STANDARD;
		}

		/**
		 * オプションを識別するために使用される可能性のある名前のリストを返します。
		 *
		 * @return オプションの名前リスト
		 */
		@Override
		public List<String> getNames() {
			return someOption;
		}

		/**
		 * オプションのパラメータを返します。
		 *
		 * @return 設定されている場合はパラメータ、それ以外の場合は空のString
		 */
		@Override
		public String getParameters() {
			return "file";
		}
	}

	/**
	 * このドックレットでサポートされているJavaプログラミング言語のバージョンを返します。
	 *
	 * @return 通常は最新バージョン
	 */
	@Override
	public SourceVersion getSupportedSourceVersion() {
		// support the latest release
		return SourceVersion.latest();
	}

	/* ************************************************************************************ */

//	/**
//	 * 指定の文字列から、開始文字と終了文字の間を切り抜いて返します。
//	 * 開始文字か、終了文字のどちらかが存在しない場合は、ｵﾘｼﾞﾅﾙの文字列を返します。
//	 *
//	 * @param org	ｵﾘｼﾞﾅﾙの文字列
//	 * @param stCh	開始文字
//	 * @param edCh	終了文字
//	 *
//	 * @return 切り抜かれた文字列
//	 */
//	protected String cut( final String org , final char stCh , final char edCh ) {
//		final int st = org.indexOf( stCh );
//		final int ed = st > 0 ? org.indexOf( edCh , st ) : -1;
//
//		return ed > 0 ? org.substring( st+1,ed ) : org ;
//	}

//	/**
//	 * 指定の文字列から、開始文字と終了文字の間を切り抜いて返します。
//	 * 開始文字か、終了文字のどちらかが存在しない場合は、ｵﾘｼﾞﾅﾙの文字列を返します。
//	 *
//	 * @param org	ｵﾘｼﾞﾅﾙの文字列
//	 * @param omit	先頭から削除する文字列
//	 *
//	 * @return 切り抜かれた文字列
//	 */
//	protected String cutTag( final String org , final String omit ) {
//		return org.substring( 1+omit.length() ).trim();
//	}

	/**
	 * BlockTagsのキーとリストのMapを作成して返します。
	 * キーと値を分離します。同じキーが複数存在しますので、それらは Listに入れて返します。
	 * docTreeは、null の場合もあるので、その場合は、空のMapを返します。
	 *
	 * @param docTree	DocCommentTreeｵﾌﾞｼﾞｪｸﾄ
	 *
	 * @return BlockTagsのキーとリストのMap
	 */
	protected Map<String,List<String>> blockTagsMap( final DocCommentTree docTree ) {
		final Map<String,List<String>> rtnMap = new HashMap<>();

		if( docTree != null ) {
			for( final DocTree dt : docTree.getBlockTags() ) {
				final String tag = String.valueOf(dt).trim();
				final int ad = tag.indexOf( ' ' );			// 最初のスペースで分離
				final String key = ad > 0 ? tag.substring( 1,ad ).trim() : tag ;		// 最初の文字列は、@ なので。
				final String val = ad > 0 ? tag.substring( ad+1 ).trim() : "" ;

				rtnMap.computeIfAbsent(key, k -> new ArrayList<String>()).add( val );
			}
		}

		return rtnMap;
	}

	/**
	 * blockTagsMapで作成されたMapｵﾌﾞｼﾞｪｸﾄから、文字列を作成します。
	 * キーがMapに存在しない場合は、空文字列を返します。
	 * docTreeは、null の場合もあるので、その場合は、空のMapを返します。
	 *
	 * @param key		blockTagのキー
	 * @param blcMap	blockTagsMapで作成されたMapｵﾌﾞｼﾞｪｸﾄ
	 * @param delimiter	複数タグを連結する場合の、区切り文字
	 *
	 * @return 指定のタグの文字列
	 */
	protected String getBlockTag( final String key,final Map<String,List<String>> blcMap,final String delimiter ) {
		final List<String> blkList = blcMap.get( key );

		return blkList == null ? "" : String.join( delimiter,blkList );
	}

	/**
	 * 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文(改行か『｡』まで)と本文の文字列を分割した配列
	 */
	protected 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) );

		final StringBuilder buf = new StringBuilder();		// 8.0.2.1 (2021/12/10) & で切れる？
		doc.forEach( val -> buf.append(val) );
		final String body = buf.toString();

//		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();		//
			if( rtns[1].isEmpty() ) { rtns[1] = rtns[0]; }
		}
		else {
			rtns[0] = body.trim();
			rtns[1] = rtns[0];							// 8.0.2.1 (2021/12/10) 同じ値をセットする。
		}

		return rtns ;
	}
}
