package message

// Below code is largely inspired from the standard golang library encoding/asn
// If put BEGIN / END tags in the comments to give the original library name
import (
	//	"errors"
	"fmt"
	"math/big"
	// "strconv"
	// "time"
)

//
// BEGIN: encoding/asn1/common.go
//

// 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.
const (
	tagBoolean = 1
	tagInteger = 2
	// tagBitString   = 3
	tagOctetString = 4
	// tagOID         = 6
	tagEnum = 10
	// tagUTF8String = 12
	tagSequence = 16
	tagSet      = 17
	// tagPrintableString = 19
	// tagT61String       = 20
	// tagIA5String       = 22
	// tagUTCTime         = 23
	// tagGeneralizedTime = 24
	tagGeneralString = 27
)

var tagNames = map[int]string{
	tagBoolean:     "BOOLEAN",
	tagInteger:     "INTEGER",
	tagOctetString: "OCTET STRING",
	tagEnum:        "ENUM",
	tagSequence: "SEQUENCE",
	tagSet:      "SET",
}

const (
	classUniversal       = 0
	classApplication     = 1
	classContextSpecific = 2
	// classPrivate         = 3
)

var classNames = map[int]string{
	classUniversal:       "UNIVERSAL",
	classApplication:     "APPLICATION",
	classContextSpecific: "CONTEXT SPECIFIC",
}

const (
	isCompound    = true
	isNotCompound = false
)

var compoundNames = map[bool]string{
	isCompound:    "COMPOUND",
	isNotCompound: "NOT COMPOUND",
}

type TagAndLength struct {
	Class, Tag, Length int
	IsCompound         bool
}

//
// END: encoding/asn1/common.go
//

func (t *TagAndLength) Expect(class int, tag int, isCompound bool) (err error) {
	err = t.ExpectClass(class)
	if err != nil {
		return LdapError{fmt.Sprintf("Expect: %s.", err)}
	}
	err = t.ExpectTag(tag)
	if err != nil {
		return LdapError{fmt.Sprintf("Expect: %s.", err)}
	}
	err = t.ExpectCompound(isCompound)
	if err != nil {
		return LdapError{fmt.Sprintf("Expect: %s.", err)}
	}
	return
}
func (t *TagAndLength) ExpectClass(class int) (err error) {
	if class != t.Class {
		err = SyntaxError{fmt.Sprintf("ExpectClass: wrong tag class: got %d (%s), expected %d (%s)", t.Class, classNames[t.Class], class, classNames[class])}
	}
	return
}
func (t *TagAndLength) ExpectTag(tag int) (err error) {
	if tag != t.Tag {
		err = SyntaxError{fmt.Sprintf("ExpectTag: wrong tag value: got %d (%s), expected %d (%s)", t.Tag, tagNames[t.Tag], tag, tagNames[tag])}
	}
	return
}
func (t *TagAndLength) ExpectCompound(isCompound bool) (err error) {
	if isCompound != t.IsCompound {
		err = SyntaxError{fmt.Sprintf("ExpectCompound: wrong tag compound: got %t (%s), expected %t (%s)", t.IsCompound, compoundNames[t.IsCompound], isCompound, compoundNames[isCompound])}
	}
	return
}

func ParseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) {
	ret, offset, err = parseTagAndLength(bytes, initOffset)
	return
}

//
// BEGIN encoding/asn1/asn1.go
//

// 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.

// Package asn1 implements parsing of DER-encoded ASN.1 data structures,
// as defined in ITU-T Rec X.690.
//
// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,''
// http://luca.ntop.org/Teaching/Appunti/asn1.html.
// package asn1

// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc
// are different encoding formats for those objects. Here, we'll be dealing
// with DER, the Distinguished Encoding Rules. DER is used in X.509 because
// it's fast to parse and, unlike BER, has a unique encoding for every object.
// When calculating hashes over objects, it's important that the resulting
// bytes be the same at both ends and DER removes this margin of error.
//
// ASN.1 is very complex and this package doesn't attempt to implement
// everything by any means.

//import (
//	"fmt"
//	"math/big"
//	"reflect"
//	"strconv"
//	"time"
//)

