/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.dynamicPageList; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*;
import gplx.core.primitives.*;
import gplx.langs.htmls.*; import gplx.xowa.htmls.*;
import gplx.xowa.parsers.*; import gplx.xowa.parsers.xndes.*;
import gplx.xowa.wikis.nss.*;
class Dpl_itm {
	public byte[] Page_ttl() {return page_ttl;} private byte[] page_ttl;
	public List_adp Ctg_includes() {return ctg_includes;} private List_adp ctg_includes;
	public List_adp Ctg_excludes() {return ctg_excludes;} private List_adp ctg_excludes;
	public int Count() {return count;} private int count = Int_.Min_value;
	public int Offset() {return offset;} private int offset = Int_.Min_value;
	public boolean No_follow() {return no_follow;} private boolean no_follow;
	public boolean Suppress_errors() {return suppress_errors;} private boolean suppress_errors;
	public boolean Show_ns() {return show_ns;} private boolean show_ns;
	public byte Sort_ascending() {return sort_ascending;} private byte sort_ascending = Bool_.__byte;
	public int Ns_filter() {return ns_filter;} private int ns_filter = Ns_filter_null;
	public boolean Gallery_filesize() {return gallery_filesize;} private boolean gallery_filesize;
	public boolean Gallery_filename() {return gallery_filename;} private boolean gallery_filename;
	public int Gallery_imgs_per_row() {return gallery_imgs_per_row;} private int gallery_imgs_per_row;
	public int Gallery_img_w() {return gallery_img_w;} private int gallery_img_w;
	public int Gallery_img_h() {return gallery_img_h;} private int gallery_img_h;
	public byte[] Gallery_caption() {return gallery_caption;} private byte[] gallery_caption;
	public byte Redirects_mode() {return redirects_mode;} private byte redirects_mode = Dpl_redirect.Tid_unknown;
	public byte Sort_tid() {return sort_tid;} private byte sort_tid = Dpl_sort.Tid_categoryadd;
	public byte Quality_pages() {return quality_pages;} private byte quality_pages;
	public byte Stable_pages() {return stable_pages;} private byte stable_pages;
	private Xop_ctx sub_ctx; private Xop_tkn_mkr sub_tkn_mkr; private Xop_root_tkn sub_root;
	public void Parse(Xowe_wiki wiki, Xop_ctx ctx, byte[] page_ttl, byte[] src, Xop_xnde_tkn xnde) {	// parse kvps in xnde; EX:<dpl>category=abc\nredirects=y\n</dpl>
		this.page_ttl = page_ttl;
		sub_ctx = Xop_ctx.New__sub__reuse_page(ctx);
		sub_tkn_mkr = sub_ctx.Tkn_mkr();
		sub_root = sub_tkn_mkr.Root(Bry_.Empty);
		int content_bgn = xnde.Tag_open_end(), content_end = xnde.Tag_close_bgn();
		int pos = content_bgn;
		int fld_bgn = content_bgn;
		byte key_id = 0;
		Gfo_usr_dlg usr_dlg = wiki.Appe().Usr_dlg();
		boolean ws_bgn_chk = true; int ws_bgn_idx = -1, ws_end_idx = -1;
		boolean loop = true;
		while (loop) {										// iterate over content
			boolean done = pos >= content_end;
			byte b = done ? Byte_ascii.Nl : src[pos];		// get cur byte
			switch (b) {
				case Byte_ascii.Space: case Byte_ascii.Tab:
					if	(ws_bgn_chk) ws_bgn_idx = pos;										// definite ws at bgn; set ws_bgn_idx, and keep setting until text reached; handles mixed sequence of \s\n\t where last tkn should be ws_bgn_idx
					else			{if (ws_end_idx == -1) ws_end_idx = pos;};				// possible ws at end; may be overriden later; see AdjustWsForTxtTkn
					break;
				case Byte_ascii.Eq: {						// =; make key; EX: "=" in "category="
					if (ws_bgn_idx != -1) fld_bgn = ws_bgn_idx + 1;	// +1 to position after last known ws
					int fld_end = ws_end_idx == -1 ? pos : ws_end_idx;
					key_id = Dpl_itm_keys.Parse(src, fld_bgn, fld_end, Dpl_itm_keys.Key_null);
					if (key_id == Dpl_itm_keys.Key_null) {	// unknown key; warn and set pos to end of line; EX: "unknown=";
						Parse_missing_key(usr_dlg, page_ttl, src, fld_bgn, fld_end);
						fld_bgn = Bry_find_.Find_fwd(src, Byte_ascii.Nl, pos);
						if (fld_bgn == Bry_find_.Not_found)
							loop = false;
						else {
							pos = fld_bgn;	// set pos after \n else bounds error if multiple bad keys on same line; NOTE: ++pos below; EX: \nbad1=a bad2=b\n; PAGE:de.n:Brandenburg DATE:2016-04-21
							++fld_bgn;		// set fld_bgn after \n;
						}
					}
					else {									// known key; set pos to val_bgn
						fld_bgn = pos + Int_.Const_dlm_len;
					}
					ws_bgn_chk = true; ws_bgn_idx = ws_end_idx = -1;
					break;
				}
				case Byte_ascii.Nl: {						// dlm is nl; EX: "\n" in "category=abc\n"
					if (fld_bgn != pos) {					// ignores blank lines
						if (ws_bgn_idx != -1) fld_bgn = ws_bgn_idx + 1;	// +1 to position after last known ws
						int fld_end = ws_end_idx == -1 ? pos : ws_end_idx;
						byte[] val = Bry_.Mid(src, fld_bgn, fld_end);
						Parse_cmd(wiki, key_id, val);
					}
					fld_bgn = pos + Int_.Const_dlm_len;
					ws_bgn_chk = true; ws_bgn_idx = ws_end_idx = -1;
					break;
				}
				default:	// text token
					if (ws_bgn_chk) ws_bgn_chk = false; else ws_end_idx = -1;		// INLINE: AdjustWsForTxtTkn
					break;
			}
			if (done) break;
			++pos;
		}
	}
	public void Parse_cmd(Xowe_wiki wiki, byte key_id, byte[] val) {
		sub_root.Clear();
		val = wiki.Parser_mgr().Main().Expand_tmpl(sub_root, sub_ctx, sub_tkn_mkr, val);
		switch (key_id) {
			case Dpl_itm_keys.Key_category: 			if (ctg_includes == null) ctg_includes = List_adp_.New(); ctg_includes.Add(Xoa_ttl.Replace_spaces(val)); break;
			case Dpl_itm_keys.Key_notcategory:		 	if (ctg_excludes == null) ctg_excludes = List_adp_.New(); ctg_excludes.Add(Xoa_ttl.Replace_spaces(val)); break;
			case Dpl_itm_keys.Key_ns:		 			{Xow_ns ns = (Xow_ns)wiki.Ns_mgr().Names_get_or_null(val, 0, val.length); ns_filter = ns == null ? Xow_ns_.Tid__main : ns.Id(); break;}
			case Dpl_itm_keys.Key_order:				sort_ascending = Dpl_sort.Parse_as_bool_byte(val); break;
			case Dpl_itm_keys.Key_suppresserrors:		suppress_errors = Dpl_itm_keys.Parse_as_bool(val, false); break;
			case Dpl_itm_keys.Key_nofollow:				no_follow = Dpl_itm_keys.Parse_as_bool(val, true); break;	// NOTE: default to true; allows passing nofollow=nofollow; MW: if ('false' != $arg)
			case Dpl_itm_keys.Key_shownamespace:		show_ns = Dpl_itm_keys.Parse_as_bool(val, true); break; // NOTE: default to true;
			case Dpl_itm_keys.Key_redirects:			redirects_mode = Dpl_redirect.Parse(val); break;
			case Dpl_itm_keys.Key_stablepages:			stable_pages = Dpl_stable_tid.Parse(val); break;
			case Dpl_itm_keys.Key_qualitypages:			quality_pages = Dpl_redirect.Parse(val); break;
			case Dpl_itm_keys.Key_addfirstcategorydate:	Parse_ctg_date(val); break;
			case Dpl_itm_keys.Key_count:				count = Bry_.To_int_or(val, Int_.Min_value); break;
			case Dpl_itm_keys.Key_offset:				offset = Bry_.To_int_or(val, Int_.Min_value); break;
			case Dpl_itm_keys.Key_imagesperow:			gallery_imgs_per_row = Bry_.To_int_or(val, Int_.Min_value); break;
			case Dpl_itm_keys.Key_imagewidth:			gallery_img_w = Bry_.To_int_or(val, Int_.Min_value); break;
			case Dpl_itm_keys.Key_imageheight:			gallery_img_h = Bry_.To_int_or(val, Int_.Min_value); break;
			case Dpl_itm_keys.Key_gallerycaption:		gallery_caption = val; break;	// FUTURE: parse for {{int:}}?
			case Dpl_itm_keys.Key_galleryshowfilesize:	gallery_filesize = Dpl_itm_keys.Parse_as_bool(val, true); break;
			case Dpl_itm_keys.Key_galleryshowfilename:	gallery_filename = Dpl_itm_keys.Parse_as_bool(val, true); break;
			case Dpl_itm_keys.Key_ordermethod:			sort_tid = Dpl_sort.Parse_ordermethod(val); break;
		}
	}
	private void Parse_ctg_date(byte[] val) {
//			byte val_key = Keys_get_or(val, Dpl_itm_keys.Key_false);
//			if (val_key == Dpl_itm_keys.Key_true)
//				ctg_date = true;
//			else {
//				if (val.length == 8) { 	// HACK: preg_match( '/^(?:[ymd]{2,3}|ISO 8601)$/'
//					ctg_date = true;
//					ctg_date_fmt = val;
//					if (ctg_date_fmt.length == 2) {
//						ctg_date_strip = true;
//						ctg_date_fmt = Bry_.Add(ctg_date_fmt, new byte[] {Byte_ascii.Ltr_y});
//					}
//				}
//				else
//					ctg_date = false;
//			}
	}
	private void Parse_missing_key(Gfo_usr_dlg usr_dlg, byte[] page_ttl, byte[] src, int fld_bgn, int fld_end) {
		byte[] key_bry = Bry_.Mid(src, fld_bgn, fld_end);
		boolean log = 
			(	Known_invalid_keys.Get_by_mid(src, fld_bgn, fld_end) != null	// known invalid key; just log it; handles common items like orcer and showcurid
			||	Bry_.Has_at_bgn(key_bry, Gfh_tag_.Comm_bgn)					// ignore comment-like keys; EX: <!--category=Ctg_0--> will have key of "<!--category="
			);
		String err_msg = String_.Format("dynamic_page_list:unknown_key: page={0} key={1}", String_.new_u8(page_ttl), String_.new_u8(key_bry));
		if (log)
			usr_dlg.Log_many("", "", err_msg);
		else
			usr_dlg.Warn_many("", "", err_msg);
	}
	private static final    Hash_adp_bry Known_invalid_keys = Hash_adp_bry.ci_a7()
	.Add_str_obj("orcer"						, Bool_obj_val.True)	// ignore as per http://en.wikinews.org/wiki/Template_talk:United_States; (Note it doesn't make a difference, as categoryadd is the default order method.)
	.Add_str_obj("addcategorydatefirst"			, Bool_obj_val.True)
	.Add_str_obj("mainspace"					, Bool_obj_val.True)
	.Add_str_obj("showcurid"					, Bool_obj_val.True)	// ignore for now; puts unique id at end of link for google news; https://www.mediawiki.org/wiki/Extension:DynamicPageList_%28Wikimedia%29; DATE:2015-09-07
	.Add_str_obj("googlehack"					, Bool_obj_val.True)	// same as showcurid
	.Add_str_obj("sort"							, Bool_obj_val.True)	// fr.n
	.Add_str_obj("supresserror"					, Bool_obj_val.True)	// fr.n
	.Add_str_obj("supresserrors"				, Bool_obj_val.True)	// frequency: 3 - 10
	.Add_str_obj("addlasteditor"				, Bool_obj_val.True)
	.Add_str_obj("noresultsheader"				, Bool_obj_val.True)
	.Add_str_obj("catergory"					, Bool_obj_val.True)
	.Add_str_obj("catrgory"						, Bool_obj_val.True)
	.Add_str_obj("allrevisionssince"			, Bool_obj_val.True)	// frequency: 1
	.Add_str_obj("limit"						, Bool_obj_val.True)
	.Add_str_obj("namespacename"				, Bool_obj_val.True)
	;
	public static final int Ns_filter_null = Int_.Min_value;
	// boolean ctg_date = false, ctg_date_strip = false;
	// byte[] ns_include = null;
	// byte[] ctg_date_fmt;
}
class Dpl_stable_tid {
	public static final byte Tid_null = 0, Tid_include = 1, Tid_only = 2, Tid_exclude = 3;
	public static byte Parse(byte[] bry) {
		byte key = Dpl_itm_keys.Parse(bry, Dpl_redirect.Tid_exclude);	// NOTE: exclude is default value.
		switch (key) {
			case Dpl_itm_keys.Key_exclude: 			return Tid_exclude;
			case Dpl_itm_keys.Key_include: 			return Tid_include;
			case Dpl_itm_keys.Key_only: 			return Tid_only;
			default:								throw Err_.new_unhandled(key);
		}
	}
}
