package jp.kirikiri.tjs2;

import java.util.ArrayList;

public class CustomObject extends Dispatch {
	private static final boolean LOGD = false;

	private static final int MAX_NATIVE_CLASS = 4;
	private static final int OBJECT_HASH__BITS_LIMITS = 32;

	private static final int NAMESPACE_DEFAULT_HASH_BITS = 3;
	private static final int SYMBOL_USING	= 0x1;
	private static final int SYMBOL_INIT	= 0x2;
	private static final int SYMBOL_HIDDEN	= 0x8;
	private static final int SYMBOL_STATIC	= 0x10;

	// copy for optimize
	private static final int
		HIDDENMEMBER = 0x00001000, // member is hidden
		STATICMEMBER = 0x00010000, // member is not registered to the
											   // object (internal use)
		ENUM_NO_VALUE = 0x00100000, // values are not retrieved
											   // (for EnumMembers)
		IGNOREPROP = 0x00000800, // ignore property invoking
		MEMBERENSURE = 0x00000200,
		NIS_REGISTER = 0x00000001, // set native pointer
		NIS_GETINSTANCE = 0x00000002, // get native pointer
		CII_ADD = 0x00000001,	// register name
								// 'num' argument passed to CII is to be igonored.
		CII_GET = 0x00000000,	// retrieve name
		CII_SET_FINALIZE = 0x00000002,	// register "finalize" method name
										// (set empty string not to call the method)
										// 'num' argument passed to CII is to be igonored.
		CII_SET_MISSING = 0x00000003,	// register "missing" method name.
										// the method is called when the member is not present.
										// (set empty string not to call the method)
										// 'num' argument passed to CII is to be igonored.
										// the method is to be called with three arguments;
										// get_or_set    : false for get, true for set
										// name          : member name
										// value         : value property; you must
										//               : dereference using unary '*' operator.
										// the method must return true for found, false for not-found.

		E_NOTIMPL = (-1002),
		E_INVALIDTYPE = (-1005),
		E_INVALIDOBJECT = (-1006),
		E_INVALIDPARAM = (-1003),
		E_MEMBERNOTFOUND = (-1001),
		E_FAIL = (-1),
		S_TRUE = (1),
		S_FALSE = (2),
		S_OK = 0;
	static private final int
		OP_BAND	= 0x0001,
		OP_BOR	= 0x0002,
		OP_BXOR	= 0x0003,
		OP_SUB	= 0x0004,
		OP_ADD	= 0x0005,
		OP_MOD	= 0x0006,
		OP_DIV	= 0x0007,
		OP_IDIV	= 0x0008,
		OP_MUL	= 0x0009,
		OP_LOR	= 0x000a,
		OP_LAND	= 0x000b,
		OP_SAR	= 0x000c,
		OP_SAL	= 0x000d,
		OP_SR	= 0x000e,
		OP_INC	= 0x000f,
		OP_DEC	= 0x0010,
		OP_MASK	= 0x001f,
		OP_MIN	= OP_BAND,
		OP_MAX	= OP_DEC;

	private static String mFinalizeName;
	private static String mMissingName;
	private static int mGlobalRebuildHashMagic = 0;
	private static void doRehash() { mGlobalRebuildHashMagic++; }

	public int mCount;
	public int mHashMask;
	public int mHashSize;
	public SymbolData[] mSymbols;
	public int mRebuildHashMagic;
	public boolean mIsInvalidated;
	public boolean mIsInvalidating;
	public NativeInstance[] mClassInstances;	//[MAX_NATIVE_CLASS];
	public int[] mClassIDs;	// [MAX_NATIVE_CLASS];

	protected boolean mCallFinalize; // set false if this object does not need to call "finalize"
	protected String mfinalize_name; // name of the 'finalize' method
	protected boolean mCallMissing; // set true if this object should call 'missing' method
	protected boolean mProsessingMissing; // true if 'missing' method is being called
	protected String mmissing_name; // name of the 'missing' method
	protected ArrayList<String> mClassNames;

	protected boolean getValidity() { return !mIsInvalidated; }
	protected void finalize() {
		for( int i = MAX_NATIVE_CLASS-1; i>=0; i--) {
			if( mClassIDs[i] != -1 ){
				if( mClassInstances[i] != null ) mClassInstances[i].destruct();
			}
		}
		mSymbols = null;
		//if(TJSObjectHashMapEnabled()) TJSRemoveObjectHashRecord(this);
		try {
			super.finalize();
		} catch (Throwable e) {
		}
	}
	protected void finalizeObject() throws VariantException, TJSException {
		// call this object's "finalize"
		if( mCallFinalize ) {
			//funcCall( 0, mfinalize_name, mfinalize_name.hashCode(), null, 0, null, this );
			funcCall( 0, mfinalize_name, null, null, null, this );
		}

		for( int i = MAX_NATIVE_CLASS-1; i >= 0; i-- ) {
			if( mClassIDs[i] != -1 ) {
				if( mClassInstances[i] != null ) mClassInstances[i].invalidate();
			}
		}
		deleteAllMembers();
	}

	public void finalizeInternal() throws VariantException, TJSException {
		if( mIsInvalidating ) return; // to avoid re-entrance
		mIsInvalidating = true;
		try {
			if( !mIsInvalidated ) {
				finalizeObject();
				mIsInvalidated = true;
			}
		} catch( VariantException e ) {
			mIsInvalidating = false;
			throw e;
		} catch( TJSException e ){
			mIsInvalidating = false;
			throw e;
		}
		mIsInvalidating = false;
	}

