/*
Copyright 2015 Google Inc. All rights reserved.

Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file or at
https://developers.google.com/open-source/licenses/bsd
*/

// Package oid implements SNMP OID and related data structures.
package oid

import (
	"fmt"
	"sort"
	"strings"
)

// OID reprents a numeric object ID.
type OID []uint

// AsString formats the OID as a string.
func (o OID) AsString() string {
	q := make([]string, len(o))
	for i := range o {
		q[i] = fmt.Sprintf("%d", o[i])
	}
	return strings.Join(q, ".")
}

// HasPrefix answers the question "does this OID have this prefix?"
func (a OID) HasPrefix(b OID) bool {
	if len(a) < len(b) {
		return false
	}

	for i := 0; i < len(b); i++ {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}

// IsEqualTo checks whether this OID == that OID.
func (a OID) IsEqualTo(b OID) bool {
	if len(a) != len(b) {
		return false
	}

	for i := 0; i < len(a); i++ {
		if a[i] != b[i] {
			return false
		}
	}

	return true
}

// ComesBefore answers the question "does this OID sort before that OID?"
func (a OID) ComesBefore(b OID) bool {
	var size int
	if len(a) < len(b) {
		size = len(a)
	} else {
		size = len(b)
	}

	for i := 0; i < size; i++ {
		if a[i] < b[i] {
			return true
		}
		if a[i] > b[i] {
			return false
		}
	}

	if len(a) < len(b) {
		return true
	}

	return false
}

// Variable represents an OID name:value pair.
type Variable struct {
	Name  OID
	Value string
}

// NameAsString formats OID.Name as a string.
func (v *Variable) NameAsString() string {
	return v.Name.AsString()
}

// VariableSet represents an ordered set of OID variables.
type VariableSet struct {
	vars []Variable
}

// Size gets the current size of this set.
func (vs *VariableSet) Size() int {
	return len(vs.vars)
}

// Returns the variables in this set.
func (vs *VariableSet) Variables() []Variable {
	return vs.vars
}

// AddVariable adds a variable to this set.
func (vs *VariableSet) AddVariable(name OID, value string) {
	if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
		value = value[1 : len(value)-1]
	}
	vs.vars = append(vs.vars, Variable{name, value})
}

// GetSubtree gets the subtree of variables whose OID name has prefix.
func (vs *VariableSet) GetSubtree(prefix OID) *VariableSet {
	head := sort.Search(len(vs.vars), func(i int) bool { return !vs.vars[i].Name.ComesBefore(prefix) })
	tail := head
	for tail < len(vs.vars) && vs.vars[tail].Name.HasPrefix(prefix) {
		tail++
	}
	return &VariableSet{vs.vars[head:tail]}
}

// GetValues gets all values in this set.
func (vs *VariableSet) GetValues() []string {
	values := make([]string, len(vs.vars))
	for i := range vs.vars {
		values[i] = vs.vars[i].Value
	}
	return values
}

// GetVariable gets a single variable, by OID.
func (vs *VariableSet) GetVariable(o OID) (*Variable, bool) {
	subtree := vs.GetSubtree(o)
	if len(subtree.vars) > 0 && subtree.vars[0].Name.IsEqualTo(o) {
		return &subtree.vars[0], true
	}
	return nil, false
}

// GetValue gets the value of a single variable, by OID.
func (vs *VariableSet) GetValue(o OID) (string, bool) {
	if v, exists := vs.GetVariable(o); exists {
		return v.Value, true
	}
	return "", false
}