// A StructuralError suggests that the ASN.1 data is valid, but the Go type
// which is receiving it doesn't match.
type StructuralError struct {
	Msg string
}

func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }

// A SyntaxError suggests that the ASN.1 data is invalid.
type SyntaxError struct {
	Msg string
}

func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }

// We start by dealing with each of the primitive types in turn.

// BOOLEAN

func parseBool(bytes []byte) (ret bool, err error) {
	if len(bytes) > 1 {
		err = SyntaxError{"invalid boolean: should be encoded on one byte only"}
		return
	} else if len(bytes) == 0 {
		err = SyntaxError{"invalid boolean: no data to read"}
	}

	// DER demands that "If the encoding represents the boolean value TRUE,
	// its single contents octet shall have all eight bits set to one."
	// Thus only 0 and 255 are valid encoded values.
	switch bytes[0] {
	case 0:
		ret = false
	case 0xff:
		ret = true
	default:
		err = SyntaxError{"invalid boolean: should be 0x00 of 0xFF"}
	}

	return
}

func sizeBool(b bool) int {
	return 1
}

func writeBool(bytes *Bytes, b bool) int {
	if b == false {
		return bytes.writeBytes([]byte{0x00})
	} else {
		return bytes.writeBytes([]byte{0xff})
	}
}

// INTEGER

// parseInt64 treats the given bytes as a big-endian, signed integer and
// returns the result.
func parseInt64(bytes []byte) (ret int64, err error) {
	if len(bytes) > 8 {
		// We'll overflow an int64 in this case.
		err = StructuralError{"integer too large"}
		return
	}
	for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
		ret <<= 8
		ret |= int64(bytes[bytesRead])
	}

	// Shift up and down in order to sign extend the result.
	ret <<= 64 - uint8(len(bytes))*8
	ret >>= 64 - uint8(len(bytes))*8
	return
}

func sizeInt64(i int64) int {
	n := 1

	for i > 127 {
		n++
		i >>= 8
	}

	for i < -128 {
		n++
		i >>= 8
	}

	return n
}

func writeInt64(bytes *Bytes, i int64) int {
	n := sizeInt64(i)
	buf := [8]byte{}

	for j := 0; j < n; j++ {
		b := i >> uint((n-1-j)*8)
		buf[j] = byte(b)
	}
	bytes.writeBytes(buf[:n])

	return n
}

// parseInt treats the given bytes as a big-endian, signed integer and returns
// the result.
func parseInt32(bytes []byte) (int32, error) {
	ret64, err := parseInt64(bytes)
	if err != nil {
		return 0, err
	}
	if ret64 != int64(int32(ret64)) {
		return 0, StructuralError{"integer too large"}
	}
	return int32(ret64), nil
}

func sizeInt32(i int32) int {
	return sizeInt64(int64(i))
}

func writeInt32(bytes *Bytes, i int32) int {
	return writeInt64(bytes, int64(i))
}

var bigOne = big.NewInt(1)

// // parseBigInt treats the given bytes as a big-endian, signed integer and returns
// // the result.
// func parseBigInt(bytes []byte) *big.Int {
// 	ret := new(big.Int)
// 	if len(bytes) > 0 && bytes[0]&0x80 == 0x80 {
// 		// This is a negative number.
// 		notBytes := make([]byte, len(bytes))
// 		for i := range notBytes {
// 			notBytes[i] = ^bytes[i]
// 		}
// 		ret.SetBytes(notBytes)
// 		ret.Add(ret, bigOne)
// 		ret.Neg(ret)
// 		return ret
// 	}
// 	ret.SetBytes(bytes)
// 	return ret
// }

// // BIT STRING

// // BitString is the structure to use when you want an ASN.1 BIT STRING type. A
// // bit string is padded up to the nearest byte in memory and the number of
// // valid bits is recorded. Padding bits will be zero.
// type BitString struct {
// 	Bytes     []byte // bits packed into bytes.
// 	BitLength int    // length in bits.
// }

// // At returns the bit at the given index. If the index is out of range it
// // returns false.
// func (b BitString) At(i int) int {
// 	if i < 0 || i >= b.BitLength {
// 		return 0
// 	}
// 	x := i / 8
// 	y := 7 - uint(i%8)
// 	return int(b.Bytes[x]>>y) & 1
// }