	public CustomObject() {
		this(NAMESPACE_DEFAULT_HASH_BITS);
	}
	public CustomObject( int hashbits ) {
		super();
		// デバッグ系はなし
		// if(TJSObjectHashMapEnabled()) TJSAddObjectHashRecord(this);

		mRebuildHashMagic = mGlobalRebuildHashMagic;
		if( hashbits > OBJECT_HASH__BITS_LIMITS ) hashbits = OBJECT_HASH__BITS_LIMITS;

		mHashSize = (1 << hashbits);
		mHashMask = mHashSize - 1;
		mSymbols = new SymbolData[mHashSize];
		for( int i = 0; i < mHashSize; i++ ) {
			mSymbols[i] = new SymbolData();
		}

		mIsInvalidated = false;
		mIsInvalidating = false;
		mCallFinalize = true;
		mCallMissing = false;
		mProsessingMissing = false;
		if( mFinalizeName == null ) {
			// first time; initialize 'finalize' name and 'missing' name
			mFinalizeName = TJS.mapGlobalStringMap( "finalize" );
			mMissingName  = TJS.mapGlobalStringMap( "missing" );
		}
		mfinalize_name = mFinalizeName;
		mmissing_name = mMissingName;
		mClassInstances = new NativeInstance[MAX_NATIVE_CLASS];
		mClassIDs = new int[MAX_NATIVE_CLASS];
		for( int i = 0; i < MAX_NATIVE_CLASS; i++ ) {
			mClassIDs[i] = -1;
		}
		mClassNames = new ArrayList<String>();
	}
	protected void beforeDestruction() throws VariantException, TJSException {
		// デバッグ系はなし
		// if(TJSObjectHashMapEnabled()) TJSSetObjectHashFlag(this, TJS_OHMF_DELETING, TJS_OHMF_SET);
		finalizeInternal();
		super.beforeDestruction();
	}

	//private void checkObjectClosureAdd( final Variant val ) {
		// adjust the reference counter when the object closure's "objthis" is
		// referring to the object itself.
		/* 使われない
		if( val.isObject() ) {
			Dispatch2 dsp = (Dispatch2) val.asObjectClosure().mObjThis;
			// if( dsp == this ) this.Release();
		}
		*/
	//}
	//private void checkObjectClosureRemove( final Variant val ) {
		/* 使われない
		if( val.val.isObject() ) {
			Dispatch2 dsp = val.val.asObjectClosure().mObjThis;
			if( dsp == this ) this.AddRef();
		}
		*/
	//}
	private boolean callGetMissing( final String name, Variant result ) throws VariantException, TJSException {
		// call 'missing' method for PopGet
		if( mProsessingMissing ) return false;
		mProsessingMissing = true;
		boolean res = false;
		try {
			Variant val = new Variant();
			SimpleGetSetProperty prop = new SimpleGetSetProperty(val);
			try {
				Variant[] args = new Variant[3];
				args[0] = new Variant(0);		// false: get
				args[1] = new Variant(new String(name) );	// member name
				args[2] = new Variant(prop);
				//tTJSVariant *pargs[3] = {args +0, args +1, args +2};
				Variant funcresult = new Variant();
				//int er = funcCall( 0, mmissing_name, missing_name.GetHint(), funcresult, pargs, this );
				int er = funcCall( 0, mmissing_name, null, funcresult, args, this );
				if( er < 0 ) {
					res = false;
				} else {
					res = funcresult.asInteger() != 0;
					result.set( val );
				}
			} finally {
				// prop.Release();
			}
		} finally {
			mProsessingMissing = false;
		}
		return res;
	}
	private boolean callSetMissing( final String name, final Variant value ) throws VariantException, TJSException {
		// call 'missing' method for PopSet
		if( mProsessingMissing ) return false;
		mProsessingMissing = true;
		boolean res = false;
		try {
			Variant val = new Variant(value);
			SimpleGetSetProperty prop = new SimpleGetSetProperty(val);
			try {
				Variant[] args = new Variant[3];
				args[0] = new Variant(1);					// true: set
				args[1] = new Variant(new String(name) );	// member name
				args[2] = new Variant(prop);
				//tTJSVariant *pargs[3] = {args +0, args +1, args +2};
				Variant funcresult = new Variant();
				//int er = funcCall( 0, mmissing_name, missing_name.GetHint(), funcresult, 3, pargs, this);
				int er = funcCall( 0, mmissing_name, null, funcresult, args, this );
				if( er < 0 ) {
					res = false;
				} else {
					res = funcresult.asInteger() != 0;
				}
			} finally {
				//prop.Release();
			}
		} finally {
			mProsessingMissing = false;
		}
		return res;
	}

