package mssql

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"strconv"
	"time"
)

// fixed-length data types
// http://msdn.microsoft.com/en-us/library/dd341171.aspx
const (
	typeNull     = 0x1f
	typeInt1     = 0x30
	typeBit      = 0x32
	typeInt2     = 0x34
	typeInt4     = 0x38
	typeDateTim4 = 0x3a
	typeFlt4     = 0x3b
	typeMoney    = 0x3c
	typeDateTime = 0x3d
	typeFlt8     = 0x3e
	typeMoney4   = 0x7a
	typeInt8     = 0x7f
)

// variable-length data types
// http://msdn.microsoft.com/en-us/library/dd358341.aspx
const (
	// byte len types
	typeGuid            = 0x24
	typeIntN            = 0x26
	typeDecimal         = 0x37 // legacy
	typeNumeric         = 0x3f // legacy
	typeBitN            = 0x68
	typeDecimalN        = 0x6a
	typeNumericN        = 0x6c
	typeFltN            = 0x6d
	typeMoneyN          = 0x6e
	typeDateTimeN       = 0x6f
	typeDateN           = 0x28
	typeTimeN           = 0x29
	typeDateTime2N      = 0x2a
	typeDateTimeOffsetN = 0x2b
	typeChar            = 0x2f // legacy
	typeVarChar         = 0x27 // legacy
	typeBinary          = 0x2d // legacy
	typeVarBinary       = 0x25 // legacy

	// short length types
	typeBigVarBin  = 0xa5
	typeBigVarChar = 0xa7
	typeBigBinary  = 0xad
	typeBigChar    = 0xaf
	typeNVarChar   = 0xe7
	typeNChar      = 0xef
	typeXml        = 0xf1
	typeUdt        = 0xf0

	// long length types
	typeText    = 0x23
	typeImage   = 0x22
	typeNText   = 0x63
	typeVariant = 0x62
)

// TYPE_INFO rule
// http://msdn.microsoft.com/en-us/library/dd358284.aspx
type typeInfo struct {
	TypeId    uint8
	Size      int
	Scale     uint8
	Prec      uint8
	Buffer    []byte
	Collation collation
	Reader    func(ti *typeInfo, r *tdsBuffer) (res interface{})
	Writer    func(w io.Writer, ti typeInfo, buf []byte) (err error)
}

func readTypeInfo(r *tdsBuffer) (res typeInfo) {
	res.TypeId = r.byte()
	switch res.TypeId {
	case typeNull, typeInt1, typeBit, typeInt2, typeInt4, typeDateTim4,
		typeFlt4, typeMoney, typeDateTime, typeFlt8, typeMoney4, typeInt8:
		// those are fixed length types
		switch res.TypeId {
		case typeNull:
			res.Size = 0
		case typeInt1, typeBit:
			res.Size = 1
		case typeInt2:
			res.Size = 2
		case typeInt4, typeDateTim4, typeFlt4, typeMoney4:
			res.Size = 4
		case typeMoney, typeDateTime, typeFlt8, typeInt8:
			res.Size = 8
		}
		res.Reader = readFixedType
		res.Buffer = make([]byte, res.Size)
	default: // all others are VARLENTYPE
		readVarLen(&res, r)
	}
	return
}

func writeTypeInfo(w io.Writer, ti *typeInfo) (err error) {
	err = binary.Write(w, binary.LittleEndian, ti.TypeId)
	if err != nil {
		return
	}
	switch ti.TypeId {
	case typeNull, typeInt1, typeBit, typeInt2, typeInt4, typeDateTim4,
		typeFlt4, typeMoney, typeDateTime, typeFlt8, typeMoney4, typeInt8:
		// those are fixed length types
	default: // all others are VARLENTYPE
		err = writeVarLen(w, ti)
		if err != nil {
			return
		}
	}
	return
}

