package cn.citycraft.GsonAgent.nms.stream;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;

import net.minecraft.util.com.google.gson.stream.JsonWriter;

public class JsonWriterHandle implements cn.citycraft.GsonAgent.api.stream.JsonWriter {

	private static String[] REPLACEMENT_CHARS;

	private static String[] HTML_SAFE_REPLACEMENT_CHARS;

	static {
		try {
			Field field = JsonWriter.class.getDeclaredField("REPLACEMENT_CHARS");
			field.setAccessible(true);
			REPLACEMENT_CHARS = (String[]) field.get(null);
			field = JsonWriter.class.getDeclaredField("HTML_SAFE_REPLACEMENT_CHARS");
			field.setAccessible(true);
			HTML_SAFE_REPLACEMENT_CHARS = (String[]) field.get(null);
		} catch (final Exception e) {
			e.printStackTrace();
		}
	}

	private final JsonWriter handle;

	private Writer out;

	private boolean withoutQuotes = false;

	public JsonWriterHandle(final Writer out) {
		this(new JsonWriter(out));
		this.out = out;
	}

	protected JsonWriterHandle(final JsonWriter handle) {
		this.handle = handle;
	}

	@Override
	public JsonWriterHandle beginArray() throws IOException {
		getHandle().beginArray();
		return this;
	}

	@Override
	public JsonWriterHandle beginObject() throws IOException {
		getHandle().beginObject();
		return this;
	}

	@Override
	public void close() throws IOException {
		getHandle().close();
	}

	@Override
	public JsonWriterHandle endArray() throws IOException {
		getHandle().endArray();
		return this;
	}

	@Override
	public JsonWriterHandle endObject() throws IOException {
		getHandle().endObject();
		return this;
	}

	@Override
	public void flush() throws IOException {
		getHandle().flush();
	}

	public JsonWriter getHandle() {
		return handle;
	}

	@Override
	public boolean getSerializeNulls() {
		return getHandle().getSerializeNulls();
	}

	@Override
	public boolean isHtmlSafe() {
		return getHandle().isHtmlSafe();
	}

	@Override
	public boolean isLenient() {
		return getHandle().isLenient();
	}

	@Override
	public JsonWriterHandle name(final String name) throws IOException {
		getHandle().name(name);
		withoutQuotes = false;
		return this;
	}

	@Override
	public JsonWriterHandle nameWithoutQuotes(final String name) throws IOException {
		getHandle().name(name);
		withoutQuotes = true;
		return this;
	}

	@Override
	public JsonWriterHandle nullValue() throws IOException {
		getHandle().nullValue();
		return this;
	}

	@Override
	public void setHtmlSafe(final boolean htmlSafe) {
		getHandle().setHtmlSafe(htmlSafe);
	}

	@Override
	public void setIndent(final String indent) {
		getHandle().setIndent(indent);
	}

	@Override
	public void setLenient(final boolean lenient) {
		getHandle().setLenient(lenient);
	}

	@Override
	public void setSerializeNulls(final boolean serializeNulls) {
		getHandle().setSerializeNulls(serializeNulls);
	}

	@Override
	public void string(final String value) throws IOException {
		final String[] replacements = getHandle().isHtmlSafe() ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS;
		if (!this.withoutQuotes) {
			this.out.write("\"");
		}
		int last = 0;
		final int length = value.length();
		for (int i = 0; i < length; i++) {
			final char c = value.charAt(i);
			String replacement;
			if (c < '') {
				replacement = replacements[c];
				if (replacement == null) {
					continue;
				}
			} else {
				if (c == ' ') {
					replacement = "\\u2028";
				} else {
					if (c != ' ') {
						continue;
					}
					replacement = "\\u2029";
				}
			}

			if (last < i) {
				this.out.write(value, last, i - last);
			}
			this.out.write(replacement);
			last = i + 1;
		}
		if (last < length) {
			this.out.write(value, last, length - last);
		}
		if (!this.withoutQuotes) {
			this.out.write("\"");
		}
		withoutQuotes = false;
	}

	@Override
	public JsonWriterHandle value(final boolean value) throws IOException {
		getHandle().value(value);
		return this;
	}

	@Override
	public JsonWriterHandle value(final double value) throws IOException {
		getHandle().value(value);
		return this;
	}

	@Override
	public JsonWriterHandle value(final long value) throws IOException {
		getHandle().value(value);
		return this;
	}

	@Override
	public JsonWriterHandle value(final Number value) throws IOException {
		getHandle().value(value);
		return this;
	}

	@Override
	public JsonWriterHandle value(final String value) throws IOException {
		getHandle().value(value);
		return this;
	}

}
