package crawler

import (
	"crypto/tls"
	config "git.loafle.net/overflow/overflow_probe/agent_api/config_manager"
	"git.loafle.net/overflow/overflow_probe/matcher"
	"git.loafle.net/overflow/overflow_probe/matcher/packet"
	"git.loafle.net/overflow/overflow_probe/model/scaninfo"
	"net"
)

type SocketHeahthCrawler struct {
	CrawlerImpl
	m matcher.Matcher
}

func (s *SocketHeahthCrawler) SetMatcher(m matcher.Matcher) {
	s.m = m
}

func (s *SocketHeahthCrawler) getConnection(params config.Config) (net.Conn, error) {

	connection := params.Target.Connection

	ip := connection.Ip
	port := connection.Port
	portType := connection.PortType
	ssl := connection.SSL

	var addr string = ip
	addr += ":"
	addr += port

	if ssl == false {
		conn, err := net.Dial(portType, addr)
		if err != nil {
			return nil, err
		}
		return conn, nil
	} else {
		conn, err := tls.Dial(
			portType,
			addr,
			&tls.Config{
				InsecureSkipVerify: true,
				ServerName:         ip,
				ClientAuth:         tls.RequestClientCert,
			},
		)
		if err != nil {
			return nil, err
		}
		return conn, nil
	}
}

func (s *SocketHeahthCrawler) CheckHeahth(params config.Config) (bool, error) {
	conn, err := s.getConnection(params)
	if err != nil {
		return false, err
	}
	defer conn.Close()

	connection := params.Target.Connection
	info := scaninfo.NewScanInfoImpl(connection.Ip, connection.Port)

	if s.m.IsPrePacket() == true {
		bytes := make([]byte, 1024)
		n, _ := conn.Read(bytes)
		p := packet.NewPacket(bytes, n)
		if s.m.Match(0, p, info) == false {
			return false, nil
		} else {

			for i := 0; i < s.m.PacketCount(); i++ {
				pack := s.m.Packet(i)
				conn.Write(pack.Buffer)
				bytes := make([]byte, 1024)
				n, _ := conn.Read(bytes)

				if s.m.IsNoResponse(i+1) == true { // empty last response
					break
				}

				p := packet.NewPacket(bytes, n)
				if s.m.Match(i+1, p, info) == false {
					return false, nil
				}
			}

		}

	} else {
		for i := 0; i < s.m.PacketCount(); i++ {
			pack := s.m.Packet(i)
			conn.Write(pack.Buffer)
			bytes := make([]byte, 1024)
			n, _ := conn.Read(bytes)

			if s.m.IsNoResponse(i) == true { // empty last response
				break
			}

			p := packet.NewPacket(bytes, n)
			if s.m.Match(i, p, info) == false {
				return false, nil
			}
		}
	}

	return true, nil
}
