// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Generic representation of JSON objects.

package json

import (
	"container/vector";
	"fmt";
	"math";
	"strconv";
	"strings";
)

// Integers identifying the data type in the Json interface.
const (
	StringKind	= iota;
	NumberKind;
	MapKind;	// JSON term is "Object", but in Go, it's a map
	ArrayKind;
	BoolKind;
	NullKind;
)

// The Json interface is implemented by all JSON objects.
type Json interface {
	Kind() int;		// StringKind, NumberKind, etc.
	String() string;	// a string form (any kind)
	Number() float64;	// numeric form (NumberKind)
	Bool() bool;		// boolean (BoolKind)
	Get(s string) Json;	// field lookup (MapKind)
	Elem(i int) Json;	// element lookup (ArrayKind)
	Len() int;		// length (ArrayKind, MapKind)
}

// JsonToString returns the textual JSON syntax representation
// for the JSON object j.
//
// JsonToString differs from j.String() in the handling
// of string objects.  If j represents the string abc,
// j.String() == `abc`, but JsonToString(j) == `"abc"`.
func JsonToString(j Json) string {
	if j == nil {
		return "null"
	}
	if j.Kind() == StringKind {
		return Quote(j.String())
	}
	return j.String();
}

type _Null struct{}

// Null is the JSON object representing the null data object.
var Null Json = &_Null{}

func (*_Null) Kind() int		{ return NullKind }
func (*_Null) String() string		{ return "null" }
func (*_Null) Number() float64		{ return 0 }
func (*_Null) Bool() bool		{ return false }
func (*_Null) Get(s string) Json	{ return Null }
func (*_Null) Elem(int) Json		{ return Null }
func (*_Null) Len() int			{ return 0 }

type _String struct {
	s	string;
	_Null;
}

func (j *_String) Kind() int		{ return StringKind }
func (j *_String) String() string	{ return j.s }

type _Number struct {
	f	float64;
	_Null;
}

func (j *_Number) Kind() int		{ return NumberKind }
func (j *_Number) Number() float64	{ return j.f }
func (j *_Number) String() string {
	if math.Floor(j.f) == j.f {
		return fmt.Sprintf("%.0f", j.f)
	}
	return fmt.Sprintf("%g", j.f);
}

type _Array struct {
	a	*vector.Vector;
	_Null;
}

func (j *_Array) Kind() int	{ return ArrayKind }
func (j *_Array) Len() int	{ return j.a.Len() }
func (j *_Array) Elem(i int) Json {
	if i < 0 || i >= j.a.Len() {
		return Null
	}
	return j.a.At(i).(Json);
}
func (j *_Array) String() string {
	s := "[";
	for i := 0; i < j.a.Len(); i++ {
		if i > 0 {
			s += ","
		}
		s += JsonToString(j.a.At(i).(Json));
	}
	s += "]";
	return s;
}

type _Bool struct {
	b	bool;
	_Null;
}

func (j *_Bool) Kind() int	{ return BoolKind }
func (j *_Bool) Bool() bool	{ return j.b }
func (j *_Bool) String() string {
	if j.b {
		return "true"
	}
	return "false";
}

type _Map struct {
	m	map[string]Json;
	_Null;
}

func (j *_Map) Kind() int	{ return MapKind }
func (j *_Map) Len() int	{ return len(j.m) }
func (j *_Map) Get(s string) Json {
	if j.m == nil {
		return Null
	}
	v, ok := j.m[s];
	if !ok {
		return Null
	}
	return v;
}
func (j *_Map) String() string {
	s := "{";
	first := true;
	for k, v := range j.m {
		if first {
			first = false
		} else {
			s += ","
		}
		s += Quote(k);
		s += ":";
		s += JsonToString(v);
	}
	s += "}";
	return s;
}

// Walk evaluates path relative to the JSON object j.
// Path is taken as a sequence of slash-separated field names
// or numbers that can be used to index into JSON map and
// array objects.
//
// For example, if j is the JSON object for
// {"abc": [true, false]}, then Walk(j, "abc/1") returns the
// JSON object for true.
func Walk(j Json, path string) Json {
	for len(path) > 0 {
		var elem string;
		if i := strings.Index(path, "/"); i >= 0 {
			elem = path[0:i];
			path = path[i+1 : len(path)];
		} else {
			elem = path;
			path = "";
		}
		switch j.Kind() {
		case ArrayKind:
			indx, err := strconv.Atoi(elem);
			if err != nil {
				return Null
			}
			j = j.Elem(indx);
		case MapKind:
			j = j.Get(elem)
		default:
			return Null
		}
	}
	return j;
}