// // RightAlign returns a slice where the padding bits are at the beginning. The
// // slice may share memory with the BitString.
// func (b BitString) RightAlign() []byte {
// 	shift := uint(8 - (b.BitLength % 8))
// 	if shift == 8 || len(b.Bytes) == 0 {
// 		return b.Bytes
// 	}

// 	a := make([]byte, len(b.Bytes))
// 	a[0] = b.Bytes[0] >> shift
// 	for i := 1; i < len(b.Bytes); i++ {
// 		a[i] = b.Bytes[i-1] << (8 - shift)
// 		a[i] |= b.Bytes[i] >> shift
// 	}

// 	return a
// }

// // parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
// func parseBitString(bytes []byte) (ret BitString, err error) {
// 	if len(bytes) == 0 {
// 		err = SyntaxError{"zero length BIT STRING"}
// 		return
// 	}
// 	paddingBits := int(bytes[0])
// 	if paddingBits > 7 ||
// 		len(bytes) == 1 && paddingBits > 0 ||
// 		bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {
// 		err = SyntaxError{"invalid padding bits in BIT STRING"}
// 		return
// 	}
// 	ret.BitLength = (len(bytes)-1)*8 - paddingBits
// 	ret.Bytes = bytes[1:]
// 	return
// }

// OBJECT IDENTIFIER

// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
// type ObjectIdentifier []int

// // Equal reports whether oi and other represent the same identifier.
// func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
// 	if len(oi) != len(other) {
// 		return false
// 	}
// 	for i := 0; i < len(oi); i++ {
// 		if oi[i] != other[i] {
// 			return false
// 		}
// 	}

// 	return true
// }

// func (oi ObjectIdentifier) String() string {
// 	var s string

// 	for i, v := range oi {
// 		if i > 0 {
// 			s += "."
// 		}
// 		s += strconv.Itoa(v)
// 	}

// 	return s
// }

// // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
// // returns it. An object identifier is a sequence of variable length integers
// // that are assigned in a hierarchy.
// func parseObjectIdentifier(bytes []byte) (s []int, err error) {
// 	if len(bytes) == 0 {
// 		err = SyntaxError{"zero length OBJECT IDENTIFIER"}
// 		return
// 	}

// 	// In the worst case, we get two elements from the first byte (which is
// 	// encoded differently) and then every varint is a single byte long.
// 	s = make([]int, len(bytes)+1)

// 	// The first varint is 40*value1 + value2:
// 	// According to this packing, value1 can take the values 0, 1 and 2 only.
// 	// When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
// 	// then there are no restrictions on value2.
// 	v, offset, err := parseBase128Int(bytes, 0)
// 	if err != nil {
// 		return
// 	}
// 	if v < 80 {
// 		s[0] = v / 40
// 		s[1] = v % 40
// 	} else {
// 		s[0] = 2
// 		s[1] = v - 80
// 	}

// 	i := 2
// 	for ; offset < len(bytes); i++ {
// 		v, offset, err = parseBase128Int(bytes, offset)
// 		if err != nil {
// 			return
// 		}
// 		s[i] = v
// 	}
// 	s = s[0:i]
// 	return
// }

// ENUMERATED

// An Enumerated is represented as a plain int.
type Enumerated int

// FLAG

// A Flag accepts any data and is set to true if present.
type Flag bool

// parseBase128Int parses a base-128 encoded int from the given offset in the
// given byte slice. It returns the value and the new offset.
func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) {
	offset = initOffset
	for shifted := 0; offset < len(bytes); shifted++ {
		if shifted > 4 {
			err = StructuralError{"base 128 integer too large"}
			return
		}
		ret <<= 7
		b := bytes[offset]
		ret |= int(b & 0x7f)
		offset++
		if b&0x80 == 0 {
			return
		}
	}
	err = SyntaxError{"truncated base 128 integer"}
	return
}

func sizeBase128Int(value int) (size int) {
	for i := value; i > 0; i >>= 7 {
		size++
	}
	return
}

