package com.android.commands.monkey.ape.jev;

import java.util.HashSet;
import java.util.Set;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

/** RFC JSON syntax and duplicate-key guard before Android's permissive parser. */
final class StrictJson {
    private final String source;
    private int pos;
    private StrictJson(String source) { this.source = source; }
    static JSONObject object(String source) throws JSONException {
        if (source == null) throw new JSONException("null JSON");
        StrictJson scan = new StrictJson(source);
        scan.value(0);
        scan.space();
        if (scan.pos != source.length()) throw new JSONException("trailing JSON");
        return new JSONObject(source);
    }
    private void space() {
        while (pos < source.length() && " \r\n\t".indexOf(source.charAt(pos)) >= 0) pos++;
    }
    private boolean eat(char c) {
        space();
        if (pos < source.length() && source.charAt(pos) == c) { pos++; return true; }
        return false;
    }
    private void require(char c) throws JSONException {
        if (!eat(c)) throw new JSONException("invalid JSON syntax");
    }
    private void value(int depth) throws JSONException {
        if (depth > 64) throw new JSONException("JSON nesting limit");
        space();
        if (pos >= source.length()) throw new JSONException("missing JSON value");
        char c = source.charAt(pos);
        if (c == '{') {
            pos++;
            Set<String> keys = new HashSet<String>();
            if (eat('}')) return;
            do {
                space();
                String key = string();
                if (!keys.add(key)) throw new JSONException("duplicate JSON key");
                require(':'); value(depth + 1);
                if (eat('}')) return;
                require(',');
            } while (true);
        } else if (c == '[') {
            pos++;
            if (eat(']')) return;
            do {
                value(depth + 1);
                if (eat(']')) return;
                require(',');
            } while (true);
        } else if (c == '"') {
            string();
        } else if (source.startsWith("true", pos)) { pos += 4;
        } else if (source.startsWith("false", pos)) { pos += 5;
        } else if (source.startsWith("null", pos)) { pos += 4;
        } else {
            int start = pos;
            while (pos < source.length() && "0123456789eE+-.".indexOf(source.charAt(pos)) >= 0) pos++;
            if (!source.substring(start, pos).matches("-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?"))
                throw new JSONException("invalid JSON number");
        }
    }
    private String string() throws JSONException {
        int start = pos;
        if (pos >= source.length() || source.charAt(pos++) != '"') throw new JSONException("expected JSON string");
        while (pos < source.length()) {
            char c = source.charAt(pos++);
            if (c == '"') return (String) new JSONTokener(source.substring(start, pos)).nextValue();
            if (c < 0x20) throw new JSONException("JSON control character");
            if (c == '\\') {
                if (pos >= source.length()) throw new JSONException("invalid JSON escape");
                char escape = source.charAt(pos++);
                if (escape == 'u') {
                    for (int n = 0; n < 4; n++) {
                        if (pos >= source.length() || "0123456789abcdefABCDEF".indexOf(source.charAt(pos++)) < 0)
                            throw new JSONException("invalid unicode escape");
                    }
                } else if ("\"\\/bfnrt".indexOf(escape) < 0) throw new JSONException("invalid JSON escape");
            }
        }
        throw new JSONException("unterminated JSON string");
    }
}
