package gcc

import (
	"bytes"
	"errors"
	"io"
	"os"

	"github.com/lcvvvv/kscan/grdp/glog"

	"github.com/lcvvvv/kscan/grdp/core"
	"github.com/lcvvvv/kscan/grdp/protocol/t125/per"
	"github.com/lunixbochs/struc"
)

var t124_02_98_oid = []byte{0, 0, 20, 124, 0, 1}
var h221_cs_key = "Duca"
var h221_sc_key = "McDn"

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240509.aspx
 */
type Message uint16

const (
	//server -> client
	SC_CORE     Message = 0x0C01
	SC_SECURITY         = 0x0C02
	SC_NET              = 0x0C03
	//client -> server
	CS_CORE     = 0xC001
	CS_SECURITY = 0xC002
	CS_NET      = 0xC003
	CS_CLUSTER  = 0xC004
	CS_MONITOR  = 0xC005
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
 */
type ColorDepth uint16

const (
	RNS_UD_COLOR_8BPP      ColorDepth = 0xCA01
	RNS_UD_COLOR_16BPP_555            = 0xCA02
	RNS_UD_COLOR_16BPP_565            = 0xCA03
	RNS_UD_COLOR_24BPP                = 0xCA04
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
 */
type HighColor uint16

const (
	HIGH_COLOR_4BPP  HighColor = 0x0004
	HIGH_COLOR_8BPP            = 0x0008
	HIGH_COLOR_15BPP           = 0x000f
	HIGH_COLOR_16BPP           = 0x0010
	HIGH_COLOR_24BPP           = 0x0018
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
 */
type Support uint16

const (
	RNS_UD_24BPP_SUPPORT uint16 = 0x0001
	RNS_UD_16BPP_SUPPORT        = 0x0002
	RNS_UD_15BPP_SUPPORT        = 0x0004
	RNS_UD_32BPP_SUPPORT        = 0x0008
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
 */
type CapabilityFlag uint16

const (
	RNS_UD_CS_SUPPORT_ERRINFO_PDU        uint16 = 0x0001
	RNS_UD_CS_WANT_32BPP_SESSION                = 0x0002
	RNS_UD_CS_SUPPORT_STATUSINFO_PDU            = 0x0004
	RNS_UD_CS_STRONG_ASYMMETRIC_KEYS            = 0x0008
	RNS_UD_CS_UNUSED                            = 0x0010
	RNS_UD_CS_VALID_CONNECTION_TYPE             = 0x0020
	RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU        = 0x0040
	RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT        = 0x0080
	RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL        = 0x0100
	RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE         = 0x0200
	RNS_UD_CS_SUPPORT_HEARTBEAT_PDU             = 0x0400
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
 */
type ConnectionType uint8

const (
	CONNECTION_TYPE_MODEM          ConnectionType = 0x01
	CONNECTION_TYPE_BROADBAND_LOW                 = 0x02
	CONNECTION_TYPE_SATELLITEV                    = 0x03
	CONNECTION_TYPE_BROADBAND_HIGH                = 0x04
	CONNECTION_TYPE_WAN                           = 0x05
	CONNECTION_TYPE_LAN                           = 0x06
	CONNECTION_TYPE_AUTODETECT                    = 0x07
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240510.aspx
 */
type VERSION uint32

const (
	RDP_VERSION_4      VERSION = 0x00080001
	RDP_VERSION_5_PLUS         = 0x00080004
)

type Sequence uint16

const (
	RNS_UD_SAS_DEL Sequence = 0xAA03
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240511.aspx
 */
type EncryptionMethod uint32

const (
	ENCRYPTION_FLAG_40BIT  uint32 = 0x00000001
	ENCRYPTION_FLAG_128BIT        = 0x00000002
	ENCRYPTION_FLAG_56BIT         = 0x00000008
	FIPS_ENCRYPTION_FLAG          = 0x00000010
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240518.aspx
 */
type EncryptionLevel uint32

const (
	ENCRYPTION_LEVEL_NONE              EncryptionLevel = 0x00000000
	ENCRYPTION_LEVEL_LOW                               = 0x00000001
	ENCRYPTION_LEVEL_CLIENT_COMPATIBLE                 = 0x00000002
	ENCRYPTION_LEVEL_HIGH                              = 0x00000003
	ENCRYPTION_LEVEL_FIPS                              = 0x00000004
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240513.aspx
 */
type ChannelOptions uint32

const (
	CHANNEL_OPTION_INITIALIZED   ChannelOptions = 0x80000000
	CHANNEL_OPTION_ENCRYPT_RDP                  = 0x40000000
	CHANNEL_OPTION_ENCRYPT_SC                   = 0x20000000
	CHANNEL_OPTION_ENCRYPT_CS                   = 0x10000000
	CHANNEL_OPTION_PRI_HIGH                     = 0x08000000
	CHANNEL_OPTION_PRI_MED                      = 0x04000000
	CHANNEL_OPTION_PRI_LOW                      = 0x02000000
	CHANNEL_OPTION_COMPRESS_RDP                 = 0x00800000
	CHANNEL_OPTION_COMPRESS                     = 0x00400000
	CHANNEL_OPTION_SHOW_PROTOCOL                = 0x00200000
	REMOTE_CONTROL_PERSISTENT                   = 0x00100000
)

/**
 * IBM_101_102_KEYS is the most common keyboard type
 */
type KeyboardType uint32

const (
	KT_IBM_PC_XT_83_KEY KeyboardType = 0x00000001
	KT_OLIVETTI                      = 0x00000002
	KT_IBM_PC_AT_84_KEY              = 0x00000003
	KT_IBM_101_102_KEYS              = 0x00000004
	KT_NOKIA_1050                    = 0x00000005
	KT_NOKIA_9140                    = 0x00000006
	KT_JAPANESE                      = 0x00000007
)

/**
 * @see http://technet.microsoft.com/en-us/library/cc766503%28WS.10%29.aspx
 */
type KeyboardLayout uint32

const (
	ARABIC              KeyboardLayout = 0x00000401
	BULGARIAN                          = 0x00000402
	CHINESE_US_KEYBOARD                = 0x00000404
	CZECH                              = 0x00000405
	DANISH                             = 0x00000406
	GERMAN                             = 0x00000407
	GREEK                              = 0x00000408
	US                                 = 0x00000409
	SPANISH                            = 0x0000040a
	FINNISH                            = 0x0000040b
	FRENCH                             = 0x0000040c
	HEBREW                             = 0x0000040d
	HUNGARIAN                          = 0x0000040e
	ICELANDIC                          = 0x0000040f
	ITALIAN                            = 0x00000410
	JAPANESE                           = 0x00000411
	KOREAN                             = 0x00000412
	DUTCH                              = 0x00000413
	NORWEGIAN                          = 0x00000414
)

/**
 * @see http://msdn.microsoft.com/en-us/library/cc240521.aspx
 */
type CertificateType uint32

const (
	CERT_CHAIN_VERSION_1 CertificateType = 0x00000001
	CERT_CHAIN_VERSION_2                 = 0x00000002
)

type ChannelDef struct {
	Name    [8]byte
	Options uint32
}

type ClientCoreData struct {
	RdpVersion             VERSION        `struc:"uint32,little"`
	DesktopWidth           uint16         `struc:"little"`
	DesktopHeight          uint16         `struc:"little"`
	ColorDepth             ColorDepth     `struc:"little"`
	SasSequence            Sequence       `struc:"little"`
	KbdLayout              KeyboardLayout `struc:"little"`
	ClientBuild            uint32         `struc:"little"`
	ClientName             [32]byte       `struc:"[32]byte"`
	KeyboardType           uint32         `struc:"little"`
	KeyboardSubType        uint32         `struc:"little"`
	KeyboardFnKeys         uint32         `struc:"little"`
	ImeFileName            [64]byte       `struc:"[64]byte"`
	PostBeta2ColorDepth    ColorDepth     `struc:"little"`
	ClientProductId        uint16         `struc:"little"`
	SerialNumber           uint32         `struc:"little"`
	HighColorDepth         HighColor      `struc:"little"`
	SupportedColorDepths   uint16         `struc:"little"`
	EarlyCapabilityFlags   uint16         `struc:"little"`
	ClientDigProductId     [64]byte       `struc:"[64]byte"`
	ConnectionType         uint8          `struc:"uint8"`
	Pad1octet              uint8          `struc:"uint8"`
	ServerSelectedProtocol uint32         `struc:"little"`
}

func NewClientCoreData() *ClientCoreData {
	name, _ := os.Hostname()
	var ClientName [32]byte
	copy(ClientName[:], core.UnicodeEncode(name)[:])
	return &ClientCoreData{
		RDP_VERSION_5_PLUS, 1280, 800, RNS_UD_COLOR_8BPP,
		RNS_UD_SAS_DEL, US, 3790, ClientName, KT_IBM_101_102_KEYS,
		0, 12, [64]byte{}, RNS_UD_COLOR_8BPP, 1, 0, HIGH_COLOR_24BPP,
		RNS_UD_15BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_24BPP_SUPPORT | RNS_UD_32BPP_SUPPORT,
		RNS_UD_CS_SUPPORT_ERRINFO_PDU, [64]byte{}, 0, 0, 0}
}

func (data *ClientCoreData) Block() []byte {
	buff := &bytes.Buffer{}
	core.WriteUInt16LE(CS_CORE, buff) // 01C0
	core.WriteUInt16LE(0xd8, buff)    // d800
	struc.Pack(buff, data)
	return buff.Bytes()
}

type ClientNetworkData struct {
	ChannelCount    uint32
	ChannelDefArray []ChannelDef
}

func NewClientNetworkData() *ClientNetworkData {
	return &ClientNetworkData{}
}

func (d *ClientNetworkData) Block() []byte {
	buff := &bytes.Buffer{}
	core.WriteUInt16LE(CS_NET, buff) // type
	core.WriteUInt16LE(0x08, buff)   // len 8
	buff.Write([]byte{0, 0, 0, 0})   // data
	return buff.Bytes()
}

type ClientSecurityData struct {
	EncryptionMethods    uint32
	ExtEncryptionMethods uint32
}

func NewClientSecurityData() *ClientSecurityData {
	return &ClientSecurityData{
		ENCRYPTION_FLAG_40BIT | ENCRYPTION_FLAG_56BIT | ENCRYPTION_FLAG_128BIT,
		00}
}

func (d *ClientSecurityData) Block() []byte {
	buff := &bytes.Buffer{}
	core.WriteUInt16LE(CS_SECURITY, buff) // type
	core.WriteUInt16LE(0x0c, buff)        // len 12
	core.WriteUInt32LE(d.EncryptionMethods, buff)
	core.WriteUInt32LE(d.ExtEncryptionMethods, buff)
	return buff.Bytes()
}

type RSAPublicKey struct {
	Magic   uint32 `struc:"little"` //0x31415352
	Keylen  uint32 `struc:"little,sizeof=Modulus"`
	Bitlen  uint32 `struc:"little"`
	Datalen uint32 `struc:"little"`
	PubExp  uint32 `struc:"little"`
	Modulus []byte `struc:"little"`
	Padding []byte `struc:"[8]byte"`
}

type ProprietaryServerCertificate struct {
	DwSigAlgId        uint32       `struc:"little"` //0x00000001
	DwKeyAlgId        uint32       `struc:"little"` //0x00000001
	PublicKeyBlobType uint16       `struc:"little"` //0x0006
	PublicKeyBlobLen  uint16       `struc:"little,sizeof=PublicKeyBlob"`
	PublicKeyBlob     RSAPublicKey `struc:"little"`
	SignatureBlobType uint16       `struc:"little"` //0x0008
	SignatureBlobLen  uint16       `struc:"little,sizeof=SignatureBlob"`
	SignatureBlob     []byte       `struc:"little"`
	//PaddingLen        uint16       `struc:"little,sizeof=Padding,skip"`
	Padding []byte `struc:"[8]byte"`
}

func (p *ProprietaryServerCertificate) GetPublicKey() (uint32, []byte) {
	return p.PublicKeyBlob.PubExp, p.PublicKeyBlob.Modulus
}
func (p *ProprietaryServerCertificate) Verify() bool {
	//todo
	return true
}
func (p *ProprietaryServerCertificate) Encrypt() []byte {
	//todo
	return nil
}
func (p *ProprietaryServerCertificate) Unpack(r io.Reader) error {
	p.DwSigAlgId, _ = core.ReadUInt32LE(r)
	p.DwKeyAlgId, _ = core.ReadUInt32LE(r)
	p.PublicKeyBlobType, _ = core.ReadUint16LE(r)
	p.PublicKeyBlobLen, _ = core.ReadUint16LE(r)
	var b RSAPublicKey
	b.Magic, _ = core.ReadUInt32LE(r)
	b.Keylen, _ = core.ReadUInt32LE(r)
	b.Bitlen, _ = core.ReadUInt32LE(r)
	b.Datalen, _ = core.ReadUInt32LE(r)
	b.PubExp, _ = core.ReadUInt32LE(r)
	b.Modulus, _ = core.ReadBytes(int(b.Keylen)-8, r)
	b.Padding, _ = core.ReadBytes(8, r)
	p.PublicKeyBlob = b
	p.SignatureBlobType, _ = core.ReadUint16LE(r)
	p.SignatureBlobLen, _ = core.ReadUint16LE(r)
	p.SignatureBlob, _ = core.ReadBytes(int(p.SignatureBlobLen)-8, r)
	p.Padding, _ = core.ReadBytes(8, r)

	return nil
}

type CertBlob struct {
	CbCert uint32 `struc:"little,sizeof=AbCert"`
	AbCert []byte `struc:"little"`
}
type X509CertificateChain struct {
	NumCertBlobs  uint32     `struc:"little,sizeof=CertBlobArray"`
	CertBlobArray []CertBlob `struc:"little"`
	Padding       []byte     `struc:"little"`
}

func (p *X509CertificateChain) GetPublicKey() (uint32, []byte) {
	//todo
	return 0, nil
}
func (p *X509CertificateChain) Verify() bool {
	return true
}
func (p *X509CertificateChain) Encrypt() []byte {

	//todo
	return nil
}
func (p *X509CertificateChain) Unpack(r io.Reader) error {
	return struc.Unpack(r, p)
}

type ServerCoreData struct {
	RdpVersion              VERSION `struc:"uint32,little"`
	ClientRequestedProtocol uint32  `struc:"little"`
	EarlyCapabilityFlags    uint32  `struc:"little"`
}

func NewServerCoreData() *ServerCoreData {
	return &ServerCoreData{
		RDP_VERSION_5_PLUS, 0, 0}
}

func (d *ServerCoreData) Serialize() []byte {
	return []byte{}
}

func (d *ServerCoreData) ScType() Message {
	return SC_CORE
}
func (d *ServerCoreData) Unpack(r io.Reader) error {
	return struc.Unpack(r, d)
}

type ServerNetworkData struct {
	MCSChannelId   uint16   `struc:"little"`
	ChannelCount   uint16   `struc:"little,sizeof=ChannelIdArray"`
	ChannelIdArray []uint16 `struc:"little"`
}

func NewServerNetworkData() *ServerNetworkData {
	return &ServerNetworkData{}
}
func (d *ServerNetworkData) ScType() Message {
	return SC_NET
}
func (d *ServerNetworkData) Unpack(r io.Reader) error {
	return struc.Unpack(r, d)
}

type CertData interface {
	GetPublicKey() (uint32, []byte)
	Verify() bool
	Unpack(io.Reader) error
}
type ServerCertificate struct {
	DwVersion uint32
	CertData  CertData
}
type ServerSecurityData struct {
	EncryptionMethod  uint32 `struc:"little"`
	EncryptionLevel   uint32 `struc:"little"`
	ServerRandomLen   uint32 //0x00000020
	ServerCertLen     uint32
	ServerRandom      []byte
	ServerCertificate ServerCertificate
}

func NewServerSecurityData() *ServerSecurityData {
	return &ServerSecurityData{
		0, 0, 0x00000020, 0, []byte{}, ServerCertificate{}}
}
func (d *ServerSecurityData) ScType() Message {
	return SC_SECURITY
}
func (s *ServerSecurityData) Unpack(r io.Reader) error {
	s.EncryptionMethod, _ = core.ReadUInt32LE(r)
	s.EncryptionLevel, _ = core.ReadUInt32LE(r)
	if !(s.EncryptionMethod == 0 && s.EncryptionLevel == 0) {
		s.ServerRandomLen, _ = core.ReadUInt32LE(r)
		s.ServerCertLen, _ = core.ReadUInt32LE(r)
		s.ServerRandom, _ = core.ReadBytes(int(s.ServerRandomLen), r)
		var sc ServerCertificate
		data, _ := core.ReadBytes(int(s.ServerCertLen), r)
		rd := bytes.NewReader(data)
		sc.DwVersion, _ = core.ReadUInt32LE(rd)
		var cd CertData
		switch CertificateType(sc.DwVersion & 0x7fffffff) {
		case CERT_CHAIN_VERSION_1:
			glog.Debug("ProprietaryServerCertificate")
			cd = &ProprietaryServerCertificate{}
		case CERT_CHAIN_VERSION_2:
			glog.Debug("X509CertificateChain")
			cd = &X509CertificateChain{}
		default:
			glog.Error("Unsupported version:", sc.DwVersion&0x7fffffff)
			return errors.New("Unsupported version")
		}
		if cd != nil {
			//err := struc.Unpack(rd, cd)
			err := cd.Unpack(rd)
			if err != nil {
				glog.Error("Unpack:", err)
				return err
			}
			glog.Infof("d:%+v", cd)
		}
		sc.CertData = cd
		s.ServerCertificate = sc
	}

	return nil
}

func MakeConferenceCreateRequest(userData []byte) []byte {
	buff := &bytes.Buffer{}
	per.WriteChoice(0, buff)                        // 00
	per.WriteObjectIdentifier(t124_02_98_oid, buff) // 05:00:14:7c:00:01
	per.WriteLength(len(userData)+14, buff)
	per.WriteChoice(0, buff)                   // 00
	per.WriteSelection(0x08, buff)             // 08
	per.WriteNumericString("1", 1, buff)       // 00 10
	per.WritePadding(1, buff)                  // 00
	per.WriteNumberOfSet(1, buff)              // 01
	per.WriteChoice(0xc0, buff)                // c0
	per.WriteOctetStream(h221_cs_key, 4, buff) // 00 44:75:63:61
	per.WriteOctetStream(string(userData), 0, buff)
	return buff.Bytes()
}

type ScData interface {
	ScType() Message
	Unpack(io.Reader) error
}

func ReadConferenceCreateResponse(data []byte) []interface{} {
	ret := make([]interface{}, 0, 3)
	r := bytes.NewReader(data)
	per.ReadChoice(r)
	if !per.ReadObjectIdentifier(r, t124_02_98_oid) {
		glog.Error("NODE_RDP_PROTOCOL_T125_GCC_BAD_OBJECT_IDENTIFIER_T124")
		return ret
	}
	per.ReadLength(r)
	per.ReadChoice(r)
	per.ReadInteger16(r)
	per.ReadInteger(r)
	per.ReadEnumerates(r)
	per.ReadNumberOfSet(r)
	per.ReadChoice(r)

	if !per.ReadOctetStream(r, h221_sc_key, 4) {
		glog.Error("NODE_RDP_PROTOCOL_T125_GCC_BAD_H221_SC_KEY")
		return ret
	}

	glog.Debug("all:", SC_CORE, SC_SECURITY, SC_NET,
		CS_CORE, CS_SECURITY, CS_NET, CS_CLUSTER, CS_MONITOR)

	ln, _ := per.ReadLength(r)
	for ln > 0 {
		t, _ := core.ReadUint16LE(r)
		l, _ := core.ReadUint16LE(r)
		dataBytes, _ := core.ReadBytes(int(l)-4, r)
		ln = ln - l
		var d ScData
		switch Message(t) {
		case SC_CORE:
			d = &ServerCoreData{}
		case SC_SECURITY:
			d = &ServerSecurityData{}
		case SC_NET:
			d = &ServerNetworkData{}
		default:
			glog.Error("Unknown type", t)
			continue
		}
		r := bytes.NewReader(dataBytes)
		err := d.Unpack(r)
		if err != nil {
			glog.Error("Unpack:", err)
			if err.Error() != "EOF" {
				return ret
			}
		}
		ret = append(ret, d)
		glog.Debugf("d:%+v", d)
	}

	return ret
}