// Write start as the end of the slice and goes back
// We assume we have enough size
func writeBase128Int(bytes *Bytes, value int) (size int) {
	for ; value > 0 || size == 0; value >>= 7 { // Write at least one byte even if the value is 0
		// Get the 7 lowest bits
		b := byte(value) & 0x7f
		if value < 128 {
			b |= 0x80
		}
		bytes.writeBytes([]byte{b})
		size++
	}
	return
}

// // UTCTime

// func parseUTCTime(bytes []byte) (ret time.Time, err error) {
// 	s := string(bytes)
// 	ret, err = time.Parse("0601021504Z0700", s)
// 	if err != nil {
// 		ret, err = time.Parse("060102150405Z0700", s)
// 	}
// 	if err == nil && ret.Year() >= 2050 {
// 		// UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
// 		ret = ret.AddDate(-100, 0, 0)
// 	}

// 	return
// }

// // parseGeneralizedTime parses the GeneralizedTime from the given byte slice
// // and returns the resulting time.
// func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
// 	return time.Parse("20060102150405Z0700", string(bytes))
// }

// // PrintableString

// // parsePrintableString parses a ASN.1 PrintableString from the given byte
// // array and returns it.
// func parsePrintableString(bytes []byte) (ret string, err error) {
// 	for _, b := range bytes {
// 		if !isPrintable(b) {
// 			err = SyntaxError{"PrintableString contains invalid character"}
// 			return
// 		}
// 	}
// 	ret = string(bytes)
// 	return
// }

// // isPrintable returns true iff the given b is in the ASN.1 PrintableString set.
// func isPrintable(b byte) bool {
// 	return 'a' <= b && b <= 'z' ||
// 		'A' <= b && b <= 'Z' ||
// 		'0' <= b && b <= '9' ||
// 		'\'' <= b && b <= ')' ||
// 		'+' <= b && b <= '/' ||
// 		b == ' ' ||
// 		b == ':' ||
// 		b == '=' ||
// 		b == '?' ||
// 		// This is technically not allowed in a PrintableString.
// 		// However, x509 certificates with wildcard strings don't
// 		// always use the correct string type so we permit it.
// 		b == '*'
// }

// // IA5String

// // parseIA5String parses a ASN.1 IA5String (ASCII string) from the given
// // byte slice and returns it.
// func parseIA5String(bytes []byte) (ret string, err error) {
// 	for _, b := range bytes {
// 		if b >= 0x80 {
// 			err = SyntaxError{"IA5String contains invalid character"}
// 			return
// 		}
// 	}
// 	ret = string(bytes)
// 	return
// }

// // T61String

// // parseT61String parses a ASN.1 T61String (8-bit clean string) from the given
// // byte slice and returns it.
// func parseT61String(bytes []byte) (ret string, err error) {
// 	return string(bytes), nil
// }

// UTF8String

// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte
// array and returns it.
// func parseUTF8String(bytes []byte) (ret string, err error) {
// 	return string(bytes), nil
// }
// func sizeUTF8String(s string) int {
// 	return len(s)
// }
// func writeUTF8String(bytes *Bytes, s string) int {
// 	return bytes.writeString(s)
// }

// Octet string
func parseOctetString(bytes []byte) (ret []byte, err error) {
	return bytes, nil
}
func sizeOctetString(s []byte) int {
	return len(s)
}
func writeOctetString(bytes *Bytes, s []byte) int {
	return bytes.writeBytes(s)
}

// A RawValue represents an undecoded ASN.1 object.
type RawValue struct {
	Class, Tag int
	IsCompound bool
	Bytes      []byte
	FullBytes  []byte // includes the tag and length
}

// RawContent is used to signal that the undecoded, DER data needs to be
// preserved for a struct. To use it, the first field of the struct must have
// this type. It's an error for any of the other fields to have this type.
type RawContent []byte

// Tagging