	// Adds the symbol, returns the newly created data;
	// if already exists, returns the data.
	private SymbolData add( final String name, IntWrapper hint) throws TJSException {
		// add a data element named "name".
		// return existing element if the element named "name" is already alive.
		if( name == null ) return null;

		SymbolData data;
		data = find( name, hint );
		if( data != null ) {
			// the element is already alive
			return data;
		}

		int hash;
		if( hint != null && hint.value != 0 )
			hash = hint.value;  // hint must be hash because of previous calling of "Find"
		else
			hash = name.hashCode(); // HashFunc.Make(name);

		final int pos = (hash & mHashMask);
		if( LOGD ) Logger.log("Symbol Pos:"+pos);
		SymbolData lv1 = mSymbols[pos];
		if( (lv1.mSymFlags & SYMBOL_USING) != 0 ) {
			// lv1 is using
			// make a chain and insert it after lv1
			data = new SymbolData();
			data.selfClear();
			data.mNext = lv1.mNext;
			lv1.mNext = data;
			data.setName(name, hash);
			data.mSymFlags |= SYMBOL_USING;
		} else {
			// lv1 is unused
			if( (lv1.mSymFlags & SYMBOL_INIT) == 0 ) {
				lv1.selfClear();
			}
			lv1.setName(name, hash);
			lv1.mSymFlags |= SYMBOL_USING;
			data = lv1;
		}
		mCount++;
		return data;
	}
	private SymbolData add( final String name) throws TJSException {
		if( name == null ) return null;

		IntWrapper hint = new IntWrapper();
		hint.value = name.hashCode();
		return add( name, hint );
	}
	private SymbolData addTo( final String name, SymbolData[] newdata, int newhashmask ) throws TJSException {
		// similar to Add, except for adding member to new hash space.
		if( name == null) return null;

		// at this point, the member must not exist in destination hash space

		int hash;
		hash = name.hashCode();

		SymbolData lv1 = newdata[hash & newhashmask];
		SymbolData data;

		if( (lv1.mSymFlags & SYMBOL_USING) != 0 ) {
			// lv1 is using
			// make a chain and insert it after lv1
			data = new SymbolData();
			data.selfClear();
			data.mNext = lv1.mNext;
			lv1.mNext = data;
			data.setName(name, hash);
			data.mSymFlags |= SYMBOL_USING;
		} else {
			// lv1 is unused
			if( (lv1.mSymFlags & SYMBOL_INIT) == 0 ) {
				lv1.selfClear();
			}
			lv1.setName(name, hash);
			lv1.mSymFlags |= SYMBOL_USING;
			data = lv1;
		}

		// count is not incremented

		return data;
	}
	private void rebuildHash() throws TJSException {
		// rebuild hash table
		mRebuildHashMagic = mGlobalRebuildHashMagic;

		// decide new hash table size

		int r, v = mCount;
		if( (v & 0xffff0000) != 0 ) { r = 16; v >>= 16; } else r = 0;
		if( (v & 0xff00) != 0 ) { r += 8; v >>= 8; }
		if( (v & 0xf0) != 0 ) { r += 4; v >>= 4; }
		v<<=1;
		int newhashbits = r + ((0xffffaa50 >> v) &0x03) + 2;
		if(newhashbits > OBJECT_HASH__BITS_LIMITS) newhashbits = OBJECT_HASH__BITS_LIMITS;
		int newhashsize = (1 << newhashbits);


		if(newhashsize == mHashSize) return;

		int newhashmask = newhashsize - 1;
		int orgcount = mCount;

		// allocate new hash space
		SymbolData[] newsymbols = new SymbolData[newhashsize];
		for( int i = 0; i < newhashsize; i++ ) {
			newsymbols[i] = new SymbolData();
		}

		// enumerate current symbol and push to new hash space
		try {
			//memset(newsymbols, 0, sizeof(tTJSSymbolData) * newhashsize);
			//int i;
			//SymbolData lv1 = mSymbols[0];
			//SymbolData lv1lim = mSymbols[mHashSize]; // 末尾か、iterator のように処理してるんだな
			for( int i = 0; i < mHashSize; i++ ) {
			//for( ; lv1 < lv1lim; lv1++ ) {
				SymbolData lv1 = mSymbols[i];
				SymbolData d = lv1.mNext;
				while( d != null ) {
					SymbolData nextd = d.mNext;
					if( (d.mSymFlags & SYMBOL_USING) != 0 ) {
//						d->ReShare();
						SymbolData data = addTo( d.mName, newsymbols, newhashmask );
						if( data != null ) {
							data.mValue = d.mValue;
							//GetValue(data).CopyRef(*(tTJSVariant*)(&(d->Value)));
							data.mSymFlags &= ~ (SYMBOL_HIDDEN | SYMBOL_STATIC);
							data.mSymFlags |= d.mSymFlags & (SYMBOL_HIDDEN | SYMBOL_STATIC);
							//checkObjectClosureAdd( (Variant)data.mValue );
						}
					}
					d = nextd;
				}

				if( (lv1.mSymFlags & SYMBOL_USING) != 0 ) {
//					lv1->ReShare();
					SymbolData data = addTo( lv1.mName, newsymbols, newhashmask );
					if( data != null ) {
						data.mValue = lv1.mValue;
						//GetValue(data).CopyRef(*(tTJSVariant*)(&(lv1->Value)));
						data.mSymFlags &= ~ (SYMBOL_HIDDEN | SYMBOL_STATIC);
						data.mSymFlags |= lv1.mSymFlags & (SYMBOL_HIDDEN | SYMBOL_STATIC);
						//checkObjectClosureAdd( (Variant)data.mValue );
					}
				}
			}
		} catch( TJSException e ) {
			// recover
			int _HashMask = mHashMask;
			int _HashSize = mHashSize;
			SymbolData[] _Symbols = mSymbols;

			mSymbols = newsymbols;
			mHashSize = newhashsize;
			mHashMask = newhashmask;

			deleteAllMembers();
			mSymbols = null;

			mHashMask = _HashMask;
			mHashSize = _HashSize;
			mSymbols = _Symbols;
			mCount = orgcount;

			throw e;
		}

		// delete all current members
		deleteAllMembers();
		mSymbols = null;

		// assign new members
		mSymbols = newsymbols;
		mHashSize = newhashsize;
		mHashMask = newhashmask;
		mCount = orgcount;
	}
	private boolean deleteByName( final String name, IntWrapper hint ) {
		// OriginalTODO: utilize hint
		// find an element named "name" and deletes it
		int hash = name.hashCode();
		SymbolData lv1 = mSymbols[hash & mHashMask];

		if( (lv1.mSymFlags & SYMBOL_USING) == 0 && lv1.mNext == null )
			return false; // not found

		if( (lv1.mSymFlags & SYMBOL_USING) != 0 && lv1.nameMatch(name) ) {
			// mark the element place as "unused"
			//checkObjectClosureRemove( (Variant)lv1.mValue );
			lv1.postClear();
			mCount--;
			return true;
		}

		// chain processing
		SymbolData d = lv1.mNext;
		SymbolData prevd = lv1;
		while( d != null ) {
			if((d.mSymFlags & SYMBOL_USING) != 0 && d.mHash == hash ) {
				if( d.nameMatch(name) ) {
					// sever from the chain
					prevd.mNext = d.mNext;
					//checkObjectClosureRemove( (Variant)d.mValue );
					d.destory();
					d = null;
					mCount--;
					return true;
				}
			}
			prevd = d;
			d = d.mNext;
		}

		return false;
	}
	private void deleteAllMembers() {
		// delete all members
		/* メンバ数が少ない時の効率化用にあるみたいだけど、Javaでは使わない。
		if( mCount <= 10 ) {
			deleteAllMembersInternal();
			return;
		}
		*/

		//Vector<Dispatch2> vector = new Vector<Dispatch2>();
		//try {
			SymbolData lv1;//, lv1lim;

			// list all members up that hold object
			for( int i = 0; i < mHashSize; i++ ) {
				lv1 = mSymbols[i];
				SymbolData d = lv1.mNext;
				while( d != null ) {
					SymbolData nextd = d.mNext;
					if( (d.mSymFlags & SYMBOL_USING) != 0 ) {
						Variant val = (Variant)d.mValue;
						if( val.isObject() ) {
							//checkObjectClosureRemove( val );
							//VariantClosure clo = val.asObjectClosure();
							//if( clo.mObject != null ) vector.add( clo.mObject );
							//if( clo.mObjThis != null ) vector.add( clo.mObjThis );
							val.clear();
						}
					}
					d = nextd;
				}

				if( (lv1.mSymFlags & SYMBOL_USING) != 0 ) {
					Variant val = (Variant)lv1.mValue;
					if( val.isObject() ) {
						//checkObjectClosureRemove( val );
						//VariantClosure clo = val.asObjectClosure();
						//if( clo.mObject != null ) vector.add( clo.mObject );
						//if( clo.mObjThis != null ) vector.add( clo.mObjThis );
						val.clear();
					}
				}
			}

			// delete all members
			for( int i = 0; i < mHashSize; i++ ) {
				lv1 = mSymbols[i];
				SymbolData d = lv1.mNext;
				while( d != null ) {
					SymbolData nextd = d.mNext;
					if( (d.mSymFlags & SYMBOL_USING) != 0 ) {
						d.destory();
					}
					d = null;
					d = nextd;
				}
				if( (lv1.mSymFlags & SYMBOL_USING) != 0 ) {
					lv1.postClear();
				}
				lv1.mNext = null;
			}
			mCount = 0;
		//} finally {}

		// release all objects
		/*
		std::vector<iTJSDispatch2*>::iterator i;
		for(i = vector.begin(); i != vector.end(); i++)
		{
			(*i)->Release();
		}
		*/
	}

