// Copyright (c) Faye Amacker. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

package cbor

import (
	"errors"
	"io"
	"reflect"
)

// Decoder reads and decodes CBOR values from an input stream.
type Decoder struct {
	r         io.Reader
	buf       []byte
	d         decodeState
	off       int // start of unread data in buf
	bytesRead int
}

// NewDecoder returns a new decoder that reads from r using the default decoding options.
func NewDecoder(r io.Reader) *Decoder {
	return defaultDecMode.NewDecoder(r)
}

// Decode reads the next CBOR-encoded value from its input and stores it in
// the value pointed to by v.
func (dec *Decoder) Decode(v interface{}) error {
	if len(dec.buf) == dec.off {
		if n, err := dec.read(); n == 0 {
			return err
		}
	}

	dec.d.reset(dec.buf[dec.off:])
	err := dec.d.value(v)
	dec.off += dec.d.off
	dec.bytesRead += dec.d.off
	if err != nil {
		if err != io.ErrUnexpectedEOF {
			return err
		}
		// Need to read more data.
		if n, e := dec.read(); n == 0 {
			return e
		}
		return dec.Decode(v)
	}
	return nil
}

// NumBytesRead returns the number of bytes read.
func (dec *Decoder) NumBytesRead() int {
	return dec.bytesRead
}

func (dec *Decoder) read() (int, error) {
	// Copy unread data over read data and reset off to 0.
	if dec.off > 0 {
		n := copy(dec.buf, dec.buf[dec.off:])
		dec.buf = dec.buf[:n]
		dec.off = 0
	}

	// Grow buf if needed.
	const minRead = 512
	if cap(dec.buf)-len(dec.buf) < minRead {
		newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead)
		copy(newBuf, dec.buf)
		dec.buf = newBuf
	}

	// Read from reader and reslice buf.
	n, err := dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)])
	dec.buf = dec.buf[0 : len(dec.buf)+n]
	return n, err
}

// Encoder writes CBOR values to an output stream.
type Encoder struct {
	w          io.Writer
	em         *encMode
	e          *encodeState
	indefTypes []cborType
}

// NewEncoder returns a new encoder that writes to w using the default encoding options.
func NewEncoder(w io.Writer) *Encoder {
	return defaultEncMode.NewEncoder(w)
}

// Encode writes the CBOR encoding of v to the stream.
func (enc *Encoder) Encode(v interface{}) error {
	if len(enc.indefTypes) > 0 && v != nil {
		indefType := enc.indefTypes[len(enc.indefTypes)-1]
		if indefType == cborTypeTextString {
			k := reflect.TypeOf(v).Kind()
			if k != reflect.String {
				return errors.New("cbor: cannot encode item type " + k.String() + " for indefinite-length text string")
			}
		} else if indefType == cborTypeByteString {
			t := reflect.TypeOf(v)
			k := t.Kind()
			if (k != reflect.Array && k != reflect.Slice) || t.Elem().Kind() != reflect.Uint8 {
				return errors.New("cbor: cannot encode item type " + k.String() + " for indefinite-length byte string")
			}
		}
	}

	err := encode(enc.e, enc.em, reflect.ValueOf(v))
	if err == nil {
		_, err = enc.e.WriteTo(enc.w)
	}
	enc.e.Reset()
	return err
}

// StartIndefiniteByteString starts byte string encoding of indefinite length.
// Subsequent calls of (*Encoder).Encode() encodes definite length byte strings
// ("chunks") as one continguous string until EndIndefinite is called.
func (enc *Encoder) StartIndefiniteByteString() error {
	return enc.startIndefinite(cborTypeByteString)
}

// StartIndefiniteTextString starts text string encoding of indefinite length.
// Subsequent calls of (*Encoder).Encode() encodes definite length text strings
// ("chunks") as one continguous string until EndIndefinite is called.
func (enc *Encoder) StartIndefiniteTextString() error {
	return enc.startIndefinite(cborTypeTextString)
}

// StartIndefiniteArray starts array encoding of indefinite length.
// Subsequent calls of (*Encoder).Encode() encodes elements of the array
// until EndIndefinite is called.
func (enc *Encoder) StartIndefiniteArray() error {
	return enc.startIndefinite(cborTypeArray)
}

// StartIndefiniteMap starts array encoding of indefinite length.
// Subsequent calls of (*Encoder).Encode() encodes elements of the map
// until EndIndefinite is called.
func (enc *Encoder) StartIndefiniteMap() error {
	return enc.startIndefinite(cborTypeMap)
}

// EndIndefinite closes last opened indefinite length value.
func (enc *Encoder) EndIndefinite() error {
	if len(enc.indefTypes) == 0 {
		return errors.New("cbor: cannot encode \"break\" code outside indefinite length values")
	}
	_, err := enc.w.Write([]byte{0xff})
	if err == nil {
		enc.indefTypes = enc.indefTypes[:len(enc.indefTypes)-1]
	}
	return err
}

var cborIndefHeader = map[cborType][]byte{
	cborTypeByteString: {0x5f},
	cborTypeTextString: {0x7f},
	cborTypeArray:      {0x9f},
	cborTypeMap:        {0xbf},
}

func (enc *Encoder) startIndefinite(typ cborType) error {
	if enc.em.indefLength == IndefLengthForbidden {
		return &IndefiniteLengthError{typ}
	}
	_, err := enc.w.Write(cborIndefHeader[typ])
	if err == nil {
		enc.indefTypes = append(enc.indefTypes, typ)
	}
	return err
}

// RawMessage is a raw encoded CBOR value. It implements Marshaler and
// Unmarshaler interfaces and can be used to delay CBOR decoding or
// precompute a CBOR encoding.
type RawMessage []byte

// MarshalCBOR returns m as the CBOR encoding of m.
func (m RawMessage) MarshalCBOR() ([]byte, error) {
	if len(m) == 0 {
		return cborNil, nil
	}
	return m, nil
}

// UnmarshalCBOR sets *m to a copy of data.
func (m *RawMessage) UnmarshalCBOR(data []byte) error {
	if m == nil {
		return errors.New("cbor.RawMessage: UnmarshalCBOR on nil pointer")
	}
	*m = append((*m)[0:0], data...)
	return nil
}
