package jp.kirikiri.tjs2;

import java.util.ArrayList;

public class DictionaryNI extends NativeInstanceObject {

	private static final int
		S_OK					= 0,
		E_BADPARAMCOUNT			= -1004;

	private static final int
		MEMBERENSURE		= 0x00000200, // create a member if not exists
		IGNOREPROP			= 0x00000800, // ignore property invoking
		HIDDENMEMBER		= 0x00001000; // member is hidden

	private static final int NIS_GETINSTANCE		= 0x00000002; // get native pointer

	public static int ClassID_Dictionary;

	CustomObject mOwner;


	class AssignCallback extends Dispatch {
		public CustomObject mOwner;
		public int funcCall( int flag, final String membername, IntWrapper hint, Variant result, Variant[] param, Dispatch2 objthis) throws VariantException, TJSException {
			// called from iTJSDispatch2::EnumMembers
			// (tTJSDictionaryNI::Assign calls iTJSDispatch2::EnumMembers)
			if( param.length < 3 ) return E_BADPARAMCOUNT;

			// hidden members are not copied
			int flags = param[1].asInteger();
			if( (flags & HIDDENMEMBER) != 0 ) {
				if( result != null ) result.set(1);
				return S_OK;
			}

			mOwner.propSetByVS( MEMBERENSURE|IGNOREPROP|flags, param[0].asString(), param[2], mOwner);

			if( result != null ) result.set(1);
			return S_OK;
		}
		// method from iTJSDispatch2, for enumeration callback
	}
	class SaveStructCallback extends Dispatch {
		public ArrayList<Dispatch2> mStack;
		public TextWriteStream mStream;
		public String mIndentStr;
		public boolean mFirst;