// Equal returns whether a and b are indistinguishable JSON objects.
func Equal(a, b Json) bool {
	switch {
	case a == nil && b == nil:
		return true
	case a == nil || b == nil:
		return false
	case a.Kind() != b.Kind():
		return false
	}

	switch a.Kind() {
	case NullKind:
		return true
	case StringKind:
		return a.String() == b.String()
	case NumberKind:
		return a.Number() == b.Number()
	case BoolKind:
		return a.Bool() == b.Bool()
	case ArrayKind:
		if a.Len() != b.Len() {
			return false
		}
		for i := 0; i < a.Len(); i++ {
			if !Equal(a.Elem(i), b.Elem(i)) {
				return false
			}
		}
		return true;
	case MapKind:
		m := a.(*_Map).m;
		if len(m) != len(b.(*_Map).m) {
			return false
		}
		for k, v := range m {
			if !Equal(v, b.Get(k)) {
				return false
			}
		}
		return true;
	}

	// invalid kind
	return false;
}


// Parse builder for JSON objects.

type _JsonBuilder struct {
	// either writing to *ptr
	ptr	*Json;

	// or to a[i] (can't set ptr = &a[i])
	a	*vector.Vector;
	i	int;

	// or to m[k] (can't set ptr = &m[k])
	m	map[string]Json;
	k	string;
}

func (b *_JsonBuilder) Put(j Json) {
	switch {
	case b.ptr != nil:
		*b.ptr = j
	case b.a != nil:
		b.a.Set(b.i, j)
	case b.m != nil:
		b.m[b.k] = j
	}
}

func (b *_JsonBuilder) Get() Json {
	switch {
	case b.ptr != nil:
		return *b.ptr
	case b.a != nil:
		return b.a.At(b.i).(Json)
	case b.m != nil:
		return b.m[b.k]
	}
	return nil;
}

func (b *_JsonBuilder) Float64(f float64)	{ b.Put(&_Number{f, _Null{}}) }

func (b *_JsonBuilder) Int64(i int64)	{ b.Float64(float64(i)) }

func (b *_JsonBuilder) Uint64(i uint64)	{ b.Float64(float64(i)) }

func (b *_JsonBuilder) Bool(tf bool)	{ b.Put(&_Bool{tf, _Null{}}) }

func (b *_JsonBuilder) Null()	{ b.Put(Null) }

func (b *_JsonBuilder) String(s string)	{ b.Put(&_String{s, _Null{}}) }


func (b *_JsonBuilder) Array()	{ b.Put(&_Array{vector.New(0), _Null{}}) }

func (b *_JsonBuilder) Map()	{ b.Put(&_Map{make(map[string]Json), _Null{}}) }

func (b *_JsonBuilder) Elem(i int) Builder {
	bb := new(_JsonBuilder);
	bb.a = b.Get().(*_Array).a;
	bb.i = i;
	for i >= bb.a.Len() {
		bb.a.Push(Null)
	}
	return bb;
}

func (b *_JsonBuilder) Key(k string) Builder {
	bb := new(_JsonBuilder);
	bb.m = b.Get().(*_Map).m;
	bb.k = k;
	bb.m[k] = Null;
	return bb;
}

func (b *_JsonBuilder) Flush()	{}

// StringToJson parses the string s as a JSON-syntax string
// and returns the generic JSON object representation.
// On success, StringToJson returns with ok set to true and errtok empty.
// If StringToJson encounters a syntax error, it returns with
// ok set to false and errtok set to a fragment of the offending syntax.
func StringToJson(s string) (json Json, ok bool, errtok string) {
	var j Json;
	b := new(_JsonBuilder);
	b.ptr = &j;
	ok, _, errtok = Parse(s, b);
	if !ok {
		return nil, false, errtok
	}
	return j, true, "";
}

// BUG(rsc): StringToJson should return an os.Error instead of a bool.
