package gob

// This file is not normally included in the gob package.  Used only for debugging the package itself.
// Add debug.go to the files listed in the Makefile to add Debug to the gob package.

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"reflect"
	"runtime"
)

var dump = false // If true, print the remaining bytes in the input buffer at each item.

// Init installs the debugging facility. If this file is not compiled in the
// package, the test in codec_test.go is a no-op.
func init() {
	debugFunc = Debug
}

// Debug prints a human-readable representation of the gob data read from r.
func Debug(r io.Reader) {
	defer func() {
		if e := recover(); e != nil {
			if _, ok := e.(runtime.Error); ok {
				panic(e)
			}
			fmt.Printf("error during debugging: %v\n", e)
		}
	}()
	NewDecoder(r).debug()
}

// debugRecv is like recv but prints what it sees.
func (dec *Decoder) debugRecv() {
	if dec.byteBuffer != nil && dec.byteBuffer.Len() != 0 {
		fmt.Printf("error in recv: %d bytes left in input buffer\n", dec.byteBuffer.Len())
		return
	}
	// Read a count.
	var nbytes uint64
	nbytes, dec.err = decodeUintReader(dec.r, dec.countBuf[0:])
	if dec.err != nil {
		fmt.Printf("receiver error on count: %s\n", dec.err)
		return
	}
	// Allocate the buffer.
	if nbytes > uint64(len(dec.buf)) {
		dec.buf = make([]byte, nbytes+1000)
	}
	dec.byteBuffer = bytes.NewBuffer(dec.buf[0:nbytes])

	// Read the data
	_, dec.err = io.ReadFull(dec.r, dec.buf[0:nbytes])
	if dec.err != nil {
		fmt.Printf("receiver error on data: %s\n", dec.err)
		if dec.err == os.EOF {
			dec.err = io.ErrUnexpectedEOF
		}
		return
	}
	if dump {
		fmt.Printf("received %d bytes:\n\t% x\n", nbytes, dec.byteBuffer.Bytes())
	}
}


// debug is like Decode but just prints what it finds.  It should be safe even for corrupted data.
func (dec *Decoder) debug() {
	// Make sure we're single-threaded through here.
	dec.mutex.Lock()
	defer dec.mutex.Unlock()

	dec.err = nil
	dec.debugRecv()
	if dec.err != nil {
		return
	}
	dec.debugFromBuffer(0, false)
}

// printFromBuffer prints the next value.  The buffer contains data, but it may
// be a type descriptor and we may need to load more data to see the value;
// printType takes care of that.
func (dec *Decoder) debugFromBuffer(indent int, countPresent bool) {
	for dec.state.b.Len() > 0 {
		// Receive a type id.
		id := typeId(decodeInt(dec.state))

		// Is it a new type?
		if id < 0 { // 0 is the error state, handled above
			// If the id is negative, we have a type.
			dec.debugRecvType(-id)
			if dec.err != nil {
				break
			}
			continue
		}

		// No, it's a value.
		// Make sure the type has been defined already or is a builtin type (for
		// top-level singleton values).
		if dec.wireType[id] == nil && builtinIdToType[id] == nil {
			dec.err = errBadType
			break
		}
		if countPresent {
			decodeUint(dec.state)
		}
		dec.debugPrint(indent, id)
		break
	}
}

func (dec *Decoder) debugRecvType(id typeId) {
	// Have we already seen this type?  That's an error
	if _, alreadySeen := dec.wireType[id]; alreadySeen {
		dec.err = os.ErrorString("gob: duplicate type received")
		return
	}

	// Type:
	wire := new(wireType)
	dec.err = dec.decode(tWireType, reflect.NewValue(wire))
	if dec.err == nil {
		printWireType(wire)
	}
	// Remember we've seen this type.
	dec.wireType[id] = wire

	// Load the next parcel.
	dec.debugRecv()
}

func printWireType(wire *wireType) {
	fmt.Printf("type definition {\n")
	switch {
	case wire.arrayT != nil:
		printCommonType("array", &wire.arrayT.commonType)
		fmt.Printf("\tlen %d\n\telemid %d\n", wire.arrayT.Len, wire.arrayT.Elem)
	case wire.mapT != nil:
		printCommonType("map", &wire.mapT.commonType)
		fmt.Printf("\tkeyid %d\n", wire.mapT.Key)
		fmt.Printf("\telemid %d\n", wire.mapT.Elem)
	case wire.sliceT != nil:
		printCommonType("slice", &wire.sliceT.commonType)
		fmt.Printf("\telemid %d\n", wire.sliceT.Elem)
	case wire.structT != nil:
		printCommonType("struct", &wire.structT.commonType)
		for i, field := range wire.structT.field {
			fmt.Printf("\tfield %d:\t%s\tid=%d\n", i, field.name, field.id)
		}
	}
	fmt.Printf("}\n")
}