func writeVarLen(w io.Writer, ti *typeInfo) (err error) {
	switch ti.TypeId {
	case typeDateN:

	case typeTimeN, typeDateTime2N, typeDateTimeOffsetN:
		if err = binary.Write(w, binary.LittleEndian, ti.Scale); err != nil {
			return
		}
		ti.Writer = writeByteLenType
	case typeGuid, typeIntN, typeDecimal, typeNumeric,
		typeBitN, typeDecimalN, typeNumericN, typeFltN,
		typeMoneyN, typeDateTimeN, typeChar,
		typeVarChar, typeBinary, typeVarBinary:
		// byle len types
		if ti.Size > 0xff {
			panic("Invalid size for BYLELEN_TYPE")
		}
		if err = binary.Write(w, binary.LittleEndian, uint8(ti.Size)); err != nil {
			return
		}
		switch ti.TypeId {
		case typeDecimal, typeNumeric, typeDecimalN, typeNumericN:
			err = binary.Write(w, binary.LittleEndian, ti.Prec)
			if err != nil {
				return
			}
			err = binary.Write(w, binary.LittleEndian, ti.Scale)
			if err != nil {
				return
			}
		}
		ti.Writer = writeByteLenType
	case typeBigVarBin, typeBigVarChar, typeBigBinary, typeBigChar,
		typeNVarChar, typeNChar, typeXml, typeUdt:
		// short len types
		if ti.Size > 8000 || ti.Size == 0 {
			if err = binary.Write(w, binary.LittleEndian, uint16(0xffff)); err != nil {
				return
			}
			ti.Writer = writePLPType
		} else {
			if err = binary.Write(w, binary.LittleEndian, uint16(ti.Size)); err != nil {
				return
			}
			ti.Writer = writeShortLenType
		}
		switch ti.TypeId {
		case typeBigVarChar, typeBigChar, typeNVarChar, typeNChar:
			if err = writeCollation(w, ti.Collation); err != nil {
				return
			}
		case typeXml:
			var schemapresent uint8 = 0
			if err = binary.Write(w, binary.LittleEndian, schemapresent); err != nil {
				return
			}
		}
	case typeText, typeImage, typeNText, typeVariant:
		// LONGLEN_TYPE
		panic("LONGLEN_TYPE not implemented")
	default:
		panic("Invalid type")
	}
	return
}

// http://msdn.microsoft.com/en-us/library/ee780895.aspx
func decodeDateTim4(buf []byte) time.Time {
	days := binary.LittleEndian.Uint16(buf)
	mins := binary.LittleEndian.Uint16(buf[2:])
	return time.Date(1900, 1, 1+int(days),
		0, int(mins), 0, 0, time.UTC)
}

func decodeDateTime(buf []byte) time.Time {
	days := int32(binary.LittleEndian.Uint32(buf))
	tm := binary.LittleEndian.Uint32(buf[4:])
	ns := int(math.Trunc(float64(tm%300)/0.3+0.5)) * 1000000
	secs := int(tm / 300)
	return time.Date(1900, 1, 1+int(days),
		0, 0, secs, ns, time.UTC)
}

func readFixedType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
	r.ReadFull(ti.Buffer)
	buf := ti.Buffer
	switch ti.TypeId {
	case typeNull:
		return nil
	case typeInt1:
		return int64(buf[0])
	case typeBit:
		return buf[0] != 0
	case typeInt2:
		return int64(int16(binary.LittleEndian.Uint16(buf)))
	case typeInt4:
		return int64(int32(binary.LittleEndian.Uint32(buf)))
	case typeDateTim4:
		return decodeDateTim4(buf)
	case typeFlt4:
		return math.Float32frombits(binary.LittleEndian.Uint32(buf))
	case typeMoney4:
		return decodeMoney4(buf)
	case typeMoney:
		return decodeMoney(buf)
	case typeDateTime:
		return decodeDateTime(buf)
	case typeFlt8:
		return math.Float64frombits(binary.LittleEndian.Uint64(buf))
	case typeInt8:
		return int64(binary.LittleEndian.Uint64(buf))
	default:
		badStreamPanicf("Invalid typeid")
	}
	panic("shoulnd't get here")
}

func writeFixedType(w io.Writer, ti typeInfo, buf []byte) (err error) {
	_, err = w.Write(buf)
	return
}

