class JavaxJson {

    public static JsonValue wrap(final Object object) {
        if (object == null) {
            return JsonValue.NULL;
        } else if (object instanceof JsonValue) {
            return (JsonValue) object;
        } else if (object instanceof HasValue) {
            return ((HasValue) object).toValue().get();
        } else if (object instanceof JsonObjectConvertible) {
            return ((JsonObjectConvertible) object).toJson();
        } else if (object instanceof JsonArrayConvertible) {
            return ((JsonArrayConvertible) object).toJson();
        } else if (object instanceof String) {
            return Json.createArrayBuilder().add((String) object).build().get(0);
        } else if (object instanceof Calendar) {
            return wrap(ISO8601.format((Calendar) object));
        } else if (object instanceof Boolean) {
            return ((Boolean) object) ? JsonValue.TRUE : JsonValue.FALSE;
        } else if (object instanceof Number) {
            if (object instanceof Integer) {
                return Json.createArrayBuilder().add((Integer) object).build().get(0);
            } else if (object instanceof Long) {
                return Json.createArrayBuilder().add((Long) object).build().get(0);
            } else if (object instanceof Double) {
                return Json.createArrayBuilder().add((Double) object).build().get(0);
            } else if (object instanceof BigInteger) {
                return Json.createArrayBuilder().add((BigInteger) object).build().get(0);
            } else if (object instanceof BigDecimal) {
                return Json.createArrayBuilder().add((BigDecimal) object).build().get(0);
            } else {
                // interesting... javax.json doesn't provide support for floats alongside doubles,
                // but it does support both ints and longs.
                return Json.createArrayBuilder().add(new BigDecimal(object.toString())).build().get(0);
            }
        } else if (object instanceof Object[]) {
            return Arrays.stream((Object[]) object).map(JavaxJson::wrap)
                    .collect(JsonCollectors.toJsonArray());
        } else if (object.getClass().isArray() && object.getClass().getComponentType().isPrimitive()) {
            if (object instanceof int[]) {
                return Arrays.stream((int[]) object).mapToObj(JavaxJson::wrap).collect(JsonCollectors.toJsonArray());
            } else if (object instanceof long[]) {
                return Arrays.stream((long[]) object).mapToObj(JavaxJson::wrap).collect(JsonCollectors.toJsonArray());
            } else if (object instanceof double[]) {
                return Arrays.stream((double[]) object).mapToObj(JavaxJson::wrap).collect(JsonCollectors.toJsonArray());
            } else if (object instanceof char[]) {
                return wrap(String.valueOf((char[]) object));
            } else if (object instanceof byte[]) {
                return wrap(Base64.getUrlEncoder().encodeToString((byte[]) object));
            } else if (object instanceof float[]) {
                // interesting... there is an IntStream and a LongStream,
                // and there is a DoubleStream, but there is no FloatStream
                final float[] floats = (float[]) object;
                final double[] doubles = new double[floats.length];
                for (int i = 0; i < floats.length; i++) {
                    doubles[i] = floats[i];
                }
                return wrap(doubles);
            } else if (object instanceof boolean[]) {
                // zero support anywhere for boolean arrays. just box and throw over
                final boolean[] booleans = (boolean[]) object;
                final Boolean[] boxed = new Boolean[booleans.length];
                for (int i = 0; i < booleans.length; i++) {
                    boxed[i] = booleans[i];
                }
                return wrap(boxed);
            }
        } else if (object instanceof Map) {
            return ((Map<?, ?>) object).entrySet().stream()
                    .collect(Collectors.toMap(
                            entry -> String.valueOf(entry.getKey()),
                            entry -> wrap(entry.getValue()))).entrySet().stream()
                    .collect(JsonCollectors.toJsonObject());
        } else if (object instanceof Collection) {
            return ((Collection<?>) object).stream().map(JavaxJson::wrap)
                    .collect(JsonCollectors.toJsonArray());
        }
        return Json.createArrayBuilder().add(String.valueOf(object)).build().get(0);
    }

    private  JavaxJson();

}