package client

import (
	"ShotRDP/common"
	"ShotRDP/grdp/core"
	"ShotRDP/grdp/glog"
	"ShotRDP/grdp/plugin"
	"ShotRDP/grdp/protocol/nla"
	"ShotRDP/grdp/protocol/pdu"
	"ShotRDP/grdp/protocol/sec"
	"ShotRDP/grdp/protocol/t125"
	"ShotRDP/grdp/protocol/tpkt"
	"ShotRDP/grdp/protocol/x224"
	"context"
	"log"
	"net"
	"os"
	"strings"
	"sync"
	"time"
)

type RdpClient struct {
	Host string          // 服务地址(ip:port)
	tpkt *tpkt.TPKT      // TPKT协议层
	x224 *x224.X224      // X224协议层
	mcs  *t125.MCSClient // MCS协议层
	sec  *sec.Client     // 安全层
	pdu  *pdu.Client     // PDU协议层

	channels     *plugin.Channels
	screenWidth  int
	screenHeight int
}

type Position struct {
	Top    int
	Left   int
	Right  int
	Bottom int
}

var (
	timerCycle     = 500 * time.Millisecond
	timer          = time.NewTimer(timerCycle)
	bitmapRevOne   = sync.Once{}
	bitmapRevList  = make([]pdu.BitmapData, 0)
	bitmapRevCount = 0
	bitmapRevSig   = make(chan struct{})
	modifyMux      = sync.Mutex{}
	positionMap    = make(map[Position]*common.Bitmap)
)

func Newclient(host string, logLevel glog.LEVEL, screenWidth, screenHeight int) *RdpClient {
	_logger := log.New(os.Stdout, "", 0)

	glog.SetLogger(_logger)
	glog.SetLevel(logLevel)

	return &RdpClient{
		Host:         host,
		screenWidth:  screenWidth,
		screenHeight: screenHeight,
	}
}

func (client *RdpClient) Login(domain, username, password string) ([]byte, error) {
	conn, err := net.DialTimeout("tcp", client.Host, 3*time.Second)
	if err != nil {
		return nil, err
	}

	// 初始化协议栈
	client.initProtocolStack(conn, domain, username, password)

	// 建立X224连接
	err = client.x224.Connect()
	if err != nil {
		return nil, err
	}

	// 设置事件处理
	client.setEventHandler()
	go func() {
		for {
			select {
			case <-timer.C:
				modifyMux.Lock()
				// 检查是否有新元素添加
				if len(bitmapRevList) == bitmapRevCount {
					bitmapRevOne.Do(func() {
						close(bitmapRevSig)
					})
				}
				modifyMux.Unlock()
			}
		}
	}()

	// 合并登录界面位图
	wg := &sync.WaitGroup{}
	wg.Add(1)

	var buff []byte
	surfaceflingerCtx, _ := context.WithCancel(context.Background())
	go func(ctx context.Context, c *RdpClient) {
		outputName := strings.ReplaceAll(client.Host, ":", "_")
		var bitmapList = make([]*common.Bitmap, 0)

		// TODO: 寻找合适的截图时机
		<-bitmapRevSig

		modifyMux.Lock()
		for _, bitmap := range positionMap {
			//common.DrawOneBitmap(bitmap, fmt.Sprintf("%d-%d-%d-%d.png", pos.Left, pos.Top, pos.Right, pos.Bottom))
			bitmapList = append(bitmapList, bitmap)
		}
		modifyMux.Unlock()

		buff = common.DrawFullImage(outputName, bitmapList)
		wg.Done()
	}(surfaceflingerCtx, client)

	wg.Wait()
	return buff, nil
}

// initProtocolStack 初始化RDP协议栈
func (client *RdpClient) initProtocolStack(conn net.Conn, domain, username, password string) {
	// 创建协议层实例
	client.tpkt = tpkt.New(core.NewSocketLayer(conn), nla.NewNTLMv2(domain, username, password))
	client.x224 = x224.New(client.tpkt)
	client.mcs = t125.NewMCSClient(client.x224)
	client.sec = sec.NewClient(client.mcs)
	client.pdu = pdu.NewClient(client.sec)

	// 配置桌面信息
	client.channels = plugin.NewChannels(client.sec)
	client.mcs.SetClientDesktop(uint16(client.screenWidth), uint16(client.screenHeight))

	// 设置认证信息
	client.sec.SetDomain(domain)
	client.sec.SetUsername(username)
	client.sec.SetPassword(password)

	// 配置协议层关联
	client.tpkt.SetFastPathListener(client.sec)
	client.sec.SetFastPathListener(client.pdu)

	client.sec.SetChannelSender(client.mcs)
}

// setEventHandler 设置PDU事件处理器
func (client *RdpClient) setEventHandler() {
	client.pdu.On("ready", func() {

	})

	client.pdu.On("success", func() {

	})

	client.pdu.On("close", func() {

	})

	client.pdu.On("done", func() {

	})

	client.pdu.On("error", func(err error) {
		client.pdu.Emit("done")
	})

	client.pdu.On("bitmap", func(rectangles []pdu.BitmapData) {
		modifyMux.Lock()
		defer modifyMux.Unlock()

		// 重置定时器
		if !timer.Stop() {
			<-timer.C
		}
		timer.Reset(timerCycle)

		oldCount := len(bitmapRevList)
		bitmapRevList = append(bitmapRevList, rectangles...)
		newCount := len(bitmapRevList)

		if newCount > oldCount {
			bitmapRevCount = newCount
		}

		for _, rectangle := range rectangles {
			pos := Position{Left: int(rectangle.DestLeft / rectangle.Width), Top: int(rectangle.DestTop / rectangle.Height), Right: int(rectangle.DestRight / rectangle.Width), Bottom: int(rectangle.DestBottom / rectangle.Height)}
			isCompress := rectangle.IsCompress()
			data := rectangle.BitmapDataStream
			if isCompress {
				data = common.BitmapDecompress(&rectangle)
				isCompress = false
			}

			bitmap := &common.Bitmap{
				DestLeft:     int(rectangle.DestLeft),
				DestTop:      int(rectangle.DestTop),
				DestRight:    int(rectangle.DestRight),
				DestBottom:   int(rectangle.DestBottom),
				Width:        int(rectangle.Width),
				Height:       int(rectangle.Height),
				BitsPerPixel: common.Bpp(rectangle.BitsPerPixel),
				IsCompress:   isCompress,
				Data:         data,
			}
			positionMap[pos] = bitmap
		}
	})
}

/*
修改部分：grdp/protocol/x224/x224.go#New()
去除requestedProtocol中的PROTOCOL_HYBRID，避免使用NLA
*/

func RealGetScreen(host string, screenWidth, screenHeight int) ([]byte, error) {
	client := Newclient(host, glog.INFO, screenWidth, screenHeight)
	return client.Login("", "", "")
}