	private SymbolData find(String name, IntWrapper hint) {
		// searche an element named "name" and return its "SymbolData".
		// return NULL if the element is not found.
		if( name == null ) return null;

		if( hint != null && hint.value != 0 ) {
			// try finding via hint
			// search over the chain
			int hash = hint.value;
			int cnt = 0;
			final int pos = (hash & mHashMask);
			if( LOGD ) Logger.log("Symbol Pos:"+pos);
			SymbolData lv1 = mSymbols[pos];
			SymbolData prevd = lv1;
			SymbolData d = lv1.mNext;
			for(; d != null; prevd = d, d = d.mNext, cnt++) {
				if( d.mHash == hash && (d.mSymFlags & SYMBOL_USING) != 0 ) {
					if(d.nameMatch(name)) {
						if( cnt > 2 ) {
							// move to first
							prevd.mNext = d.mNext;
							d.mNext = lv1.mNext;
							lv1.mNext = d;
						}
						return d;
					}
				}
			}

			if( lv1.mHash == hash && (lv1.mSymFlags & SYMBOL_USING) != 0 ) {
				if( lv1.nameMatch(name) ) {
					return lv1;
				}
			}
		}

		int hash = name.hashCode();
		if( hint != null && hint.value != 0 ) {
			if( hint.value == hash ) return null;
				// given hint was not differ from the hash;
				// we already know that the member was not found.
		}

		if( hint != null ) hint.value = hash;

		final int pos = (hash & mHashMask);
		if( LOGD ) Logger.log("Symbol Pos:"+pos);
		SymbolData lv1 = mSymbols[pos];

		if( (lv1.mSymFlags & SYMBOL_USING) == 0 && lv1.mNext == null )
			return null; // lv1 is unused and does not have any chains

		// search over the chain
		int cnt = 0;
		SymbolData prevd = lv1;
		SymbolData d = lv1.mNext;
		for(; d != null; prevd = d, d=d.mNext, cnt++) {
			if( d.mHash == hash && (d.mSymFlags & SYMBOL_USING) != 0 ) {
				if( d.nameMatch(name) ) {
					if( cnt > 2 ) {
						// move to first
						prevd.mNext = d.mNext;
						d.mNext = lv1.mNext;
						lv1.mNext = d;
					}
					return d;
				}
			}
		}

		if( lv1.mHash == hash && (lv1.mSymFlags & SYMBOL_USING) != 0 ) {
			if( lv1.nameMatch(name) ) {
				return lv1;
			}
		}

		return null;
	}
	private static boolean callEnumCallbackForData( int flags, Variant[] params, VariantClosure callback, Dispatch2 objthis, final SymbolData data) throws VariantException, TJSException {
		int newflags = 0;
		if( (data.mSymFlags & SYMBOL_HIDDEN) != 0 ) newflags |= HIDDENMEMBER;
		if( (data.mSymFlags & SYMBOL_STATIC) != 0 ) newflags |= STATICMEMBER;
		params[0].set( data.mName );
		params[1].set( newflags );

		if( (flags & ENUM_NO_VALUE) == 0 ) {
			// get value
			int ret = defaultPropGet( flags, (Variant)data.mValue, params[2], objthis );
			if( ret < 0 ) return false;
		}

		Variant res = new Variant();
		int ret;
		if( (flags & ENUM_NO_VALUE) != 0 ) {
			Variant[] args = new Variant[2];
			args[0] = params[0];
			args[1] = params[1];
			ret = callback.funcCall( 0, null, null, res, args, null );
		} else {
			ret = callback.funcCall( 0, null, null, res, params, null );
		}
		if( ret < 0 ) return false;
		return res.asInteger() != 0;
	}
	protected static int defaultPropGet( int flag, Variant targ, Variant result, Dispatch2 objthis ) throws VariantException, TJSException {
		if( (flag & IGNOREPROP) == 0 ) {
			// if IGNOREPROP is not specified

			// if member's type is tvtObject, call the object's PropGet with "member=NULL"
			//  ( default member invocation ). if it is succeeded, return its return value.
			// if the PropGet's return value is TJS_E_ACCESSDENYED,
			// return as an error, otherwise return the member itself.
			if( targ.isObject() ) {
				VariantClosure tvclosure = targ.asObjectClosure();
				int hr = E_NOTIMPL;
				if( tvclosure.mObject != null ) {
					Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
					hr = tvclosure.mObject.propGet(0, null, null, result, disp );
				}
				if( hr >= 0 ) return hr;
				if( hr != E_NOTIMPL && hr != E_INVALIDTYPE && hr != E_INVALIDOBJECT ) return hr;
			}
		}

		// return the member itself
		if( result == null ) return E_INVALIDPARAM;

		result.copyRef( targ );
		return S_OK;
	}
	private void internalEnumMembers( int flags, VariantClosure callback, Dispatch2 objthis ) throws VariantException, TJSException {
		// enumlate members by calling callback.
		// note that member changes(delete or insert) through this function is not guaranteed.
		if( callback == null ) return;

		Variant name = new Variant();
		Variant newflags = new Variant();
		Variant value = new Variant();
		Variant[] params = new Variant[3];
		params[0] = name;
		params[1] = newflags;
		params[2] = value;

		//const tTJSSymbolData * lv1 = Symbols;
		//const tTJSSymbolData * lv1lim = lv1 + HashSize;
		//for(; lv1 < lv1lim; lv1++)
		for( int i = 0; i < mHashSize; i++ ) {
			final SymbolData lv1 = mSymbols[i];
			SymbolData d = lv1.mNext;
			while( d != null ) {
				final SymbolData nextd = d.mNext;

				if( (d.mSymFlags & SYMBOL_USING) != 0 ) {
					if( callEnumCallbackForData(flags, params, callback, objthis, d) == false) return;
				}
				d = nextd;
			}

			if( (lv1.mSymFlags & SYMBOL_USING) != 0 ) {
				if( callEnumCallbackForData(flags, params, callback, objthis, lv1) == false ) return;
			}
		}
	}
	public void clear() { deleteAllMembers(); }

