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

XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.

You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.

The terms of each license can be found in the source code repository:

GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.gfml; import gplx.*;
class GfmlFrame_nde extends GfmlFrame_base {
	@Override public int		FrameType()			{return GfmlFrame_.Type_nde;}
	public GfmlNde			CurNde()			{return nde;} GfmlNde nde;
	public int				CurNdeStartType()	{return ndeStartType;} int ndeStartType;
	public GfmlDocPos		CurDocPos()			{return docPos;} GfmlDocPos docPos = GfmlDocPos_.Root;
	public void				NullArea_set(boolean v) {nullArea = v;} private boolean nullArea = false;
	public void DatTkn_set(GfmlTkn datTkn) {		// < data >
		if (nullArea) {								// tkn is unnamed atrVal for new nde; EX: < a:; data >
			nullArea = false;
			GfmlFrame_nde frame = NewFrame(GfmlNdeStartType.DatTkn);
			frame.tknMgr.DatTkn_set(datTkn);
		}
		else {
			GfmlTkn itmKeyTkn = tknMgr.KeyTkn_pop();
			if (itmKeyTkn != GfmlTkn_.Null)			// elemKey is pending: tkn is atrVal; EX: < a=data >
				tknMgr.ExecMakeAtr(itmKeyTkn, datTkn);
			else									// tkn is unnamed atrVal; EX: < a datTkn >
				tknMgr.DatTkn_set(datTkn);
		}
	}
	public void NdeBody_bgn(GfmlTkn symTkn) {		// EX: < { >
		WaitingTkns_AddSym(symTkn, GfmlNdeSymType.BodyBgn);
		GfmlFrame_nde frame = this;
		if (nullArea)								// EX: {{  NOTE: else nde is in header; don't begin nde; EX: a:{
			frame = NewFrame(GfmlNdeStartType.Brace);
		tknMgr.ExecXferTkns_ndeBgn(this.CurNde());
		frame.nullArea = true;
	}
	public void NdeInline(GfmlTkn symTkn) {			// ; 
		WaitingTkns_AddSym(symTkn, GfmlNdeSymType.Inline);
		if (nullArea)								// empty node; EX: < ;; > NOTE: this will not be called if dataTkn is present, since dataTkn starts node; EX: < a; >
			NewFrame(GfmlNdeStartType.Inline);
		bldr.Frames_end();							// NOTE: no need for frame.nullArea = true b/c frame is ended
	}
	public void KeyTkn_set(GfmlTkn symTkn) {		// EX: < = >
		WaitingTkns_AddSym(symTkn, GfmlNdeSymType.Key);
		tknMgr.KeyTkn_setFromDataTknOrFail();
	}
	public void NdeHeader_set(GfmlTkn symTkn) {		// EX: < : >
		WaitingTkns_AddSym(symTkn, GfmlNdeSymType.Hnd);
		tknMgr.IdxNdeBgn_setToAtrBgn();
		tknMgr.HndTkn_setByDatTknOrFail();
		GfmlTkn hnd = tknMgr.HndTkn_pop();
		GfmlFrame_nde frame = this;
		if (bldr.CurNdeFrame().CurNdeStartType() == GfmlNdeStartType.Prop)	// EX: < [a: > - must start new sub node;
			frame = NewFrame(GfmlNdeStartType.Hnd);
		frame.CurNde().HndTkn_set(hnd);
		bldr.TypeMgr().NdeResolve(frame.CurNde(), ownerTypeKey);
	}
	public void NdeDot(GfmlTkn symTkn) {			// EX: < . >
		GfmlFrame_nde frm = this, oldFrm = this;
		GfmlTkn hndTkn = HndTkn_GetForDotOp(frm);	// get hndTkn; needed for (a) newFrm if prpNde; or (b) curFrm 
		boolean createPrpNde = dotSymCreatesPrpNde;	// get createPrpNde b/c it will be reused later in this proc
		if (createPrpNde) {							// inside parens; . will create a prpNde EX: a:(b.c); vs a.b
			frm = NewFrame(GfmlNdeStartType.Dot);
			frm.nde.KeyedSubObj_(true);				// NOTE: must set prpNdes to keyed
			frm.nde.ChainId_(bldr.ChainIdNext());
		}
		if (hndTkn != GfmlTkn_.Null) {				// NOTE: need to check for Null to handle ")."; EX: a.b().c(); 2nd . would return Null and overwrite b
			if (frm.nde.HndTkn() == GfmlTkn_.Null)
				frm.nde.HndTkn_set(hndTkn);
			else
				tknMgr.ExecMakeAtr(GfmlTkn_.Null, hndTkn);
		}
		frm.WaitingTkns_AddSym(symTkn, GfmlNdeSymType.Dot);	// REVIEW: may want to dump into differnt nde...
		frm.tknMgr.ExecXferTkns_ndeBgn(frm.CurNde());
		oldFrm = frm;								// get oldFrm
		frm = frm.NewFrame(GfmlNdeStartType.Dot);
		NdeDot_SetChainId(createPrpNde, oldFrm, frm);
	}
	public void NdeParen_bgn(GfmlTkn symTkn) {		// EX: < ( >
		HndTkn_SetFromDatTkn();
		WaitingTkns_AddSym(symTkn, GfmlNdeSymType.HdrBgn);	// REVIEW: may want to dump into differnt nde...
		dotSymCreatesPrpNde = true;
		frmHasParens = true;
	}
	public void NdeParen_end(GfmlTkn symTkn) {		// EX: < ) >
		tknMgr.ConsumeWaitingDatTkn(bldr.CurNde());
		WaitingTkns_AddSym(symTkn, GfmlNdeSymType.HdrEnd);
		dotSymCreatesPrpNde = false;
		if (!frmHasParens) {
			bldr.Frames_end();
			bldr.CurNdeFrame().frmHasParens = false; // does this assume that auto-closing stops at paren node?
		}
	}
	public void AtrSpr(GfmlTkn symTkn) {			// EX: < , > NOTE: proc resembles NdeParen_end
		tknMgr.ConsumeWaitingDatTkn(bldr.CurNde());
		WaitingTkns_AddSym(symTkn, GfmlNdeSymType.AtrSpr);
		if (!frmHasParens) {
			bldr.Frames_end();
			GfmlFrame_nde frm = bldr.CurNdeFrame(); // get the current frm after all popped
			int idx = frm.waitingTkns.Count();		// NOTE: reset idxs b/c endFrame will automatically set to 0, and should be objCount
			frm.tknMgr.IdxAtrBgn_setHack(idx);
			frm.tknMgr.IdxNdeBgn_set(idx);
			frm.nullArea = false;					// NOTE: endFrame automatically sets nullArea to true; set to false
		}
	}
	public void NdeProp_bgn(GfmlTkn symTkn) {		// EX: < [ >
		int oldNdeBgn = tknMgr.IdxNdeBgn();	// get oldNdeBgn; needed for header atrs; EX: < a:b [d] > ndeBgn = 0 (a pos), but will start keyNde at 5 ([ pos)
		GfmlTkn keyTkn = tknMgr.KeyTkn_pop(); boolean keyTknExists = keyTkn != GfmlTkn_.Null;
		int newNdeBgn = keyTknExists ? tknMgr.IdxAtrBgn() : waitingTkns.Count(); // if there is a key, set ndeBgn end atrBgn (EX: a=[); else set end curIdx
		tknMgr.IdxNdeBgn_set(newNdeBgn);
		WaitingTkns_AddSym(symTkn, GfmlNdeSymType.PrpBgn);
		tknMgr.ConsumeWaitingDatTkn(nde);// NEEDED for KeydDefault
		GfmlFrame_nde frame = NewFrame(GfmlNdeStartType.Prop);
		tknMgr.IdxNdeBgn_set(oldNdeBgn);	// restore oldNdeBgn
		if (!keyTknExists) {
			keyTkn = bldr.TypeMgr().FldPool().Keyed_PopNextAsTkn();
		}
		frame.CurNde().KeyTkn_set(keyTkn);
		bldr.TypeMgr().NdeResolve(frame.CurNde(), ownerTypeKey);
		frame.nullArea = false;
	}
	GfmlFrame_nde NewFrame(int newNdeFrameType) {
		GfmlDocPos		newPos = docPos.NewDown(subNdeCount++);
		GfmlNde			newNde = GfmlNde.new_(tknMgr.HndTkn_pop(), GfmlType_.Null, newNdeFrameType == GfmlNdeStartType.Prop).DocPos_(newPos);
		GfmlFrame_nde	newFrm = GfmlFrame_nde_.node_(bldr, newNde);
		newFrm.ndeStartType = newNdeFrameType;
		newFrm.ownerTypeKey = this.CurNde().Type().Key();
		tknMgr.ExecXferTkns_ndeBgn(newNde);
		bldr.Frames_push(newFrm);
		bldr.TypeMgr().NdeBgn(newNde, newFrm.ownerTypeKey);
		bldr.Doc().PragmaMgr().BgnCmds_exec(newPos, bldr);
		return newFrm;
	}
	@Override public void Build_end(GfmlBldr bldr, GfmlFrame ownerFrame) {
		GfmlFrame_nde ownerNdeFrame = (GfmlFrame_nde)ownerFrame;
		tknMgr.ExecXferTkns_ndeAll(nde);
		bldr.TypeMgr().NdeEnd();
		bldr.Doc().PragmaMgr().EndCmds_exec(docPos, bldr);
		bldr.Doc().PragmaMgr().Pragmas_compile(this.CurNde().Hnd(), bldr);
		if (ownerFrame == null) return; // rootFrame; ignore below
		ownerNdeFrame.waitingTkns.Add(this.CurNde());
		int ndeType = this.CurNdeStartType();
		if (ndeType == GfmlNdeStartType.Dot){ // if i was created by dot, close owner;
			if (ownerNdeFrame.nde.ChainId() != this.nde.ChainId()) {	// can also be if (nde.ChainHead())
			}
			else
				bldr.Frames_end();	// auto-close dot ndes
		}
		else if (ndeType == GfmlNdeStartType.Prop) {}
		else {
			ownerNdeFrame.tknMgr.IdxNdeBgn_set(ownerNdeFrame.waitingTkns.Count());
			ownerNdeFrame.nullArea = true;	// reset
		}
	}
	@Override protected GfmlFrame_base MakeNew_hook() {return new GfmlFrame_nde();}
	public void IdxNdeBgn_set(int v) {tknMgr.IdxNdeBgn_set(v);}
	@gplx.Internal protected void WaitingTkns_AddSym(GfmlTkn tkn, int type) {waitingTkns.Add(tkn); bldr.PrvSymType_set(type);}
	@gplx.Internal protected void HndTkn_SetFromDatTkn() {
		GfmlTkn hndTkn = tknMgr.DatTkn_pop();
		if (hndTkn != GfmlTkn_.Null)
			nde.HndTkn_set(hndTkn);
	}
	GfmlTkn HndTkn_GetForDotOp(GfmlFrame_nde frm) {
		if (bldr.PrvSymType() == GfmlNdeSymType.HdrEnd) return GfmlTkn_.Null; // if prev was ), ignore; EX: a(). does not have hndTkn
		GfmlTkn hndTkn = frm.tknMgr.DatTkn_pop();
//			if (hndTkn == GfmlTkn_.Null) bldr.UsrMsgs_fail(GfmlUsrMsgs.fail_DatTkn_notFound());
		return hndTkn;
	}
	void NdeDot_SetChainId(boolean createPrpNde, GfmlFrame_nde oldFrm, GfmlFrame_nde frm) {
		if (createPrpNde)							// prpNde; make sure new dotNde gets same chainId
			frm.nde.ChainId_(oldFrm.nde.ChainId());
		else {										// oldNde is dotNde
			if (oldFrm.ndeStartType == GfmlNdeStartType.Dot)	// oldNde is dotNde; just take oldNde's chainId; ex: a.b.c
				frm.nde.ChainId_(oldFrm.nde.ChainId());
			else {									// oldNde is something else
				int chainId = bldr.ChainIdNext();
				oldFrm.nde.ChainId_(chainId);		// NOTE: needed for auto-closing; EX: {a.b;} a is a DatTkn nde, but needs to be closed when b is closed
				frm.nde.ChainId_(chainId);
			}
		}
	}
	GfmlBldr bldr; GfmlFrame_ndeTknMgr tknMgr; int subNdeCount = 0; String ownerTypeKey; boolean frmHasParens = false; boolean dotSymCreatesPrpNde = false;
	public static GfmlFrame_nde new_(GfmlBldr bldr, GfmlNde newNde, GfmlLxr newLxr) {
		GfmlFrame_nde rv = new GfmlFrame_nde();
		rv.bldr = bldr;
		rv.ctor_(newLxr);
		rv.tknMgr = GfmlFrame_ndeTknMgr.new_(rv, bldr);
		rv.nde = newNde;
		rv.docPos = newNde.DocPos();
		return rv;
	}
}
class GfmlFrame_nde_ {
	public static GfmlFrame_nde as_(Object obj) {return obj instanceof GfmlFrame_nde ? (GfmlFrame_nde)obj : null;}
	public static GfmlFrame_nde node_(GfmlBldr bldr, GfmlNde newNde) {return GfmlFrame_nde.new_(bldr, newNde, bldr.CurFrame().Lxr());}
	@gplx.Internal protected static GfmlFrame_nde root_(GfmlBldr bldr, GfmlNde newNde, GfmlLxr newLxr) {return GfmlFrame_nde.new_(bldr, newNde, newLxr);}
	@gplx.Internal protected static final GfmlFrame_nde OwnerRoot_ = null;
	@gplx.Internal protected static void TransferToNde(GfmlObjList waitingTkns, GfmlNde nde, int bgn) {
		int end = waitingTkns.Count();
		for (int i = bgn; i < end; i++) {
			GfmlObj tkn = waitingTkns.Get_at(i);
			nde.SubObjs_Add(tkn);
		}
		if (bgn != end) // ignore if bgn == end
			waitingTkns.Del_range(bgn, end - 1);
	}
	@gplx.Internal protected static void TransferToAtr(GfmlObjList src, GfmlAtr trg, int bgn, int end) {
		int len = end - bgn;
		if (len <= 0 || end == -1) return;	// -1 b/c calling proc passes end - 1, and end may be 0
		for (int i = 0; i < len; i++)
			trg.SubObjs_Add(src.Get_at(i + bgn));
		src.Del_range(bgn, end - 1);
	}		
}
class GfmlNdeStartType {
	public static final int
			  Hnd			= 1			// a:
			, Brace			= 2			// {	// has no header (since header would have created node first)
			, DatTkn		= 3			// c;	// has no hnd
			, Inline		= 4			// ;	// is empty (since something would have created node first)
			, Dot			= 5			// a.b;
			, Prop			= 6			// a=[b c];
			;
}
class GfmlNdeSymType {
	public static final int
		  Null		=  0
		, BodyBgn	=  1 // {
		, BodyEnd	=  2 // }
		, Inline	=  3 // ;
		, Key		=  4 // =
		, Hnd		=  5 // :
		, Dot		=  6 // .
		, HdrBgn	=  7 // (
		, HdrEnd	=  8 // )
		, PrpBgn	=  9 // [
		, PrpEnd	= 10 // ]
		, AtrSpr	= 11 // ,	
		;
}
