package msrpc

import (
	
	

	
)

var le = binary.LittleEndian

func roundup(,  int) int {
	return ( + ( - 1)) &^ ( - 1)
}

const (
	RPC_VERSION       = 5
	RPC_VERSION_MINOR = 0

	RPC_TYPE_REQUEST  = 0
	RPC_TYPE_RESPONSE = 2
	RPC_TYPE_BIND     = 11
	RPC_TYPE_BIND_ACK = 12

	RPC_PACKET_FLAG_FIRST = 0x01
	RPC_PACKET_FLAG_LAST  = 0x02

	SRVSVC_VERSION       = 3
	SRVSVC_VERSION_MINOR = 0

	NDR_VERSION = 2

	OP_NET_SHARE_ENUM = 15
)

var (
	SRVSVC_UUID = []byte("c84f324b7016d30112785a47bf6ee188")
	NDR_UUID    = []byte("045d888aeb1cc9119fe808002b104860")
)

type Bind struct {
	CallId uint32
}

func ( *Bind) () int {
	return 72
}

func ( *Bind) ( []byte) {
	[0] = RPC_VERSION
	[1] = RPC_VERSION_MINOR
	[2] = RPC_TYPE_BIND
	[3] = RPC_PACKET_FLAG_FIRST | RPC_PACKET_FLAG_LAST

	// order = Little-Endian, float = IEEE, char = ASCII
	[4] = 0x10
	[5] = 0
	[6] = 0
	[7] = 0

	le.PutUint16([8:10], 72)        // frag length
	le.PutUint16([10:12], 0)        // auth length
	le.PutUint32([12:16], .CallId) // call id
	le.PutUint16([16:18], 4280)     // max xmit frag
	le.PutUint16([18:20], 4280)     // max recv frag
	le.PutUint32([20:24], 0)        // assoc group
	le.PutUint32([24:28], 1)        // num ctx items
	le.PutUint16([28:30], 0)        // ctx item[1] .context id
	le.PutUint16([30:32], 1)        // ctx item[1] .num trans items

	hex.Decode([32:48], SRVSVC_UUID)
	le.PutUint16([48:50], SRVSVC_VERSION)
	le.PutUint16([50:52], SRVSVC_VERSION_MINOR)

	hex.Decode([52:68], NDR_UUID)
	le.PutUint32([68:72], NDR_VERSION)
}

type BindAckDecoder []byte

func ( BindAckDecoder) () bool {
	if len() < 24 {
		return true
	}
	if .Version() != RPC_VERSION {
		return true
	}
	if .VersionMinor() != RPC_VERSION_MINOR {
		return true
	}
	if .PacketType() != RPC_TYPE_BIND_ACK {
		return true
	}
	return false
}

func ( BindAckDecoder) () uint8 {
	return [0]
}

func ( BindAckDecoder) () uint8 {
	return [1]
}

func ( BindAckDecoder) () uint8 {
	return [2]
}

func ( BindAckDecoder) () uint8 {
	return [3]
}

func ( BindAckDecoder) () []byte {
	return [4:8]
}

func ( BindAckDecoder) () uint16 {
	return le.Uint16([8:10])
}

func ( BindAckDecoder) () uint16 {
	return le.Uint16([10:12])
}

func ( BindAckDecoder) () uint32 {
	return le.Uint32([12:16])
}

func ( BindAckDecoder) () uint16 {
	return le.Uint16([16:18])
}

func ( BindAckDecoder) () uint16 {
	return le.Uint16([18:20])
}

func ( BindAckDecoder) () uint32 {
	return le.Uint32([20:24])
}

type NetShareEnumAllRequest struct {
	CallId     uint32
	ServerName string
	Level      uint32
}

func ( *NetShareEnumAllRequest) () int {
	 := 40 + utf16le.EncodedStringLen(.ServerName) + 2
	 = roundup(, 4)
	 += 24
	 += 4
	return 
}

func ( *NetShareEnumAllRequest) ( []byte) {
	[0] = RPC_VERSION
	[1] = RPC_VERSION_MINOR
	[2] = RPC_TYPE_REQUEST
	[3] = RPC_PACKET_FLAG_FIRST | RPC_PACKET_FLAG_LAST

	// order = Little-Endian, float = IEEE, char = ASCII
	[4] = 0x10
	[5] = 0
	[6] = 0
	[7] = 0

	le.PutUint16([10:12], 0)                 // auth length
	le.PutUint32([12:16], .CallId)          // call id
	le.PutUint16([20:22], 0)                 // context id
	le.PutUint16([22:24], OP_NET_SHARE_ENUM) // opnum

	// follwing parts will change if we use NDR64 instead of NDR

	// pointer to server unc

	le.PutUint32([24:28], 0x20000) // referent ID

	 := utf16le.EncodedStringLen(.ServerName)/2 + 1

	le.PutUint32([28:32], uint32()) // max count
	le.PutUint32([32:36], 0)             // offset
	le.PutUint32([36:40], uint32()) // actual count

	utf16le.EncodeString([40:], .ServerName) // server unc

	 := 40 + *2
	 = roundup(, 4)

	// pointer level

	le.PutUint32([:+4], .Level)

	// pointer to ctr (srvsvc_NetShareCtr)

	le.PutUint32([+4:+8], 1)            // ctr
	le.PutUint32([+8:+12], 0x20004)     // referent ID
	le.PutUint32([+12:+16], 0)          // ctr1.count
	le.PutUint32([+16:+20], 0)          // ctr1.pointer
	le.PutUint32([+20:+24], 0xffffffff) // max buffer

	 += 24

	// pointer to resume handle

	le.PutUint32([:+4], 0) // null pointer
	// le.PutUint32(b[off:off+4], 0x20008) // referent ID
	// le.PutUint32(b[off+4:off+8], 0)     // resume handle

	 += 4

	le.PutUint16([8:10], uint16())     // frag length
	le.PutUint32([16:20], uint32(-24)) // alloc hint
}