func readByteLenType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
	size := r.byte()
	if size == 0 {
		return nil
	}
	r.ReadFull(ti.Buffer[:size])
	buf := ti.Buffer[:size]
	switch ti.TypeId {
	case typeDateN:
		if len(buf) != 3 {
			badStreamPanicf("Invalid size for DATENTYPE")
		}
		return decodeDate(buf)
	case typeTimeN:
		return decodeTime(ti.Scale, buf)
	case typeDateTime2N:
		return decodeDateTime2(ti.Scale, buf)
	case typeDateTimeOffsetN:
		return decodeDateTimeOffset(ti.Scale, buf)
	case typeGuid:
		return decodeGuid(buf)
	case typeIntN:
		switch len(buf) {
		case 1:
			return int64(buf[0])
		case 2:
			return int64(int16((binary.LittleEndian.Uint16(buf))))
		case 4:
			return int64(int32(binary.LittleEndian.Uint32(buf)))
		case 8:
			return int64(binary.LittleEndian.Uint64(buf))
		default:
			badStreamPanicf("Invalid size for INTNTYPE")
		}
	case typeDecimal, typeNumeric, typeDecimalN, typeNumericN:
		return decodeDecimal(ti.Prec, ti.Scale, buf)
	case typeBitN:
		if len(buf) != 1 {
			badStreamPanicf("Invalid size for BITNTYPE")
		}
		return buf[0] != 0
	case typeFltN:
		switch len(buf) {
		case 4:
			return float64(math.Float32frombits(binary.LittleEndian.Uint32(buf)))
		case 8:
			return math.Float64frombits(binary.LittleEndian.Uint64(buf))
		default:
			badStreamPanicf("Invalid size for FLTNTYPE")
		}
	case typeMoneyN:
		switch len(buf) {
		case 4:
			return decodeMoney4(buf)
		case 8:
			return decodeMoney(buf)
		default:
			badStreamPanicf("Invalid size for MONEYNTYPE")
		}
	case typeDateTimeN:
		switch len(buf) {
		case 4:
			return decodeDateTim4(buf)
		case 8:
			return decodeDateTime(buf)
		default:
			badStreamPanicf("Invalid size for DATETIMENTYPE")
		}
	case typeChar, typeVarChar:
		return decodeChar(ti.Collation, buf)
	case typeBinary, typeVarBinary:
		// a copy, because the backing array for ti.Buffer is reused
		// and can be overwritten by the next row while this row waits
		// in a buffered chan
		cpy := make([]byte, len(buf))
		copy(cpy, buf)
		return cpy
	default:
		badStreamPanicf("Invalid typeid")
	}
	panic("shoulnd't get here")
}

func writeByteLenType(w io.Writer, ti typeInfo, buf []byte) (err error) {
	if ti.Size > 0xff {
		panic("Invalid size for BYTELEN_TYPE")
	}
	err = binary.Write(w, binary.LittleEndian, uint8(ti.Size))
	if err != nil {
		return
	}
	_, err = w.Write(buf)
	return
}

func readShortLenType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
	size := r.uint16()
	if size == 0xffff {
		return nil
	}
	r.ReadFull(ti.Buffer[:size])
	buf := ti.Buffer[:size]
	switch ti.TypeId {
	case typeBigVarChar, typeBigChar:
		return decodeChar(ti.Collation, buf)
	case typeBigVarBin, typeBigBinary:
		// a copy, because the backing array for ti.Buffer is reused
		// and can be overwritten by the next row while this row waits
		// in a buffered chan
		cpy := make([]byte, len(buf))
		copy(cpy, buf)
		return cpy
	case typeNVarChar, typeNChar:
		return decodeNChar(buf)
	case typeUdt:
		return decodeUdt(*ti, buf)
	default:
		badStreamPanicf("Invalid typeid")
	}
	panic("shoulnd't get here")
}

func writeShortLenType(w io.Writer, ti typeInfo, buf []byte) (err error) {
	if buf == nil {
		err = binary.Write(w, binary.LittleEndian, uint16(0xffff))
		return
	}
	if ti.Size > 0xfffe {
		panic("Invalid size for USHORTLEN_TYPE")
	}
	err = binary.Write(w, binary.LittleEndian, uint16(ti.Size))
	if err != nil {
		return
	}
	_, err = w.Write(buf)
	return
}

func readLongLenType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
	// information about this format can be found here:
	// http://msdn.microsoft.com/en-us/library/dd304783.aspx
	// and here:
	// http://msdn.microsoft.com/en-us/library/dd357254.aspx
	textptrsize := r.byte()
	if textptrsize == 0 {
		return nil
	}
	textptr := make([]byte, textptrsize)
	r.ReadFull(textptr)
	timestamp := r.uint64()
	_ = timestamp // ignore timestamp
	size := r.int32()
	if size == -1 {
		return nil
	}
	buf := make([]byte, size)
	r.ReadFull(buf)
	switch ti.TypeId {
	case typeText:
		return decodeChar(ti.Collation, buf)
	case typeImage:
		return buf
	case typeNText:
		return decodeNChar(buf)
	default:
		badStreamPanicf("Invalid typeid")
	}
	panic("shoulnd't get here")
}