	// service function for lexical analyzer
	public int getValueInteger( final String name, IntWrapper hint ) throws VariantException {
		SymbolData data = find( name, hint );
		if( data == null ) return -1;
		Variant val = (Variant)data.mValue;
		return val.asInteger(); // オリジナルでは、強制的に int で返す(アドレスになるかもしれない)もののようだが……
	}
	private static int tryFuncCallViaPropGet( VariantClosure tvclosure, int flag, Variant result, Variant[] param, Dispatch2 objthis) throws VariantException, TJSException {
		// retry using PropGet
		Variant tmp = new Variant();
		Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
		int er = tvclosure.mObject.propGet( 0, null, null, tmp, disp );
		if( er >= 0 ) {
			tvclosure = tmp.asObjectClosure();
			disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
			er = tvclosure.mObject.funcCall( flag, null, null, result, param, disp );
		}
		return er;
	}
	protected static int defaultFuncCall( int flag, Variant targ, Variant result, Variant[] param, Dispatch2 objthis ) throws VariantException, TJSException {
		if( targ.isObject() ) {
			int er = E_INVALIDTYPE;
			VariantClosure tvclosure = targ.asObjectClosure();
			if( tvclosure.mObject != null ) {
				// bypass
				Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
				er = tvclosure.mObject.funcCall(flag, null, null, result, param, disp );
				if(er == E_INVALIDTYPE ) {
					// retry using PropGet
					er = tryFuncCallViaPropGet( tvclosure, flag, result, param, objthis );
				}
			}
			return er;
		}
		return E_INVALIDTYPE;
	}
	public int funcCall( int flag, final String membername, IntWrapper hint, Variant result, Variant[] param, Dispatch2 objthis ) throws VariantException, TJSException {
		if(!getValidity())
			return E_INVALIDOBJECT;

		if( membername == null ) {
			// this function is called as to call a default method,
			// but this object is not a function.
			return E_INVALIDTYPE; // so returns TJS_E_INVALIDTYPE
		}

		SymbolData data =  find(membername, hint);
		if(data == null ) {
			if(mCallMissing) {
				// call 'missing' method
				Variant value_func = new Variant();
				if( callGetMissing(membername, value_func))
					return defaultFuncCall(flag, value_func, result, param, objthis);
			}

			return E_MEMBERNOTFOUND; // member not found
		}

		return defaultFuncCall(flag, data.mValue, result, param, objthis);
	}
	public int propGet( int flag, final String membername, IntWrapper hint, Variant result, Dispatch2 objthis) throws VariantException, TJSException {
		if( mRebuildHashMagic != mGlobalRebuildHashMagic ) {
			rebuildHash();
		}

		if( !getValidity() )
			return E_INVALIDOBJECT;

		if( membername == null ) {
			// this object itself has no information on PropGet with membername == NULL
			return E_INVALIDTYPE;
		}

		SymbolData data = find(membername, hint);
		if( data == null ) {
			if( mCallMissing ) {
				// call 'missing' method
				Variant value = new Variant();
				if( callGetMissing(membername, value) )
					return defaultPropGet(flag, value, result, objthis);
			}
		}

		if( data == null && (flag & MEMBERENSURE) != 0 ) {
			// create a member when TJS_MEMBERENSURE is specified
			data = add(membername, hint);
		}

		if( data == null ) return E_MEMBERNOTFOUND; // not found

		return defaultPropGet(flag, data.mValue, result, objthis);
	}
	static protected int defaultPropSet( int flag, Variant targ, final Variant param, Dispatch2 objthis ) throws VariantException, TJSException {
		if( (flag & IGNOREPROP) == 0) {
			if( targ.isObject() ) {
				// roughly the same as TJSDefaultPropGet
				VariantClosure tvclosure = targ.asObjectClosure();
				int hr = E_NOTIMPL;
				if( tvclosure.mObject != null ) {
					Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
					hr = tvclosure.mObject.propSet(0, null, null, param, disp );
				}
				if( hr >= 0 ) return hr;
				if( hr != E_NOTIMPL && hr != E_INVALIDTYPE && hr != E_INVALIDOBJECT) return hr;
			}
		}

		// normal substitution
		if( param == null ) return E_INVALIDPARAM;

		targ.copyRef( param );
		return S_OK;
	}
	public int propSet( int flag, final String membername, IntWrapper hint, final Variant param, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;

		if( membername == null ) {
			// no action is defined with the default member
			return E_INVALIDTYPE;
		}

		SymbolData data;
		if( mCallMissing ) {
			data = find(membername, hint);
			if( data == null ) {
				// call 'missing' method
				if( callSetMissing(membername, param) ) return S_OK;
			}
		}

		if( (flag & MEMBERENSURE) != 0 )
			data = add(membername, hint); // create a member when MEMBERENSURE is specified
		else
			data = find(membername, hint);

		if( data == null ) return E_MEMBERNOTFOUND; // not found

		if( (flag & HIDDENMEMBER) != 0 )
			data.mSymFlags |= SYMBOL_HIDDEN;
		else
			data.mSymFlags &= ~SYMBOL_HIDDEN;

		if( (flag & STATICMEMBER) != 0 )
			data.mSymFlags |= SYMBOL_STATIC;
		else
			data.mSymFlags &= ~SYMBOL_STATIC;

		//-- below is mainly the same as defaultPropSet

		if( (flag & IGNOREPROP) == 0 ) {
			if( data.mValue.isObject() ) {
				VariantClosure tvclosure = data.mValue.asObjectClosure();
				if( tvclosure.mObject != null ) {
					Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
					int hr = tvclosure.mObject.propSet(0, null, null, param, disp );
					if( hr >= 0 ) return hr;
					if(hr != E_NOTIMPL && hr != E_INVALIDTYPE && hr != E_INVALIDOBJECT) return hr;
				}
				data = find(membername, hint);
			}
		}

		if( param == null ) return E_INVALIDPARAM;

		//checkObjectClosureRemove( data.mValue );
		try {
			data.mValue.copyRef(param);
		} finally {
			//checkObjectClosureAdd( data.mValue );
		}

		return S_OK;
	}
	public int getCount( IntWrapper result, final String membername, IntWrapper hint, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;
		if( result == null ) return E_INVALIDPARAM;
		result.value = mCount;
		return S_OK;
	}
	public int propSetByVS( int flag, final String membername, final Variant param, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;

		if( membername == null ) {
			// no action is defined with the default member
			return E_INVALIDTYPE;
		}

		IntWrapper hint = new IntWrapper(membername.hashCode());
		SymbolData data;
		if( mCallMissing ) {
			data = find( membername, hint );
			if( data == null  ) {
				// call 'missing' method
				if( callSetMissing( membername, param ) ) return S_OK;
			}
		}

		if( (flag & MEMBERENSURE) != 0 )
			data = add(membername); // create a member when TJS_MEMBERENSURE is specified
		else
			data = find( membername, hint );

		if( data == null ) return E_MEMBERNOTFOUND; // not found

		if( (flag & HIDDENMEMBER) != 0 )
			data.mSymFlags |= SYMBOL_HIDDEN;
		else
			data.mSymFlags &= ~SYMBOL_HIDDEN;

		if( (flag & STATICMEMBER) != 0 )
			data.mSymFlags |= SYMBOL_STATIC;
		else
			data.mSymFlags &= ~SYMBOL_STATIC;

		//-- below is mainly the same as TJSDefaultPropSet

		if( (flag & IGNOREPROP) == 0 ) {
			if( data.mValue.isObject() ) {
				VariantClosure tvclosure = data.mValue.asObjectClosure();
				if( tvclosure.mObject != null ) {
					Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
					int hr = tvclosure.mObject.propSet(0, null, null, param, disp );
					if( hr >= 0 ) return hr;
					if( hr != E_NOTIMPL && hr != E_INVALIDTYPE && hr != E_INVALIDOBJECT) return hr;
				}
				data = find( membername, hint );
			}
		}

		if( param == null ) return E_INVALIDPARAM;

		//checkObjectClosureRemove( data.mValue );
		try {
			data.mValue.copyRef( param );
		} finally {
			//checkObjectClosureAdd(data.mValue);
		}
		return S_OK;
	}
	public int enumMembers( int flag, VariantClosure callback, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;
		internalEnumMembers(flag, callback, objthis);
		return S_OK;
	}
	public int deleteMember( int flag, final String membername, IntWrapper hint, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;
		if( membername == null ) return E_MEMBERNOTFOUND;
		if( !deleteByName(membername, hint) ) return E_MEMBERNOTFOUND;
		return S_OK;
	}
	protected static int defaultInvalidate( int flag, Variant targ, Dispatch2 objthis ) throws VariantException, TJSException {
		if( targ.isObject() ) {
			VariantClosure tvclosure = targ.asObjectClosure();
			if( tvclosure.mObject != null ) {
				// bypass
				Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
				return tvclosure.mObject.invalidate( flag, null, null, disp );
			}
		}
		return S_FALSE;
	}
	public int invalidate( int flag, final String membername, IntWrapper hint, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;

		if( membername == null ) {
			if( mIsInvalidated ) return S_FALSE;
			finalizeInternal();
			return S_TRUE;
		}

		SymbolData data = find(membername, hint);
		if( data == null ) {
			if( mCallMissing ) {
				// call 'missing' method
				Variant value = new Variant();
				if( callGetMissing(membername, value) )
					return defaultInvalidate(flag, value, objthis);
			}
		}

		if( data == null ) return E_MEMBERNOTFOUND; // not found

		return defaultInvalidate( flag, data.mValue, objthis);
	}
	protected static int defaultIsValid( int flag, Variant targ, Dispatch2 objthis ) throws VariantException, TJSException {
		if( targ.isObject() ) {
			VariantClosure tvclosure = targ.asObjectClosure();
			if( tvclosure.mObject != null ) {
				// bypass
				Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
				return tvclosure.mObject.isValid( flag, null, null, disp );
			}
		}
		// the target type is not tvtObject
		return S_TRUE;
	}
	public int isValid( int flag, final String membername, IntWrapper hint, Dispatch2 objthis ) throws VariantException, TJSException {
		if( membername == null ) {
			if( mIsInvalidated ) return S_FALSE;
			return S_TRUE;
		}

		SymbolData data = find(membername, hint);
		if( data == null ) {
			if( mCallMissing ) {
				// call 'missing' method
				Variant value = new Variant();
				if( callGetMissing(membername, value) )
					return defaultIsValid(flag, value, objthis);
			}
		}
		if( data == null ) return E_MEMBERNOTFOUND; // not found
		return defaultIsValid(flag, data.mValue, objthis);
	}
	protected static int defaultCreateNew( int flag, Variant targ, Holder<Dispatch2> result, Variant[] param, Dispatch2 objthis ) throws VariantException, TJSException {
		if( targ.isObject() ) {
			VariantClosure tvclosure = targ.asObjectClosure();
			if( tvclosure.mObject != null ) {
				// bypass
				Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
				return tvclosure.mObject.createNew(flag, null, null, result, param, disp );
			}
		}
		return E_INVALIDTYPE;
	}
	public int createNew( int flag, final String membername, IntWrapper hint, Holder<Dispatch2> result, Variant[] param, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;
		if( membername == null ) {
			// as an action of the default member, this object cannot create an object
			// because this object is not a class
			return E_INVALIDTYPE;
		}
		SymbolData data = find(membername, hint);
		if( data == null ) {
			if( mCallMissing ) {
				// call 'missing' method
				Variant value = new Variant();
				if( callGetMissing(membername, value) )
					return defaultCreateNew(flag, value, result, param, objthis);
			}
		}
		if( data == null ) return E_MEMBERNOTFOUND; // not found
		return defaultCreateNew( flag, data.mValue, result, param, objthis);
	}
	protected static int defaultIsInstanceOf( int flag, Variant targ, final String name, Dispatch2 objthis ) throws VariantException, TJSException {
		if( targ.isVoid() ) return S_FALSE;

		if( "Object".equals(name) ) return S_TRUE;

		if( targ.isNumber() ) {
			if( "Number".equals(name) ) return S_TRUE;
			else return S_FALSE;
		} else if( targ.isString() ) {
			if( "String".equals(name) ) return S_TRUE;
			else return S_FALSE;
		} else if( targ.isOctet() ) {
			if( "Octet".equals(name) ) return S_TRUE;
			else return S_FALSE;
		} else if( targ.isObject() ) {
			VariantClosure tvclosure = targ.asObjectClosure();
			if( tvclosure.mObject != null ) {
				// bypass
				Dispatch2 disp = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
				return tvclosure.mObject.isInstanceOf( flag, null, null, name, disp );
			}
			return S_FALSE;
		}
		return S_FALSE;
	}
	public int isInstanceOf( int flag, final String membername, IntWrapper hint, final String classname, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;

		if( membername == null ) {
			// always returns true if "Object" is specified
			if( "Object".equals(classname) ) return S_TRUE;

			// look for the class instance information
			int count = mClassNames.size();
			for( int i = 0; i < count; i++ ) {
				if( mClassNames.get(i).equals(classname) ) return S_TRUE;
			}
			return S_FALSE;
		}
		SymbolData data = find(membername, hint);
		if( data == null ) {
			if( mCallMissing ) {
				// call 'missing' method
				Variant value = new Variant();
				if( callGetMissing(membername, value) )
					return defaultIsInstanceOf( flag, value, classname, objthis );
			}
		}
		if( data== null ) return E_MEMBERNOTFOUND; // not found
		return defaultIsInstanceOf(flag, data.mValue, classname, objthis);
	}
	protected static int defaultOperation( int flag, Variant targ, Variant result, final Variant param, Dispatch2 objthis ) throws VariantException, TJSException {
		int op = flag & OP_MASK;

		if( op != OP_INC && op != OP_DEC && param == null ) return E_INVALIDPARAM;

		if( op < OP_MIN || op > OP_MAX) return E_INVALIDPARAM;

		if( targ.isObject() ) {
			// the member may be a property handler if the member's type is "tvtObject"
			// so here try to access the object.
			int hr;
			VariantClosure tvclosure = targ.asObjectClosure();
			if( tvclosure.mObject != null ) {
				Dispatch2 ot = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;
				Variant tmp = new Variant();
				hr = tvclosure.mObject.propGet( 0, null, null, tmp, ot );
				if( hr >= 0 ) {
					doVariantOperation( op, tmp, param );

					hr = tvclosure.mObject.propSet( 0, null, null, tmp, ot );
					if( hr < 0 ) return hr;
					if( result != null ) result.copyRef(tmp);
					return S_OK;
				} else if( hr != E_NOTIMPL && hr != E_INVALIDTYPE && hr != E_INVALIDOBJECT) {
					return hr;
				}
				// normal operation is proceeded if "PropGet" is failed.
			}
		}
		doVariantOperation( op, targ, param );
		if( result != null ) result.copyRef(targ);
		return S_OK;
	}
	public int operation( int flag, final String membername, IntWrapper hint, Variant result, final Variant param, Dispatch2 objthis ) throws VariantException, TJSException {
		if( !getValidity() ) return E_INVALIDOBJECT;

		// operation about the member
		// processing line is the same as above function
		if( membername == null ) {
			return E_INVALIDTYPE;
		}
		int op = flag & OP_MASK;
		if( op != OP_INC && op != OP_DEC && param == null ) return E_INVALIDPARAM;
		if( op < OP_MIN || op > OP_MAX) return E_INVALIDPARAM;
		SymbolData data = find(membername, hint);
		if( data == null ) {
			if( mCallMissing ) {
				// call default operation
				return super.operation(flag, membername, hint, result, param, objthis);
			}
		}
		if( data == null ) return E_MEMBERNOTFOUND; // not found

		if( data.mValue.isObject() ) {
			int hr;
			VariantClosure tvclosure;
			tvclosure = data.mValue.asObjectClosure();
			if( tvclosure.mObject != null ) {
				Dispatch2 ot = tvclosure.mObjThis != null ? tvclosure.mObjThis : objthis;;
				Variant tmp = new Variant();
				hr = tvclosure.mObject.propGet(0, null, null, tmp, ot);
				if( hr>= 0 ) {
					doVariantOperation(op, tmp, param);
					hr = tvclosure.mObject.propSet(0, null, null, tmp, ot);
					if( hr < 0 ) return hr;
					if( result != null ) result.copyRef(tmp);
					return S_OK;
				} else if( hr != E_NOTIMPL && hr != E_INVALIDTYPE && hr != E_INVALIDOBJECT ) {
					return hr;
				}
			}
		}

		//checkObjectClosureRemove( data.mValue );

		Variant tmp = data.mValue ;
		try {
			doVariantOperation(op, tmp, param);
		} finally {
			//checkObjectClosureAdd( data.mValue );
		}
		if( result != null ) result.copyRef(tmp);
		return S_OK;
	}
	// Dispatch クラスのメソッドを呼び出すため
	public int dispatchOperation( int flag, final String membername, IntWrapper hint, Variant result, final Variant param, Dispatch2 objthis ) throws VariantException, TJSException {
		return super.operation( flag, membername, hint, result, param, objthis );
	}
	public int nativeInstanceSupport( int flag, int classid, Holder<NativeInstance> pointer ) {
		if( flag == NIS_GETINSTANCE ) {
			// search "classid"
			for( int i = 0; i < MAX_NATIVE_CLASS; i++ ) {
				if( mClassIDs[i] == classid ) {
					pointer.mValue = mClassInstances[i];
					return S_OK;
				}
			}
			return E_FAIL;
		} else if( flag == NIS_REGISTER ) {
			// search for the empty place
			for( int i = 0; i < MAX_NATIVE_CLASS; i++ ) {
				if( mClassIDs[i] == -1 ) {
					// found... writes there
					mClassIDs[i] = classid;
					mClassInstances[i] = pointer.mValue;
					return S_OK;
				}
			}
			return E_FAIL;
		}
		return E_NOTIMPL;
	}
	public int classInstanceInfo( int flag, int num, Variant value ) throws VariantException {
		switch( flag ) {
		case CII_ADD:
		{
			// add value
			String name = value.asString();
			// デバッグ系はなし
			//if( objectHashMapEnabled() && mClassNames.size() == 0)
			//	objectHashSetType( this, "instance of class " + name );
					// First class name is used for the object classname
					// because the order of the class name
					// registration is from descendant to ancestor.
			mClassNames.add(name);
			return S_OK;
		}

		case CII_GET:
		{
			// get value
			if( num >= mClassNames.size() ) return E_FAIL;
			value.set( mClassNames.get(num) );
			return S_OK;
		}

		case CII_SET_FINALIZE:
		{
			// set 'finalize' method name
			mfinalize_name = value.asString();
			mCallFinalize = mfinalize_name.length() > 0;
			return S_OK;
		}

		case CII_SET_MISSING:
		{
			// set 'missing' method name
			mmissing_name = value.asString();
			mCallMissing = mmissing_name.length() > 0;
			return S_OK;
		}
		}
		return E_NOTIMPL;
	}
	public final String getClassNames() {
		if( mClassNames != null && mClassNames.size() > 0 ) {
			StringBuilder builder = new StringBuilder(512);
			final int count = mClassNames.size();
			for( int i = 0; i < count; i++ ) {
				builder.append(  mClassNames.get(i) );
			}
			return builder.toString();
		} else {
			return null;
		}
	}
}

