/*
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.wbases; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*;
import gplx.langs.jsons.*;
import gplx.xowa.wikis.pages.*;
public class Wbase_doc_mgr {
	private final    Xoae_app app;
	private final    Wdata_wiki_mgr wbase_mgr;
	private final    Wbase_qid_mgr qid_mgr;
	private final    gplx.xowa.apps.caches.Wdata_doc_cache hash;
	public Wbase_doc_mgr(Xoae_app app, Wdata_wiki_mgr wbase_mgr, Wbase_qid_mgr qid_mgr) {
		this.app = app; this.wbase_mgr = wbase_mgr; this.qid_mgr = qid_mgr;
		this.hash = app.Cache_mgr().Doc_cache();
	}
	public void Enabled_(boolean v) {this.enabled = v;} private boolean enabled;
	public void Clear() {
		synchronized (hash) {	// LOCK:app-level
			hash.Clear();
		}
	}
	public void Add(byte[] full_db, Wdata_doc page) {	// TEST:
		synchronized (hash) {	// LOCK:app-level
			if (hash.Get_or_null(full_db) == null)
				hash.Add(full_db, page);
		}
	}	
	public Wdata_doc Get_by_ttl_or_null(Xowe_wiki wiki, Xoa_ttl ttl) {
		byte[] qid_bry = qid_mgr.Get_or_null(wiki, ttl);	// EX: "enwiki","Earth" -> "Q2"
		return qid_bry == null ? null : this.Get_by_bry_or_null(qid_bry);
	}
	public Wdata_doc Get_by_xid_or_null(byte[] xid) {return Get_by_bry_or_null(Prepend_property_if_needed(xid));}// scribunto passes either p1 or q1; convert p1 to "Property:P1"
	public Wdata_doc Get_by_bry_or_null(byte[] ttl_bry) {// must be correct format; EX:"Q2" or "Property:P1"
		Wdata_doc rv = hash.Get_or_null(ttl_bry);
		if (rv == null) {
			synchronized (hash) {	// LOCK:app-level; hash;
				rv = Load_wdoc_or_null(ttl_bry); if (rv == null) return null;	// page not found
				Add(ttl_bry, rv);// NOTE: use ttl_bry, not rv.Qid; allows subsequent lookups to skip this redirect cycle
			}
		}
		return rv;
	}
	public Wdata_doc Load_wdoc_or_null(byte[] src_ttl_bry) {
		if (!enabled) return null;
		synchronized (hash) {	// LOCK:app-level; jdoc_parser; moved synchronized higher up; DATE:2016-09-03
			byte[] cur_ttl_bry = src_ttl_bry;
			int load_count = -1;
			while (load_count < 2) {	// limit to 2 tries (i.e.: 1 redirect)
				// parse ttl
				Xoa_ttl cur_ttl = wbase_mgr.Wdata_wiki().Ttl_parse(cur_ttl_bry);
				if (cur_ttl == null) {
					app.Usr_dlg().Warn_many("", "", "invalid wbase ttl: orig=~{0} cur=~{1}", src_ttl_bry, cur_ttl_bry);
					return null;
				}

				// get page
				Xoae_page page = wbase_mgr.Wdata_wiki().Data_mgr().Load_page_by_ttl(cur_ttl);
				if (!page.Db().Page().Exists()) return null;

				// parse jdoc
				byte[] jdoc_bry = page.Db().Text().Text_bry();
				Json_doc jdoc = null;
				jdoc = wbase_mgr.Jdoc_parser().Parse(jdoc_bry);
				if (jdoc == null) {
					app.Usr_dlg().Warn_many("", "", "invalid jdoc for ttl: orig=~{0} cur=~{1}", src_ttl_bry, cur_ttl_bry);
					return null;
				}

				// check for redirect; EX: {"entity":"Q22350516","redirect":"Q21006972"}; PAGE:fr.w:Tour_du_Táchira_2016; DATE:2016-08-13
				Json_nde jdoc_root = jdoc.Root_nde();
				byte[] redirect_ttl = jdoc_root.Get_as_bry_or(Bry__redirect, null);
				if (redirect_ttl != null) {
					cur_ttl_bry = redirect_ttl;
					continue;
				}

				// is json doc, and not a redirect; return
				return new Wdata_doc(cur_ttl_bry, wbase_mgr, jdoc);
			}
			app.Usr_dlg().Warn_many("", "", "too many redirects for ttl: orig=~{0} cur=~{1}", src_ttl_bry, cur_ttl_bry);
		}
		return null;
	}
	private static final    byte[] Bry__redirect = Bry_.new_a7("redirect");

	private static byte[] Prepend_property_if_needed(byte[] bry) {
		int len = bry == null ? 0 : bry.length;
		return	len > 1
			&&	Byte_ascii.Case_lower(bry[0]) == Byte_ascii.Ltr_p
			&&	Byte_ascii.Is_num(bry[1])
			?	Bry_.Add(Wdata_wiki_mgr.Ns_property_name_bry, Byte_ascii.Colon_bry, bry)	// if ttl starts with "p#", prepend "Property:" to get "Property:P#"; NOTE: do not ucase P b/c it breaks a test; DATE:2014-02-18
			:	bry;
	}
}