// reads variant value
// http://msdn.microsoft.com/en-us/library/dd303302.aspx
func readVariantType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
	size := r.int32()
	if size == 0 {
		return nil
	}
	vartype := r.byte()
	propbytes := int32(r.byte())
	switch vartype {
	case typeGuid:
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return buf
	case typeBit:
		return r.byte() != 0
	case typeInt1:
		return int64(r.byte())
	case typeInt2:
		return int64(int16(r.uint16()))
	case typeInt4:
		return int64(r.int32())
	case typeInt8:
		return int64(r.uint64())
	case typeDateTime:
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeDateTime(buf)
	case typeDateTim4:
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeDateTim4(buf)
	case typeFlt4:
		return float64(math.Float32frombits(r.uint32()))
	case typeFlt8:
		return math.Float64frombits(r.uint64())
	case typeMoney4:
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeMoney4(buf)
	case typeMoney:
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeMoney(buf)
	case typeDateN:
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeDate(buf)
	case typeTimeN:
		scale := r.byte()
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeTime(scale, buf)
	case typeDateTime2N:
		scale := r.byte()
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeDateTime2(scale, buf)
	case typeDateTimeOffsetN:
		scale := r.byte()
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeDateTimeOffset(scale, buf)
	case typeBigVarBin, typeBigBinary:
		r.uint16() // max length, ignoring
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return buf
	case typeDecimalN, typeNumericN:
		prec := r.byte()
		scale := r.byte()
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeDecimal(prec, scale, buf)
	case typeBigVarChar, typeBigChar:
		col := readCollation(r)
		r.uint16() // max length, ignoring
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeChar(col, buf)
	case typeNVarChar, typeNChar:
		_ = readCollation(r)
		r.uint16() // max length, ignoring
		buf := make([]byte, size-2-propbytes)
		r.ReadFull(buf)
		return decodeNChar(buf)
	default:
		badStreamPanicf("Invalid variant typeid")
	}
	panic("shoulnd't get here")
}

// partially length prefixed stream
// http://msdn.microsoft.com/en-us/library/dd340469.aspx
func readPLPType(ti *typeInfo, r *tdsBuffer) (res interface{}) {
	size := r.uint64()
	var buf *bytes.Buffer
	switch size {
	case 0xffffffffffffffff:
		// null
		return nil
	case 0xfffffffffffffffe:
		// size unknown
		buf = bytes.NewBuffer(make([]byte, 0, 1000))
	default:
		buf = bytes.NewBuffer(make([]byte, 0, size))
	}
	for true {
		chunksize := r.uint32()
		if chunksize == 0 {
			break
		}
		if _, err := io.CopyN(buf, r, int64(chunksize)); err != nil {
			badStreamPanicf("Reading PLP type failed: %s", err.Error())
		}
	}
	switch ti.TypeId {
	case typeXml:
		return decodeXml(*ti, buf.Bytes())
	case typeBigVarChar, typeBigChar, typeText:
		return decodeChar(ti.Collation, buf.Bytes())
	case typeBigVarBin, typeBigBinary, typeImage:
		return buf.Bytes()
	case typeNVarChar, typeNChar, typeNText:
		return decodeNChar(buf.Bytes())
	case typeUdt:
		return decodeUdt(*ti, buf.Bytes())
	}
	panic("shoulnd't get here")
}

func writePLPType(w io.Writer, ti typeInfo, buf []byte) (err error) {
	if err = binary.Write(w, binary.LittleEndian, uint64(len(buf))); err != nil {
		return
	}
	for {
		chunksize := uint32(len(buf))
		if err = binary.Write(w, binary.LittleEndian, chunksize); err != nil {
			return
		}
		if chunksize == 0 {
			return
		}
		if _, err = w.Write(buf[:chunksize]); err != nil {
			return
		}
		buf = buf[chunksize:]
	}
}

