package oracle

import (
	"bytes"
	"encoding/binary"
	"git.loafle.net/overflow/overflow_probe/matcher/packet"
	"git.loafle.net/overflow/overflow_probe/model/scaninfo"
)

type OracleMatcher struct {
	sendPackets []*packet.Packet
}

func (o *OracleMatcher) Match(index int, packet *packet.Packet, info scaninfo.ServiceScanInfo) bool {

	if packet == nil {
		return false
	}

	header := header_packet{}
	refuse := body_refuse{}

	buf := new(bytes.Buffer)
	buf.Write(packet.Buffer)

	binary.Read(buf, binary.BigEndian, &header)
	binary.Read(buf, binary.BigEndian, &refuse)

	//fmt.Println(header)
	//fmt.Println(refuse)

	if header.Check_sum != 0 {
		return false
	}
	if header.Types != 4 {
		return false
	}
	if header.Reserved_byte != 0 {
		return false
	}
	if header.Header_sum != 0 {
		return false
	}
	if refuse.Reason_user != 34 {
		return false
	}
	if refuse.Reason_system != 0 {
		return false
	}

	var dataLen int = int(refuse.Data_len)
	if dataLen != packet.Len-12 { //
		if dataLen != packet.Len-22 { // morformed packet error not user not service
			return false
		}
	}

	return true
}

func (o *OracleMatcher) PacketCount() int {
	return len(o.sendPackets)
}
func (o *OracleMatcher) Packet(index int) *packet.Packet {
	return o.sendPackets[index]
}
func (o *OracleMatcher) ServiceName() string {
	return "OracleMatcher"
}

func (o *OracleMatcher) IsError(index int, packet *packet.Packet, info scaninfo.ServiceScanInfo) bool {
	return false
}
func (o *OracleMatcher) IsNoResponse(index int) bool {

	return false
}

func (o *OracleMatcher) IsPrePacket() bool {
	return false
}

func NewOracleMatcher() *OracleMatcher {

	pm := OracleMatcher{}

	hp := header_packet{
		Length:        247,
		Check_sum:     0,
		Types:         1,
		Reserved_byte: 0,
		Header_sum:    0,
	}

	bc := body_connect{
		Version:            315,
		Version_compatible: 300,
		//Service_options:
		Session_unit_size:            8192,
		Maxumum_trans_data_unit_size: 65535,
		//Nt_protocol_characteristics:
		Line_turnaround_value:           0,
		Value_of_1_in_hardware:          1,
		Length_of_connect_data:          177,
		Offset_to_connect_data:          70,
		Maximum_receivable_connect_data: 0,
		//Connect_flag0:
		//Connect_flag1:
		Trace_cross_facility_item_1: 0,
		Trace_cross_facility_item_2: 0,
		Trace_unique_connection_id:  0,
		//Unknown_data:
		//Connect_data:

	}

	bc.Service_options[0] = 0x0c
	bc.Service_options[1] = 0x41

	bc.Nt_protocol_characteristics[0] = 0x4f
	bc.Nt_protocol_characteristics[1] = 0x98

	bc.Connect_flag0 = 0x81
	bc.Connect_flag1 = 0x81

	bc.Unknown_data[10] = 0x20
	bc.Unknown_data[13] = 0x20

	conDataStr := "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.30)(PORT=1521))(CONNECT_DATA=(CID=(PROGRAM=JDBC Thin Client)(HOST=__jdbc__)(USER=loafle.match))(SERVICE_NAME=oracle.loafle.com1)))"
	//conDataStr := "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.30)(PORT=1521))(CONNECT_DATA=(CID=(PROGRAM=JDBC Thin Client)(HOST=__jdbc__)(USER=Jackdaw))(SERVICE_NAME=oracle.loafle.co1m)))"

	connect_data := make([]byte, len(conDataStr))
	copy(connect_data, conDataStr)

	hpBuf := new(bytes.Buffer)
	binary.Write(hpBuf, binary.BigEndian, hp)

	hpBt := hpBuf.Bytes()

	bcBuf := new(bytes.Buffer)
	binary.Write(bcBuf, binary.BigEndian, bc)
	bcBt := bcBuf.Bytes()

	byteSize := len(hpBt) + len(bcBt) + len(conDataStr)
	sendByte := make([]byte, byteSize)

	copy(sendByte[0:], hpBt)
	copy(sendByte[len(hpBt):], bcBt)
	copy(sendByte[len(hpBt)+len(bcBt):], connect_data)

	pm.sendPackets = append(pm.sendPackets, packet.NewPacket(sendByte, byteSize))

	return &pm

}

type header_packet struct {
	Length        uint16
	Check_sum     uint16
	Types         byte
	Reserved_byte byte
	Header_sum    uint16
}

type body_connect struct {
	Version                         uint16
	Version_compatible              uint16
	Service_options                 [2]byte
	Session_unit_size               uint16
	Maxumum_trans_data_unit_size    uint16
	Nt_protocol_characteristics     [2]byte
	Line_turnaround_value           uint16
	Value_of_1_in_hardware          uint16
	Length_of_connect_data          uint16
	Offset_to_connect_data          uint16
	Maximum_receivable_connect_data uint32
	Connect_flag0                   byte
	Connect_flag1                   byte
	Trace_cross_facility_item_1     uint32
	Trace_cross_facility_item_2     uint32
	Trace_unique_connection_id      uint64
	Unknown_data                    [20]byte
	//Connect_data                    []byte
}

type body_refuse struct {
	Reason_user   byte
	Reason_system byte
	Data_len      uint16
	//Data          []byte
}
