package gonx

import (
	"fmt"
	"strconv"
	"strings"
)

// Fields is a shortcut for the map of strings
type Fields map[string]string

// Entry is a parsed log record. Use Get method to retrieve a value by name instead of
// threating this as a map, because inner representation is in design.
type Entry struct {
	fields Fields
}

// NewEmptyEntry creates an empty Entry to be filled later
func NewEmptyEntry() *Entry {
	return &Entry{make(Fields)}
}

// NewEntry creates an Entry with fiven fields
func NewEntry(fields Fields) *Entry {
	return &Entry{fields}
}

// Field returns an entry field value by name or empty string and error if it
// does not exist.
func (entry *Entry) Field(name string) (value string, err error) {
	value, ok := entry.fields[name]
	if !ok {
		err = fmt.Errorf("field '%v' does not found in record %+v", name, *entry)
	}
	return
}

// FloatField returns an entry field value as float64. Return nil if field does not exist
// and conversion error if cannot cast a type.
func (entry *Entry) FloatField(name string) (value float64, err error) {
	tmp, err := entry.Field(name)
	if err == nil {
		value, err = strconv.ParseFloat(tmp, 64)
	}
	return
}

// SetField sets the value of a field
func (entry *Entry) SetField(name string, value string) {
	entry.fields[name] = value
}

// SetFloatField is a Float field value setter. It accepts float64, but still store it as a
// string in the same fields map. The precision is 2, its enough for log
// parsing task
func (entry *Entry) SetFloatField(name string, value float64) {
	entry.SetField(name, strconv.FormatFloat(value, 'f', 2, 64))
}

// SetUintField is a Integer field value setter. It accepts float64, but still store it as a
// string in the same fields map.
func (entry *Entry) SetUintField(name string, value uint64) {
	entry.SetField(name, strconv.FormatUint(uint64(value), 10))
}

// Merge two entries by updating values for master entry with given.
func (entry *Entry) Merge(merge *Entry) {
	for name, value := range merge.fields {
		entry.SetField(name, value)
	}
}

// FieldsHash returns a hash of all fields
func (entry *Entry) FieldsHash(fields []string) string {
	var key []string
	for _, name := range fields {
		value, err := entry.Field(name)
		if err != nil {
			value = "NULL"
		}
		key = append(key, fmt.Sprintf("'%v'=%v", name, value))
	}
	return strings.Join(key, ";")
}

// Partial returns a partial field entry with the specified fields
func (entry *Entry) Partial(fields []string) *Entry {
	partial := NewEmptyEntry()
	for _, name := range fields {
		value, _ := entry.Field(name)
		partial.SetField(name, value)
	}
	return partial
}