type NetShareEnumAllResponseDecoder []byte

func ( NetShareEnumAllResponseDecoder) () bool {
	if len() < 24 {
		return true
	}
	if .Version() != RPC_VERSION {
		return true
	}
	if .VersionMinor() != RPC_VERSION_MINOR {
		return true
	}
	if .PacketType() != RPC_TYPE_RESPONSE {
		return true
	}

	return false
}

func ( NetShareEnumAllResponseDecoder) () uint8 {
	return [0]
}

func ( NetShareEnumAllResponseDecoder) () uint8 {
	return [1]
}

func ( NetShareEnumAllResponseDecoder) () uint8 {
	return [2]
}

func ( NetShareEnumAllResponseDecoder) () uint8 {
	return [3]
}

func ( NetShareEnumAllResponseDecoder) () []byte {
	return [4:8]
}

func ( NetShareEnumAllResponseDecoder) () uint16 {
	return le.Uint16([8:10])
}

func ( NetShareEnumAllResponseDecoder) () uint16 {
	return le.Uint16([10:12])
}

func ( NetShareEnumAllResponseDecoder) () uint32 {
	return le.Uint32([12:16])
}

func ( NetShareEnumAllResponseDecoder) () uint32 {
	return le.Uint32([16:20])
}

func ( NetShareEnumAllResponseDecoder) () uint16 {
	return le.Uint16([20:22])
}

func ( NetShareEnumAllResponseDecoder) () uint8 {
	return [22]
}

func ( NetShareEnumAllResponseDecoder) () bool {
	if len() < 48 {
		return true
	}

	 := le.Uint32([24:28])

	 := int(le.Uint32([36:40]))

	switch  {
	case 0:
		 := 48 + *4 // name pointer
		if len() <  {
			return true
		}

		for  := 0;  < ; ++ {
			if len() < +12 {
				return true
			}

			 := int(le.Uint32([+4 : +8]))    // offset
			 := int(le.Uint32([+8:+12])) * 2 // actual count
			 = roundup(+12++, 4)

			if len() <  {
				return true
			}
		}
	case 1:
		 := 48 + *12
		if len() <  {
			return true
		}

		for  := 0;  < ; ++ {
			{ // name
				if len() < +12 {
					return true
				}

				 := int(le.Uint32([+4 : +8]))    // offset
				 := int(le.Uint32([+8:+12])) * 2 // actual count
				 = roundup(+12++, 4)

				if len() <  {
					return true
				}
			}

			{ // comment
				if len() < +12 {
					return true
				}

				 := int(le.Uint32([+4 : +8]))    // offset
				 := int(le.Uint32([+8:+12])) * 2 // actual count
				 = roundup(+12++, 4)

				if len() <  {
					return true
				}
			}
		}
	default:
		// TODO not supported yet
		return true
	}

	return false
}

func ( NetShareEnumAllResponseDecoder) () []byte {
	return [24:]
}

func ( NetShareEnumAllResponseDecoder) () []string {
	 := le.Uint32([24:28])

	 := int(le.Uint32([36:40]))

	 := make([]string, )

	switch  {
	case 0:
		 := 48 + *4 // name pointer
		for  := 0;  < ; ++ {
			 := int(le.Uint32([+4 : +8]))    // offset
			 := int(le.Uint32([+8:+12])) * 2 // actual count

			[] = utf16le.DecodeToString([+12+ : +12++])

			 = roundup(+12++, 4)
		}
	case 1:
		 := 48 + *12
		for  := 0;  < ; ++ {
			{ // name
				 := int(le.Uint32([+4 : +8]))    // offset
				 := int(le.Uint32([+8:+12])) * 2 // actual count

				[] = utf16le.DecodeToString([+12+ : +12++])

				 = roundup(+12++, 4)
			}

			{ // comment
				 := int(le.Uint32([+4 : +8]))    // offset
				 := int(le.Uint32([+8:+12])) * 2 // actual count
				 = roundup(+12++, 4)
			}
		}
	default:
		// TODO not supported yet
		return nil
	}

	return 
}