package plugin

import "C"
import (
	"bytes"
	"fmt"
	"unsafe"

	"ShotRDP/grdp/core"
	"ShotRDP/grdp/emission"
	"ShotRDP/grdp/glog"
)

const (
	CHANNEL_RC_OK                         = 0
	CHANNEL_RC_ALREADY_INITIALIZED        = 1
	CHANNEL_RC_NOT_INITIALIZED            = 2
	CHANNEL_RC_ALREADY_CONNECTED          = 3
	CHANNEL_RC_NOT_CONNECTED              = 4
	CHANNEL_RC_TOO_MANY_CHANNELS          = 5
	CHANNEL_RC_BAD_CHANNEL                = 6
	CHANNEL_RC_BAD_CHANNEL_HANDLE         = 7
	CHANNEL_RC_NO_BUFFER                  = 8
	CHANNEL_RC_BAD_INIT_HANDLE            = 9
	CHANNEL_RC_NOT_OPEN                   = 10
	CHANNEL_RC_BAD_PROC                   = 11
	CHANNEL_RC_NO_MEMORY                  = 12
	CHANNEL_RC_UNKNOWN_CHANNEL_NAME       = 13
	CHANNEL_RC_ALREADY_OPEN               = 14
	CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY = 15
	CHANNEL_RC_NULL_DATA                  = 16
	CHANNEL_RC_ZERO_LENGTH                = 17
	CHANNEL_RC_INVALID_INSTANCE           = 18
	CHANNEL_RC_UNSUPPORTED_VERSION        = 19
	CHANNEL_RC_INITIALIZATION_ERROR       = 20
)
const (
	VIRTUAL_CHANNEL_VERSION_WIN2000 = 1
)

const (
	CHANNEL_EVENT_INITIALIZED          = 0
	CHANNEL_EVENT_CONNECTED            = 1
	CHANNEL_EVENT_V1_CONNECTED         = 2
	CHANNEL_EVENT_DISCONNECTED         = 3
	CHANNEL_EVENT_TERMINATED           = 4
	CHANNEL_EVENT_REMOTE_CONTROL_START = 5
	CHANNEL_EVENT_REMOTE_CONTROL_STOP  = 6
	CHANNEL_EVENT_ATTACHED             = 7
	CHANNEL_EVENT_DETACHED             = 8
	CHANNEL_EVENT_DATA_RECEIVED        = 10
	CHANNEL_EVENT_WRITE_COMPLETE       = 11
	CHANNEL_EVENT_WRITE_CANCELLED      = 12
)

const (
	CHANNEL_OPTION_INITIALIZED               = 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
	CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT = 0x00100000
)

type ChannelDef struct {
	Name    string
	Options uint32
}
type CHANNEL_INIT_EVENT_EX_FN func(lpUserParam interface{},
	pInitHandle interface{}, event uint, pData uintptr, dataLength uint)
type VIRTUALCHANNELINITEX func(lpUserParam interface{}, clientContext interface{},
	pInitHandle interface{}, pChannel []ChannelDef,
	channelCount int, versionRequested uint32,
	pChannelInitEventProcEx CHANNEL_INIT_EVENT_EX_FN) uint

type CHANNEL_OPEN_EVENT_EX_FN func(lpUserParam uintptr,
	openHandle uint32, event uint,
	pData uintptr, dataLength uint32, totalLength uint32, dataFlags uint32)
type VIRTUALCHANNELOPENEX func(pInitHandle interface{}, pOpenHandle *uint32,
	pChannelName string,
	pChannelOpenEventProcEx *CHANNEL_OPEN_EVENT_EX_FN) uint

type VIRTUALCHANNELCLOSEEX func(pInitHandle interface{}, openHandle uint32) uint

type VIRTUALCHANNELWRITEEX func(pInitHandle interface{}, openHandle uint32, pData interface{},
	dataLength uint32, pUserData interface{}) uint

type ChannelEntryPointsEx struct {
	CbSize                 uint32
	ProtocolVersion        uint32
	PVirtualChannelInitEx  VIRTUALCHANNELINITEX
	PVirtualChannelOpenEx  VIRTUALCHANNELOPENEX
	PVirtualChannelCloseEx VIRTUALCHANNELCLOSEEX
	PVirtualChannelWriteEx VIRTUALCHANNELWRITEEX
}