		public int funcCall( int flag, final String membername, IntWrapper hint, Variant result, Variant[] param, Dispatch2 objthis ) throws VariantException, TJSException {
			// called indirectly from tTJSDictionaryNI::SaveStructuredData
			if( param.length < 3) return E_BADPARAMCOUNT;

			// hidden members are not processed
			int flags = param[1].asInteger();
			if( (flags & HIDDENMEMBER) != 0 ) {
				if( result != null ) result.set(1);
				return S_OK;
			}

			if( !mFirst ) mStream.write( ",\n" );
			mFirst = false;

			mStream.write( mIndentStr );
			mStream.write( "\"" );
			mStream.write( LexBase.escapeC(param[0].asString()) );
			mStream.write( "\" => " );

			if( param[2].isObject() ) {
				// object
				VariantClosure clo = param[2].asObjectClosure();
				ArrayNI.saveStructuredDataForObject( clo.selectObject(), mStack, mStream, mIndentStr );
			} else {
				mStream.write( Utils.variantToExpressionString(param[2]) );
			}
			if( result != null) result.set(1);
			return S_OK;
		}
	}
	class AssignStructCallback extends Dispatch {
		public ArrayList<Dispatch2> mStack;
		public Dispatch2 mDest;
		public int funcCall( int flag, final String membername, IntWrapper hint, Variant result, Variant[] param, Dispatch2 objthis ) throws VariantException, TJSException {
			// called indirectly from tTJSDictionaryNI::AssignStructure or
			// tTJSArrayNI::AssignStructure
			if( param.length < 3) return E_BADPARAMCOUNT;

			// hidden members are not processed
			int flags = param[1].asInteger();
			if( (flags & HIDDENMEMBER) != 0 ) {
				if( result != null ) result.set(1);
				return S_OK;
			}

			Variant value = param[2];
			if( value.isObject() ){
				// object
				Dispatch2 dsp = value.asObject();
				// determin dsp's object type
				Variant val = new Variant();
				Holder<NativeInstance> holder = new Holder<NativeInstance>(null);
				if( dsp != null && dsp.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getDictionaryClassID(), holder ) >= 0 ) {
					//DictionaryNI dicni = (DictionaryNI) holder.mValue;
					// dictionary
					boolean objrec = false;
					final int count = mStack.size();
					for( int i = 0; i < count; i++ ) {
						Dispatch2 v = mStack.get(i);
						if( v == dsp ) {
							// object recursion detected
							objrec = true;
							break;
						}
					}
					if(objrec) {
						val.setObject(null); // becomes null
					} else {
						Dispatch2 newobj = TJS.createDictionaryObject();
						val.setObject(newobj, newobj);
						DictionaryNI newni = null;
						if( newobj.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getDictionaryClassID(), holder ) >= 0 ) {
							newni = (DictionaryNI) holder.mValue;
							newni.assignStructure( dsp, mStack );
						}
					}
				} else if( dsp != null && dsp.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getArrayClassID(), holder ) >= 0 ) {
					//ArrayNI arrayni = (ArrayNI) holder.mValue;
					// array
					boolean objrec = false;
					final int count = mStack.size();
					for( int i = 0; i < count; i++ ) {
						Dispatch2 v = mStack.get(i);
						if( v == dsp ) {
							// object recursion detected
							objrec = true;
							break;
						}
					} if( objrec ) {
						val.setObject( null ); // becomes null
					} else {
						Dispatch2 newobj = TJS.createArrayObject();
						val.setObject(newobj, newobj);
						ArrayNI newni = null;
						if( newobj.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getArrayClassID(), holder ) >= 0 ) {
							newni = (ArrayNI)holder.mValue;
							newni.assignStructure( dsp, mStack );
						}
					}
				} else {
					// other object types
					val = value;
				}
				mDest.propSetByVS( MEMBERENSURE|IGNOREPROP, param[0].asString(), val, mDest );
			} else {
				// other types
				mDest.propSetByVS( MEMBERENSURE|IGNOREPROP, param[0].asString(), value, mDest );
			}

			if( result != null ) result.set(1);
			return S_OK;
		}
	}

	public DictionaryNI() {
		super();
		mOwner = null;
	}
	public int construct( Variant[] param, Dispatch2 tjsobj ) {
		// called from TJS constructor
		if( param != null && param.length != 0) return E_BADPARAMCOUNT;
		mOwner = (CustomObject)(tjsobj);
		return S_OK;
	}
	// Invalidate override
	public void invalidate() {
		// put here something on invalidation
		mOwner = null;
		super.invalidate();
	}
	public boolean isValid() { return mOwner != null; } // check validation
	public void assign( Dispatch2 dsp ) throws VariantException, TJSException { assign(dsp,true); }
	public void assign( Dispatch2 dsp, boolean clear ) throws VariantException, TJSException {
		// copy members from "dsp" to "Owner"
		// determin dsp's object type
		ArrayNI arrayni = null;
		Holder<NativeInstance> holder = new Holder<NativeInstance>(null);
		if( dsp != null && dsp.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getArrayClassID(), holder) >= 0 ) {
			// convert from array
			if( clear ) mOwner.clear();

			arrayni = (ArrayNI) holder.mValue;
			final int count = arrayni.mItems.size();
			for( int i = 0; i < count; i++ ) {
				Variant v = arrayni.mItems.get(i);
				String name = v.asString();
				i++;
				if( i >= count ) break;
				Variant v2 = arrayni.mItems.get(i);
				mOwner.propSetByVS( MEMBERENSURE|IGNOREPROP, name, v2, mOwner );
			}
		} else {
			// otherwise
			if( clear ) mOwner.clear();
			AssignCallback callback = new AssignCallback();
			callback.mOwner = mOwner;
			dsp.enumMembers(IGNOREPROP, new VariantClosure(callback, null), dsp);
		}
	}
	public void clear() { mOwner.clear(); }
	public void saveStructuredData( ArrayList<Dispatch2> stack, TextWriteStream stream, final String indentstr ) throws VariantException, TJSException {
		stream.write( "(const) %[\n" );
		String indentstr2 = indentstr + " ";

		SaveStructCallback callback = new SaveStructCallback();
		callback.mStack = stack;
		callback.mStream = stream;
		callback.mIndentStr = indentstr2;
		callback.mFirst = true;

		mOwner.enumMembers( IGNOREPROP, new VariantClosure( callback, null ), mOwner );

		if( !callback.mFirst ) stream.write( "\n" );
		stream.write( indentstr );
		stream.write( "]" );
	}

	public void assignStructure(Dispatch2 dsp, ArrayList<Dispatch2> stack) throws VariantException, TJSException {
		// assign structured data from dsp
		//ArrayNI dicni = null;
		Holder<NativeInstance> holder = new Holder<NativeInstance>(null);
		if( dsp.nativeInstanceSupport( NIS_GETINSTANCE, ClassID_Dictionary, holder ) >= 0 ) {
			// copy from dictionary
			stack.add( dsp );
			try {
				mOwner.clear();

				AssignStructCallback callback = new AssignStructCallback();
				callback.mDest = mOwner;
				callback.mStack = stack;

				dsp.enumMembers( IGNOREPROP, new VariantClosure( callback, null ), dsp );
			} finally {
				stack.remove(stack.size()-1);
			}
		} else {
			throw new TJSException( Error.SpecifyDicOrArray );
		}
	}
}