// parseTagAndLength parses an ASN.1 tag and length pair from the given offset
// into a byte slice. It returns the parsed data and the new offset. SET and
// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we
// don't distinguish between ordered and unordered objects in this code.
func parseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) {
	offset = initOffset
	b := bytes[offset]
	offset++
	ret.Class = int(b >> 6)
	ret.IsCompound = b&0x20 == 0x20
	ret.Tag = int(b & 0x1f)

	// If the bottom five bits are set, then the tag number is actually base 128
	// encoded afterwards
	if ret.Tag == 0x1f {
		ret.Tag, offset, err = parseBase128Int(bytes, offset)
		if err != nil {
			return
		}
	}
	if offset >= len(bytes) {
		err = SyntaxError{"truncated tag or length"}
		return
	}
	b = bytes[offset]
	offset++
	if b&0x80 == 0 {
		// The length is encoded in the bottom 7 bits.
		ret.Length = int(b & 0x7f)
	} else {
		// Bottom 7 bits give the number of length bytes to follow.
		numBytes := int(b & 0x7f)
		if numBytes == 0 {
			err = SyntaxError{"indefinite length found (not DER)"}
			return
		}
		ret.Length = 0
		for i := 0; i < numBytes; i++ {
			if offset >= len(bytes) {
				err = SyntaxError{"truncated tag or length"}
				return
			}
			b = bytes[offset]
			offset++
			if ret.Length >= 1<<23 {
				// We can't shift ret.length up without
				// overflowing.
				err = StructuralError{"length too large"}
				return
			}
			ret.Length <<= 8
			ret.Length |= int(b)
			if ret.Length == 0 {
				// DER requires that lengths be minimal.
				err = StructuralError{"superfluous leading zeros in length"}
				return
			}
		}
	}

	return
}

// func writeTagAndLength(out *forkableWriter, t tagAndLength) (err error) {
// 	b := uint8(t.class) << 6
// 	if t.isCompound {
// 		b |= 0x20
// 	}
// 	if t.tag >= 31 {
// 		b |= 0x1f
// 		err = out.WriteByte(b)
// 		if err != nil {
// 			return
// 		}
// 		err = marshalBase128Int(out, int64(t.tag))
// 		if err != nil {
// 			return
// 		}
// 	} else {
// 		b |= uint8(t.tag)
// 		err = out.WriteByte(b)
// 		if err != nil {
// 			return
// 		}
// 	}

// 	if t.length >= 128 {
// 		l := lengthLength(t.length)
// 		err = out.WriteByte(0x80 | byte(l))
// 		if err != nil {
// 			return
// 		}
// 		err = marshalLength(out, t.length)
// 		if err != nil {
// 			return
// 		}
// 	} else {
// 		err = out.WriteByte(byte(t.length))
// 		if err != nil {
// 			return
// 		}
// 	}

// 	return nil
// }

func sizeTagAndLength(tag int, length int) (size int) {
	// Compute the size of the tag
	size = 1
	if tag >= 31 {
		// Long-form identifier if the tag is greater than 30
		// http://en.wikipedia.org/wiki/X.690#Identifier_tags_greater_than_30
		size += sizeBase128Int(tag)
	}
	// Compute the size of the length using the definite form
	// http://en.wikipedia.org/wiki/X.690#The_definite_form
	size += 1
	if length >= 128 {
		size += 1
		for length > 255 {
			size++
			length >>= 8
		}
	}
	return
}

func writeTagAndLength(bytes *Bytes, t TagAndLength) (size int) {
	// We are writing backward, so write the length bytes first
	if t.Length < 0 {
		panic("Can't have a negative length")

	} else if t.Length >= 128 {
		lengthBytes := 0
		val := t.Length
		for val > 0 {
			lengthBytes++
			bytes.writeBytes([]byte{byte(val & 0xff)})
			val >>= 8
		}
		bytes.writeBytes([]byte{byte(0x80 | byte(lengthBytes))})
		size += lengthBytes + 1

	} else if t.Length < 128 {
		size += bytes.writeBytes([]byte{byte(t.Length)})
	}
	// Then write the tag
	b := uint8(t.Class) << 6
	if t.IsCompound {
		b |= 0x20
	}
	if t.Tag >= 31 {
		b |= 0x1f
		size += writeBase128Int(bytes, t.Tag)
	} else {
		b |= uint8(t.Tag)
	}
	size += bytes.writeBytes([]byte{byte(b)})
	return
}

//
// END encoding/asn1/asn1.go
//