func NewChannelEntryPointsEx() *ChannelEntryPointsEx {
	e := &ChannelEntryPointsEx{}
	e.CbSize = uint32(unsafe.Sizeof(e))
	e.ProtocolVersion = VIRTUAL_CHANNEL_VERSION_WIN2000
	return e
}

type VIRTUALCHANNELENTRYEX func(pEntryPointsEx *ChannelEntryPointsEx,
	pInitHandle interface{}) error

/*
type ChannelEntryPoints struct {
	CbSize               uint32
	ProtocolVersion      uint32
	PVirtualChannelInit  PVIRTUALCHANNELINIT
	PVirtualChannelOpen  PVIRTUALCHANNELOPEN
	PVirtualChannelClose PVIRTUALCHANNELCLOSE
	PVirtualChannelWrite PVIRTUALCHANNELWRITE
}
typedef VOID VCAPITYPE CHANNEL_INIT_EVENT_FN(LPVOID pInitHandle,
        UINT event, LPVOID pData, UINT dataLength);

typedef CHANNEL_INIT_EVENT_FN* PCHANNEL_INIT_EVENT_FN;
typedef VOID VCAPITYPE CHANNEL_OPEN_EVENT_FN(DWORD openHandle, UINT event,
        LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags);

typedef CHANNEL_OPEN_EVENT_FN* PCHANNEL_OPEN_EVENT_FN;
typedef UINT VCAPITYPE VIRTUALCHANNELINIT(LPVOID* ppInitHandle, PCHANNEL_DEF pChannel,
                                          INT channelCount, ULONG versionRequested,
                                          PCHANNEL_INIT_EVENT_FN pChannelInitEventProc);
typedef VIRTUALCHANNELINIT* PVIRTUALCHANNELINIT;

typedef UINT VCAPITYPE VIRTUALCHANNELOPEN(LPVOID pInitHandle, LPDWORD pOpenHandle,
                                          PCHAR pChannelName,
                                          PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc);

typedef VIRTUALCHANNELOPEN* PVIRTUALCHANNELOPEN;

typedef UINT VCAPITYPE VIRTUALCHANNELCLOSE(DWORD openHandle);
typedef VIRTUALCHANNELCLOSE* PVIRTUALCHANNELCLOSE;

typedef UINT VCAPITYPE VIRTUALCHANNELWRITE(DWORD openHandle, LPVOID pData, ULONG dataLength,
                                           LPVOID pUserData);
typedef VIRTUALCHANNELWRITE* PVIRTUALCHANNELWRITE;

typedef UINT VCAPITYPE VIRTUALCHANNELINITEX(LPVOID lpUserParam, LPVOID clientContext,
                                            LPVOID pInitHandle, PCHANNEL_DEF pChannel,
                                            INT channelCount, ULONG versionRequested,
                                            PCHANNEL_INIT_EVENT_EX_FN pChannelInitEventProcEx);
typedef VIRTUALCHANNELINITEX* PVIRTUALCHANNELINITEX;

typedef UINT VCAPITYPE VIRTUALCHANNELOPENEX(LPVOID pInitHandle, LPDWORD pOpenHandle,
                                            PCHAR pChannelName,
                                            PCHANNEL_OPEN_EVENT_EX_FN pChannelOpenEventProcEx);
typedef VIRTUALCHANNELOPENEX* PVIRTUALCHANNELOPENEX;


typedef UINT VCAPITYPE VIRTUALCHANNELCLOSEEX(LPVOID pInitHandle, DWORD openHandle);
typedef VIRTUALCHANNELCLOSEEX* PVIRTUALCHANNELCLOSEEX;

typedef UINT VCAPITYPE VIRTUALCHANNELWRITEEX(LPVOID pInitHandle, DWORD openHandle, LPVOID pData,
                                             ULONG dataLength, LPVOID pUserData);
typedef VIRTUALCHANNELWRITEEX* PVIRTUALCHANNELWRITEEX;
*/

// static channel name
const (
	CLIPRDR_SVC_CHANNEL_NAME = "cliprdr" //剪切板
	RDPDR_SVC_CHANNEL_NAME   = "rdpdr"   //设备重定向(打印机，磁盘，端口，智能卡等)
	RDPSND_SVC_CHANNEL_NAME  = "rdpsnd"  //音频输出
	RAIL_SVC_CHANNEL_NAME    = "rail"    //远程应用
	DRDYNVC_SVC_CHANNEL_NAME = "drdynvc" //动态虚拟通道
	REMDESK_SVC_CHANNEL_NAME = "remdesk" //远程协助
)

