package asaModels

import (
	"regexp"
	"strconv"
	"strings"

	"github.rackspace.com/gorax/tools/util"
)

var (
	showIntRe         = regexp.MustCompile(`(?i)(|i)nterface\s+(\S+)\s+"(.*?)",\s+is.*?(up|down),\s+line.*?(up|down)((.|\n)*?)(\n\w|$)`)
	showIntHWRe       = regexp.MustCompile(`(?i)hardware\s+is\s+(.*?),`)
	showIntMACRe      = regexp.MustCompile(`(?i)mac\s+address\s+(.*?),`)
	showIntIPRe       = regexp.MustCompile(`(?i)ip\s+address\s+(.*?),`)
	showIntMaskRe     = regexp.MustCompile(`(?i)subnet\s+mask\s+(\S+)`)
	showIntInputErr   = regexp.MustCompile(`(?i)(\d+)\s+input\s+errors`)
	showIntCRCErr     = regexp.MustCompile(`(?i)(\d+)\s+crc,`)
	showIntOverRun    = regexp.MustCompile(`(?i)(\d+)\s+overrun`)
	showIntUnderRun   = regexp.MustCompile(`(?i)(\d+)\s+underruns`)
	showIntInput      = regexp.MustCompile(`(?i)(\d+)\s+packets\s+input,\s+(\d+)\s+bytes`)
	showIntOutput     = regexp.MustCompile(`(?i)(\d+)\s+packets\s+output,\s+(\d+)\s+bytes`)
	showIntResets     = regexp.MustCompile(`(?i)(\d+)\s+interface\s+resets`)
	showIntOutputErr  = regexp.MustCompile(`(?i)(\d+)\s+output\s+errors,`)
	showIntCollisions = regexp.MustCompile(`(?i)(\d+)\s+collisions`)
	showIntBroadcast  = regexp.MustCompile(`(?i)received\s+(\d+)\s+broadcasts`)
)

type (
	InterfacesStatus struct {
		Interfaces []*InterfaceStatus `json:"show_interfaces"`
		ifmap      map[string]*InterfaceStatus
	}

	InterfaceStatus struct {
		Name          string             `json:"interface"`
		IfName        string             `json:"ifname"`
		Admin         bool               `json:"admin_up"`
		Link          bool               `json:"link"`
		Hardware      string             `json:"hardware"`
		MAC           string             `json:"mac_address"`
		IPAddress     string             `json:"ip_address"`
		Netmask       string             `json:"netmask"`
		Resets        int                `json:"interface_resets"`
		Broadcast     int                `json:"received_broadcasts"`
		Collisions    int                `json:"collisions"`
		CRCErr        int                `json:"crc_errors"`
		Overruns      int                `json:"overruns"`
		Underruns     int                `json:"underruns"`
		InputErr      int                `json:"input_errors"`
		InputPackets  int                `json:"input_packets"`
		InputBytes    int                `json:"input_bytes"`
		OutputErr     int                `json:"output_errors"`
		OutputPackets int                `json:"output_packets"`
		OutputBytes   int                `json:"output_bytes"`
		Number        *int               `json:"number,omitempy"`
		SubInterface  []*InterfaceStatus `json:"sub_interfaces"`
	}
)

// ParseShowInterfaces is a function that creates the InterfaceStatus types and invokes the
// Parse method on the type, then returns the parsed structure.
func ParseShowInterfaces(s string) InterfacesStatus {
	var i InterfacesStatus
	i.Parse(s)
	return i
}

// Parse method will take an ASA interface status output and parse the
// data into the InterfacesStatus pointer receiver.
func (v *InterfacesStatus) Parse(s string) {
	order := make([]string, 0)
	v.ifmap = make(map[string]*InterfaceStatus)

	for _, i := range showIntRe.FindAllStringSubmatch(s, -1) {
		port := intSplitRE.FindStringSubmatch(i[2])
		if len(port) == 0 {
			order = append(order, i[2])
			v.ifmap[i[2]] = newShowInt(i, i[2], nil)
			continue
		}
		if _, ok := v.ifmap[port[1]]; !ok {
			order = append(order, port[2])
			v.ifmap[port[1]] = &InterfaceStatus{Name: port[1]}
		}
		v.ifmap[port[1]].SubInterface = append(v.ifmap[port[1]].SubInterface, newShowInt(i, i[2], util.AtoiPointer(port[2])))
	}
	for _, pn := range order {
		v.Interfaces = append(v.Interfaces, v.ifmap[pn])
	}
}

func newShowInt(i []string, name string, number *int) *InterfaceStatus {
	iCount := showIntInput.FindStringSubmatch(i[6])
	oCount := showIntOutput.FindStringSubmatch(i[6])
	return &InterfaceStatus{
		Number:        number,
		Name:          name,
		IfName:        i[3],
		Admin:         i[4] == "up",
		Link:          i[5] == "up",
		Hardware:      util.RegexpString(showIntHWRe, i[6]),
		MAC:           util.RegexpString(showIntMACRe, i[6]),
		IPAddress:     util.RegexpString(showIntIPRe, i[6]),
		Netmask:       util.RegexpString(showIntMaskRe, i[6]),
		CRCErr:        util.RegexpAtoInt(showIntCRCErr, i[6]),
		Overruns:      util.RegexpAtoInt(showIntOverRun, i[6]),
		Underruns:     util.RegexpAtoInt(showIntUnderRun, i[6]),
		InputErr:      util.RegexpAtoInt(showIntInputErr, i[6]),
		OutputErr:     util.RegexpAtoInt(showIntOutputErr, i[6]),
		Resets:        util.RegexpAtoInt(showIntResets, i[6]),
		Broadcast:     util.RegexpAtoInt(showIntBroadcast, i[6]),
		Collisions:    util.RegexpAtoInt(showIntCollisions, i[6]),
		InputPackets:  intCounters(iCount, 1),
		InputBytes:    intCounters(iCount, 2),
		OutputPackets: intCounters(oCount, 1),
		OutputBytes:   intCounters(oCount, 2),
	}
}

func intCounters(c []string, place int) int {
	if len(c) != 3 {
		return -1
	}
	i, err := strconv.Atoi(c[place])
	if err != nil {
		return -1
	}
	return i
}

func FindInterfaceStatus(name string, il []*InterfaceStatus) *InterfaceStatus {
	name = strings.ToLower(name)
	for _, p := range il {
		if strings.ToLower(p.Name) == name {
			return p
		}
	}
	return nil
}

func (i *InterfaceStatus) FindSubInterface(si int) *InterfaceStatus {
	for _, i := range i.SubInterface {
		if i.Number != nil {
			if *i.Number == si {
				return i
			}
		}
	}
	return nil
}

func (i *InterfaceStatus) IsVlan() bool {
	// FIX: NI-1283:2
	if i == nil {
		return true
	}
	if vlanIntRE.MatchString(i.Name) {
		return true
	}
	return false
}