func printCommonType(kind string, common *commonType) {
	fmt.Printf("\t%s %q\n\tid: %d\n", kind, common.name, common._id)
}

func (dec *Decoder) debugPrint(indent int, id typeId) {
	wire, ok := dec.wireType[id]
	if ok && wire.structT != nil {
		dec.debugStruct(indent+1, id, wire)
	} else {
		dec.debugSingle(indent+1, id, wire)
	}
}

func (dec *Decoder) debugSingle(indent int, id typeId, wire *wireType) {
	// is it a builtin type?
	_, ok := builtinIdToType[id]
	if !ok && wire == nil {
		errorf("type id %d not defined\n", id)
	}
	decodeUint(dec.state)
	dec.printItem(indent, id)
}

func (dec *Decoder) printItem(indent int, id typeId) {
	if dump {
		fmt.Printf("print item %d bytes: % x\n", dec.state.b.Len(), dec.state.b.Bytes())
	}
	_, ok := builtinIdToType[id]
	if ok {
		dec.printBuiltin(indent, id)
		return
	}
	wire, ok := dec.wireType[id]
	if !ok {
		errorf("type id %d not defined\n", id)
	}
	switch {
	case wire.arrayT != nil:
		dec.printArray(indent, wire)
	case wire.mapT != nil:
		dec.printMap(indent, wire)
	case wire.sliceT != nil:
		dec.printSlice(indent, wire)
	case wire.structT != nil:
		dec.debugStruct(indent, id, wire)
	}
}

func (dec *Decoder) printArray(indent int, wire *wireType) {
	elemId := wire.arrayT.Elem
	n := int(decodeUint(dec.state))
	for i := 0; i < n && dec.err == nil; i++ {
		dec.printItem(indent, elemId)
	}
	if n != wire.arrayT.Len {
		tab(indent)
		fmt.Printf("(wrong length for array: %d should be %d)\n", n, wire.arrayT.Len)
	}
}

func (dec *Decoder) printMap(indent int, wire *wireType) {
	keyId := wire.mapT.Key
	elemId := wire.mapT.Elem
	n := int(decodeUint(dec.state))
	for i := 0; i < n && dec.err == nil; i++ {
		dec.printItem(indent, keyId)
		dec.printItem(indent+1, elemId)
	}
}

func (dec *Decoder) printSlice(indent int, wire *wireType) {
	elemId := wire.sliceT.Elem
	n := int(decodeUint(dec.state))
	for i := 0; i < n && dec.err == nil; i++ {
		dec.printItem(indent, elemId)
	}
}

func (dec *Decoder) printBuiltin(indent int, id typeId) {
	tab(indent)
	switch id {
	case tBool:
		if decodeInt(dec.state) == 0 {
			fmt.Printf("false\n")
		} else {
			fmt.Printf("true\n")
		}
	case tInt:
		fmt.Printf("%d\n", decodeInt(dec.state))
	case tUint:
		fmt.Printf("%d\n", decodeUint(dec.state))
	case tFloat:
		fmt.Printf("%g\n", floatFromBits(decodeUint(dec.state)))
	case tBytes:
		b := make([]byte, decodeUint(dec.state))
		dec.state.b.Read(b)
		fmt.Printf("% x\n", b)
	case tString:
		b := make([]byte, decodeUint(dec.state))
		dec.state.b.Read(b)
		fmt.Printf("%q\n", b)
	case tInterface:
		b := make([]byte, decodeUint(dec.state))
		dec.state.b.Read(b)
		if len(b) == 0 {
			fmt.Printf("nil interface")
		} else {
			fmt.Printf("interface value; type %q\n", b)
			dec.debugFromBuffer(indent, true)
		}
	default:
		fmt.Print("unknown\n")
	}
}

func (dec *Decoder) debugStruct(indent int, id typeId, wire *wireType) {
	tab(indent)
	fmt.Printf("%s struct {\n", id.Name())
	strct := wire.structT
	state := newDecodeState(dec, dec.state.b)
	state.fieldnum = -1
	for dec.err == nil {
		delta := int(decodeUint(state))
		if delta < 0 {
			errorf("gob decode: corrupted data: negative delta")
		}
		if delta == 0 { // struct terminator is zero delta fieldnum
			break
		}
		fieldNum := state.fieldnum + delta
		if fieldNum < 0 || fieldNum >= len(strct.field) {
			errorf("field number out of range")
			break
		}
		tab(indent)
		fmt.Printf("%s(%d):\n", wire.structT.field[fieldNum].name, fieldNum)
		dec.printItem(indent+1, strct.field[fieldNum].id)
		state.fieldnum = fieldNum
	}
	tab(indent)
	fmt.Printf(" } // end %s struct\n", id.Name())
}

func tab(indent int) {
	for i, w := 0, 0; i < indent; i += w {
		w = 10
		if i+w > indent {
			w = indent - i
		}
		fmt.Print("\t\t\t\t\t\t\t\t\t\t"[:w])
	}
}
