/***************************************************************************
   Copyright 2014 Emily Estes

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
***************************************************************************/
package net.metanotion.json;


/** Collate a sequence of events in to a concrete instance of JsonObject.
	@param <T> the type of value expected to be produced from the JSON encoded string.
*/
public final class ObjectReader<T> implements Handler<T>, Continuation<Object, T> {
	private final Continuation<Object, T> continuation;
	private final JsonObject obj = new JsonObject();

	/** Produce a handler to collate a JSON object.
		@param continuation The continuation to call when the appropriate .endObject() event is emitted.
	*/
	public ObjectReader(final Continuation<Object, T> continuation) { this.continuation = continuation; }

	@Override public Handler<T> resume(final Object value) {
		if(currentKey != null) {
			obj.put(currentKey, value);
			currentKey = null;
			return this;
		} else {
			throw new RuntimeException("Unexpected value without key.");
		}
	}

	@Override public Handler<T> start() { return this; }
	@Override public T finish() {
		throw new
			RuntimeException(
				"Finish is not supposed to be called directly on this handler. Use Continuation.resume instead");
	}

	private String currentKey = null;

	@Override public Handler<T> startObject() {
		if(currentKey != null) {
			return new ObjectReader<T>(this);
		} else {
			throw new RuntimeException("Unexpected start of object.");
		}
	}

	@Override public Handler<T> key(final String key) {
		currentKey = key;
		return this;
	}

	@Override public Handler<T> endObject() { return this.continuation.resume(obj); }

	@Override public Handler<T> startList() {
		if(currentKey != null) {
			return new ListReader<T>(this);
		} else {
			throw new RuntimeException("Unexpected start of list.");
		}
	}

	@Override public Handler<T> endList() { throw new RuntimeException("Unexpected end of list."); }

	@Override public Handler<T> integer(final Long val) { return resume(val); }
	@Override public Handler<T> decimal(final Double val) { return resume(val); }
	@Override public Handler<T> string(final String val) { return resume(val); }
	@Override public Handler<T> bool(final boolean val) { return resume(val); }
	@Override public Handler<T> jsonNull() { return resume(null); }
}
