package rules

import (
	"fmt"
	"strconv"

	C "github.com/Dreamacro/clash/constant"
)

type PortType int

const (
	PortTypeSrc PortType = iota
	PortTypeDest
	PortTypeInbound
)

// Implements C.Rule
var _ C.Rule = (*Port)(nil)

type Port struct {
	adapter  string
	port     C.Port
	portType PortType
}

func (p *Port) RuleType() C.RuleType {
	switch p.portType {
	case PortTypeSrc:
		return C.SrcPort
	case PortTypeDest:
		return C.DstPort
	case PortTypeInbound:
		return C.InboundPort
	default:
		panic(fmt.Errorf("unknown port type: %v", p.portType))
	}
}

func (p *Port) Match(metadata *C.Metadata) bool {
	switch p.portType {
	case PortTypeSrc:
		return metadata.SrcPort == p.port
	case PortTypeDest:
		return metadata.DstPort == p.port
	case PortTypeInbound:
		return metadata.OriginDst.Port() == uint16(p.port)
	default:
		panic(fmt.Errorf("unknown port type: %v", p.portType))
	}
}

func (p *Port) Adapter() string {
	return p.adapter
}

func (p *Port) Payload() string {
	return p.port.String()
}

func (p *Port) ShouldResolveIP() bool {
	return false
}

func (p *Port) ShouldFindProcess() bool {
	return false
}

func NewPort(port string, adapter string, portType PortType) (*Port, error) {
	p, err := strconv.ParseUint(port, 10, 16)
	if err != nil {
		return nil, errPayload
	}
	return &Port{
		adapter:  adapter,
		port:     C.Port(p),
		portType: portType,
	}, nil
}