const (
	RDPGFX_DVC_CHANNEL_NAME = "Microsoft::Windows::RDS::Graphics" //图形扩展
)

var StaticVirtualChannels = map[string]int{
	CLIPRDR_SVC_CHANNEL_NAME: CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
		CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL,
	RDPDR_SVC_CHANNEL_NAME: CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP,
	RDPSND_SVC_CHANNEL_NAME: CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
		CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL,
	RAIL_SVC_CHANNEL_NAME: CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
		CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL,
}

const (
	CHANNEL_CHUNK_LENGTH       = 1600
	CHANNEL_FLAG_FIRST         = 0x01
	CHANNEL_FLAG_LAST          = 0x02
	CHANNEL_FLAG_SHOW_PROTOCOL = 0x10
)

type ChannelTransport interface {
	GetType() (string, uint32)
	Sender(core.ChannelSender)
	Process(s []byte)
}
type ChannelClient struct {
	ChannelDef
	t ChannelTransport
}

type Channels struct {
	emission.Emitter
	channels      map[string]ChannelClient
	transport     core.Transport
	buff          *bytes.Buffer
	channelSender core.ChannelSender
}

func NewChannels(t core.Transport) *Channels {
	c := &Channels{
		Emitter:   *emission.NewEmitter(),
		channels:  make(map[string]ChannelClient, 20),
		transport: t,
		buff:      &bytes.Buffer{},
	}
	t.On("channel", c.process)
	return c
}

func (c *Channels) SetChannelSender(f core.ChannelSender) {
	c.channelSender = f
}
func (c *Channels) Register(t ChannelTransport) {
	name, option := t.GetType()
	_, ok := c.channels[name]
	if ok {
		glog.Warn("Already register channel:", name)
		return
	}
	t.Sender(c)
	c.channels[name] = ChannelClient{ChannelDef{name, option}, t}
}

func (c *Channels) SendToChannel(channel string, s []byte) (int, error) {
	cli, ok := c.channels[channel]
	if !ok {
		glog.Warn("No register channel:", channel)
		return 0, fmt.Errorf("No register channel: %s", channel)
	}
	idx := 0
	ln := len(s)
	b := &bytes.Buffer{}
	for ln > 0 {
		var flag uint32 = 0
		if cli.Options&CHANNEL_OPTION_SHOW_PROTOCOL != 0 {
			flag |= CHANNEL_FLAG_SHOW_PROTOCOL
		}
		if idx == 0 {
			flag |= CHANNEL_FLAG_FIRST
		}

		var ss []byte
		if ln > CHANNEL_CHUNK_LENGTH {
			ss = s[idx : idx+CHANNEL_CHUNK_LENGTH]
			idx += CHANNEL_CHUNK_LENGTH
		} else {
			flag |= CHANNEL_FLAG_LAST
			ss = s[idx : idx+ln]
		}
		glog.Debug("len:", len(ss), "flag:", flag)
		ln -= len(ss)
		b.Reset()
		core.WriteUInt32LE(uint32(len(s)), b)
		core.WriteUInt32LE(flag, b)
		b.Write(ss)
		c.channelSender.SendToChannel(channel, b.Bytes())
	}
	return ln, nil
}

func (c *Channels) process(channel string, s []byte) {
	cli, ok := c.channels[channel]
	if !ok {
		glog.Warn("No found channel:", channel)
		return
	}
	r := bytes.NewReader(s)
	ln, _ := core.ReadUInt32LE(r)
	flags, _ := core.ReadUInt32LE(r)
	glog.Debugf("channel:%s length: %d, flags: %d", channel, ln, flags)
	if flags&CHANNEL_FLAG_FIRST == 0 || flags&CHANNEL_FLAG_LAST == 0 {
		if flags&CHANNEL_FLAG_FIRST != 0 {
			c.buff.Reset()
		}
		b, _ := core.ReadBytes(r.Len(), r)
		c.buff.Write(b)
		if flags&CHANNEL_FLAG_LAST == 0 {
			return
		}
		s = c.buff.Bytes()
	} else {
		s, _ = core.ReadBytes(r.Len(), r)
	}

	cli.t.Process(s)
}