func readVarLen(ti *typeInfo, r *tdsBuffer) {
	switch ti.TypeId {
	case typeDateN:
		ti.Size = 3
		ti.Reader = readByteLenType
		ti.Buffer = make([]byte, ti.Size)
	case typeTimeN, typeDateTime2N, typeDateTimeOffsetN:
		ti.Scale = r.byte()
		switch ti.Scale {
		case 0, 1, 2:
			ti.Size = 3
		case 3, 4:
			ti.Size = 4
		case 5, 6, 7:
			ti.Size = 5
		default:
			badStreamPanicf("Invalid scale for TIME/DATETIME2/DATETIMEOFFSET type")
		}
		switch ti.TypeId {
		case typeDateTime2N:
			ti.Size += 3
		case typeDateTimeOffsetN:
			ti.Size += 5
		}
		ti.Reader = readByteLenType
		ti.Buffer = make([]byte, ti.Size)
	case typeGuid, typeIntN, typeDecimal, typeNumeric,
		typeBitN, typeDecimalN, typeNumericN, typeFltN,
		typeMoneyN, typeDateTimeN, typeChar,
		typeVarChar, typeBinary, typeVarBinary:
		// byle len types
		ti.Size = int(r.byte())
		ti.Buffer = make([]byte, ti.Size)
		switch ti.TypeId {
		case typeDecimal, typeNumeric, typeDecimalN, typeNumericN:
			ti.Prec = r.byte()
			ti.Scale = r.byte()
		}
		ti.Reader = readByteLenType
	case typeXml:
		schemapresent := r.byte()
		if schemapresent != 0 {
			// just ignore this for now
			// dbname
			r.BVarChar()
			// owning schema
			r.BVarChar()
			// xml schema collection
			r.UsVarChar()
		}
		ti.Reader = readPLPType
	case typeBigVarBin, typeBigVarChar, typeBigBinary, typeBigChar,
		typeNVarChar, typeNChar, typeUdt:
		// short len types
		ti.Size = int(r.uint16())
		switch ti.TypeId {
		case typeBigVarChar, typeBigChar, typeNVarChar, typeNChar:
			ti.Collation = readCollation(r)
		}
		if ti.Size == 0xffff {
			ti.Reader = readPLPType
		} else {
			ti.Buffer = make([]byte, ti.Size)
			ti.Reader = readShortLenType
		}
	case typeText, typeImage, typeNText, typeVariant:
		// LONGLEN_TYPE
		ti.Size = int(r.int32())
		switch ti.TypeId {
		case typeText, typeNText:
			ti.Collation = readCollation(r)
			// ignore tablenames
			numparts := int(r.byte())
			for i := 0; i < numparts; i++ {
				r.UsVarChar()
			}
			ti.Reader = readLongLenType
		case typeImage:
			// ignore tablenames
			numparts := int(r.byte())
			for i := 0; i < numparts; i++ {
				r.UsVarChar()
			}
			ti.Reader = readLongLenType
		case typeXml:
			panic("XMLTYPE not implemented")
		case typeVariant:
			ti.Reader = readVariantType
		}
	default:
		badStreamPanicf("Invalid type %d", ti.TypeId)
	}
	return
}

func decodeMoney(buf []byte) []byte {
	money := int64(uint64(buf[4]) |
		uint64(buf[5])<<8 |
		uint64(buf[6])<<16 |
		uint64(buf[7])<<24 |
		uint64(buf[0])<<32 |
		uint64(buf[1])<<40 |
		uint64(buf[2])<<48 |
		uint64(buf[3])<<56)
	return scaleBytes(strconv.FormatInt(money, 10), 4)
}

func decodeMoney4(buf []byte) []byte {
	money := int32(binary.LittleEndian.Uint32(buf[0:4]))
	return scaleBytes(strconv.FormatInt(int64(money), 10), 4)
}

func decodeGuid(buf []byte) []byte {
	res := make([]byte, 16)
	copy(res, buf)
	return res
}

func decodeDecimal(prec uint8, scale uint8, buf []byte) []byte {
	var sign uint8
	sign = buf[0]
	dec := Decimal{
		positive: sign != 0,
		prec:     prec,
		scale:    scale,
	}
	buf = buf[1:]
	l := len(buf) / 4
	for i := 0; i < l; i++ {
		dec.integer[i] = binary.LittleEndian.Uint32(buf[0:4])
		buf = buf[4:]
	}
	return dec.Bytes()
}

// http://msdn.microsoft.com/en-us/library/ee780895.aspx
func decodeDateInt(buf []byte) (days int) {
	return int(buf[0]) + int(buf[1])*256 + int(buf[2])*256*256
}

