/*
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.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.FetchAt(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.FetchAt(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 // ,	
		;
}
