package jp.kirikiri.tjs2;

import java.util.ArrayList;

class ArrayNI extends NativeInstanceObject {
	private static int
		S_OK = 0,
		E_BADPARAMCOUNT = -1004,
		NIS_GETINSTANCE		= 0x00000002, // get native pointer
		IGNOREPROP			= 0x00000800, // ignore property invoking
		HIDDENMEMBER		= 0x00001000; // member is hidden

	static public int ClassID_Array;
	static public void register() {
		ClassID_Array = TJS.registerNativeClass("Array");
	}

	public ArrayList<Variant> mItems;

	class DictionaryEnumCallback extends Dispatch {
		public ArrayList<Variant> mItems;

		public int funcCall( int flag, final String membername, IntWrapper hint, Variant result, Variant[] param, Dispatch2 objthis ) throws VariantException {
			// called from CustomObject::EnumMembers
			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;
			}
			// push items
			mItems.add( param[0] );
			mItems.add( param[2] );
			if( result != null ) result.set(1);
			return S_OK;
		}
	};
	public ArrayNI() {
		super();
		mItems = new ArrayList<Variant>();
	}

	public int construct( Variant[] param, Dispatch2 tjsObj ) {
		// called by TJS constructor
		if( param != null && param.length != 0 ) {
			return E_BADPARAMCOUNT;
		}
		return S_OK;
	}
	public void assign( Dispatch2 dsp ) throws VariantException, TJSException {
		// copy members from "dsp" to "Owner"

		// determin dsp's object type
		//Holder<ArrayNI> arrayni = new Holder<ArrayNI>(null);
		Holder<NativeInstance> arrayni = new Holder<NativeInstance>(null);
		int ret = dsp.nativeInstanceSupport( NIS_GETINSTANCE, ClassID_Array, arrayni);
		if( ret >= 0 ) {
			// copy from array
			ArrayNI array = (ArrayNI) arrayni.mValue;
			mItems.clear();
			mItems.addAll( array.mItems );
		} else {
			// convert from dictionary or others
			mItems.clear();
			DictionaryEnumCallback callback = new DictionaryEnumCallback();
			callback.mItems = mItems;
			dsp.enumMembers( IGNOREPROP, new VariantClosure( callback, null), dsp);
		}
	}
	public void saveStructuredData( ArrayList<Dispatch2> stack, TextWriteStream stream, final String indentstr ) throws VariantException, TJSException {
		stream.write( "(const) [\n" );
		String indentstr2 = indentstr + " ";

		final int count = mItems.size();
		for( int i = 0; i < count; i++ ) {
			Variant v = mItems.get(i);
			stream.write(indentstr2);
			if( v.isObject() ) {
				// object
				VariantClosure clo = v.asObjectClosure();
				saveStructuredDataForObject( clo.selectObject(),stack, stream, indentstr2 );
			} else {
				stream.write( Utils.variantToExpressionString(v) );
			}
			if( i != mItems.size() -1) // unless last
				stream.write( ",\n" );
			else
				stream.write( "\n" );
		}
		stream.write(indentstr);
		stream.write("]");
	}
	public static void saveStructuredDataForObject( Dispatch2 dsp, ArrayList<Dispatch2> stack, TextWriteStream stream, final String indentstr ) throws VariantException, TJSException {
		// check object recursion
		final int count = stack.size();
		for( int i = 0; i < count; i++ ) {
			Dispatch2 d = stack.get(i);
			if( d == dsp ) {
				// object recursion detected
				stream.write( "null /* object recursion detected */" );
				return;
			}
		}

		// determin dsp's object type
		DictionaryNI dicni = null;
		ArrayNI arrayni = null;
		Holder<NativeInstance> ni = new Holder<NativeInstance>(null);
		if( dsp != null ) {
			int ret = dsp.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getDictionaryClassID(), ni );
			if( ret >= 0 ) {
				// dictionary
				stack.add(dsp);
				dicni = (DictionaryNI)ni.mValue;
				dicni.saveStructuredData( stack, stream, indentstr );
				stack.remove(stack.size()-1);
				return;
			} else {
				ret = dsp.nativeInstanceSupport( NIS_GETINSTANCE, ClassID_Array, ni );
				if( ret >= 0 ) {
					// array
					stack.add(dsp);
					arrayni = (ArrayNI)ni.mValue;
					arrayni.saveStructuredData(stack, stream, indentstr);
					stack.remove(stack.size()-1);
					return;
				} else {
					// other objects
					stream.write( "null /* (object) \"" ); // stored as a null
					Variant val = new Variant(dsp,dsp);
					stream.write( LexBase.escapeC(val.asString()) );
					stream.write( "\" */" );
					return;
				}
			}
		}
		stream.write( "null" );
	}
	public void assignStructure( Dispatch2 dsp, ArrayList<Dispatch2> stack ) throws TJSException, VariantException {
		// assign structured data from dsp
		ArrayNI arrayni = null;
		Holder<NativeInstance> ni = new Holder<NativeInstance>(null);
		int ret = dsp.nativeInstanceSupport( NIS_GETINSTANCE, ClassID_Array, ni );
		if( ret >= 0 ) {
			arrayni = (ArrayNI)ni.mValue;
			// copy from array
			stack.add(dsp);
			try {
				mItems.clear();
				final int count = arrayni.mItems.size();
				for( int i = 0; i < count; i++ ) {
					Variant v = arrayni.mItems.get(i);
					if( v.isObject() ) {
						// object
						Dispatch2 dsp1 = v.asObject();
						// determin dsp's object type

						//DictionaryNI dicni = null;
						//ArrayNI arrayni1 = null;

						if( dsp1 != null && dsp1.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getDictionaryClassID(), ni) >= 0 ) {
							//dicni = (DictionaryNI)ni.mValue;
							// dictionary
							boolean objrec = false;
							final int scount = stack.size();
							for( int j = 0; j < scount; j++ ) {
								Dispatch2 d = stack.get(j);
								if( d == dsp1 ) {
									// object recursion detected
									objrec = true;
									break;
								}
							}
							if( objrec ) {
								mItems.add( new Variant() ); // becomes null
							} else {
								Dispatch2 newobj = TJS.createDictionaryObject();
								mItems.add( new Variant(newobj, newobj) );
								DictionaryNI newni = null;
								if( newobj.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getDictionaryClassID(), ni ) >= 0 ) {
									newni = (DictionaryNI)ni.mValue;
									newni.assignStructure(dsp, stack);
								}
							}
						} else if( dsp1 != null && dsp1.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getArrayClassID(), ni) >= 0 ) {
							// array
							boolean objrec = false;
							final int scount = stack.size();
							for( int j = 0; j < scount; j++ ) {
								Dispatch2 d = stack.get(j);
								if( d == dsp1 ) {
									// object recursion detected
									objrec = true;
									break;
								}
							}
							if( objrec ) {
								mItems.add( new Variant() ); // becomes null
							} else {
								Dispatch2 newobj = new ArrayObject();
								mItems.add( new Variant(newobj, newobj) );
								ArrayNI newni = null;
								if( newobj.nativeInstanceSupport( NIS_GETINSTANCE, TJS.getArrayClassID(), ni ) >= 0 ) {
									newni = (ArrayNI)ni.mValue;
									newni.assignStructure(dsp, stack);
								}
							}
						} else {
							// other object types
							mItems.add( v );
						}
					} else {
						// others
						mItems.add( v );
					}
				}
			} finally {
				stack.remove( stack.size()-1 );
			}
		} else {
			throw new TJSException( Error.SpecifyDicOrArray );
		}
	}
}