func decodeDate(buf []byte) time.Time {
	return time.Date(1, 1, 1+decodeDateInt(buf), 0, 0, 0, 0, time.UTC)
}

func decodeTimeInt(scale uint8, buf []byte) (sec int, ns int) {
	var acc uint64 = 0
	for i := len(buf) - 1; i >= 0; i-- {
		acc <<= 8
		acc |= uint64(buf[i])
	}
	for i := 0; i < 7-int(scale); i++ {
		acc *= 10
	}
	nsbig := acc * 100
	sec = int(nsbig / 1000000000)
	ns = int(nsbig % 1000000000)
	return
}

func decodeTime(scale uint8, buf []byte) time.Time {
	sec, ns := decodeTimeInt(scale, buf)
	return time.Date(1, 1, 1, 0, 0, sec, ns, time.UTC)
}

func decodeDateTime2(scale uint8, buf []byte) time.Time {
	timesize := len(buf) - 3
	sec, ns := decodeTimeInt(scale, buf[:timesize])
	days := decodeDateInt(buf[timesize:])
	return time.Date(1, 1, 1+days, 0, 0, sec, ns, time.UTC)
}

func decodeDateTimeOffset(scale uint8, buf []byte) time.Time {
	timesize := len(buf) - 3 - 2
	sec, ns := decodeTimeInt(scale, buf[:timesize])
	buf = buf[timesize:]
	days := decodeDateInt(buf[:3])
	buf = buf[3:]
	offset := int(int16(binary.LittleEndian.Uint16(buf))) // in mins
	return time.Date(1, 1, 1+days, 0, 0, sec+offset*60, ns,
		time.FixedZone("", offset*60))
}

func divFloor(x int64, y int64) int64 {
	q := x / y
	r := x % y
	if r != 0 && ((r < 0) != (y < 0)) {
		q--
	}
	return q
}

func dateTime2(t time.Time) (days int32, ns int64) {
	// number of days since Jan 1 1970 UTC
	days64 := divFloor(t.Unix(), 24*60*60)
	// number of days since Jan 1 1 UTC
	days = int32(days64) + 1969*365 + 1969/4 - 1969/100 + 1969/400
	// number of seconds within day
	secs := t.Unix() - days64*24*60*60
	// number of nanoseconds within day
	ns = secs*1e9 + int64(t.Nanosecond())
	return
}

func decodeChar(col collation, buf []byte) string {
	return charset2utf8(col, buf)
}

func decodeUcs2(buf []byte) string {
	res, err := ucs22str(buf)
	if err != nil {
		badStreamPanicf("Invalid UCS2 encoding: %s", err.Error())
	}
	return res
}

func decodeNChar(buf []byte) string {
	return decodeUcs2(buf)
}

func decodeXml(ti typeInfo, buf []byte) string {
	return decodeUcs2(buf)
}

func decodeUdt(ti typeInfo, buf []byte) int {
	panic("Not implemented")
}

func makeDecl(ti typeInfo) string {
	switch ti.TypeId {
	case typeInt8:
		return "bigint"
	case typeFlt4:
		return "real"
	case typeIntN:
		switch ti.Size {
		case 1:
			return "tinyint"
		case 2:
			return "smallint"
		case 4:
			return "int"
		case 8:
			return "bigint"
		default:
			panic("invalid size of INTNTYPE")
		}
	case typeFlt8:
		return "float"
	case typeFltN:
		switch ti.Size {
		case 4:
			return "real"
		case 8:
			return "float"
		default:
			panic("invalid size of FLNNTYPE")
		}
	case typeBigVarBin:
		if ti.Size > 8000 || ti.Size == 0 {
			return fmt.Sprintf("varbinary(max)")
		} else {
			return fmt.Sprintf("varbinary(%d)", ti.Size)
		}
	case typeNVarChar:
		if ti.Size > 8000 || ti.Size == 0 {
			return fmt.Sprintf("nvarchar(max)")
		} else {
			return fmt.Sprintf("nvarchar(%d)", ti.Size/2)
		}
	case typeBit, typeBitN:
		return "bit"
	case typeDateTimeN:
		return "datetime"
	case typeDateTimeOffsetN:
		return fmt.Sprintf("datetimeoffset(%d)", ti.Scale)
	default:
		panic(fmt.Sprintf("not implemented makeDecl for type %d", ti.TypeId))
	}
}
