package util

import (
	"errors"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"strconv"
	"strings"
)

// MinFreeSpaceType is the type of MinFreeSpace.
type MinFreeSpaceType int

const (
	// AsPercent set the MinFreeSpaceType to a percentage value from 0 to 100.
	AsPercent MinFreeSpaceType = iota
	// AsBytes set the MinFreeSpaceType to a absolute value bytes.
	AsBytes
)

// MinFreeSpace is type that defines the limit for the minimum free space.
type MinFreeSpace struct {
	Type    MinFreeSpaceType
	Bytes   uint64
	Percent float32
	Raw     string
}

// IsLow tells whether the free space is low or not.
func (s MinFreeSpace) IsLow(freeBytes uint64, freePercent float32) (yes bool, desc string) {
	switch s.Type {
	case AsPercent:
		yes = freePercent < s.Percent
		op := IfElse(yes, "<", ">=")
		return yes, fmt.Sprintf("disk free %.2f%% %s required %.2f%%", freePercent, op, s.Percent)
	case AsBytes:
		yes = freeBytes < s.Bytes
		op := IfElse(yes, "<", ">=")
		return yes, fmt.Sprintf("disk free %s %s required %s",
			BytesToHumanReadable(freeBytes), op, BytesToHumanReadable(s.Bytes))
	}

	return false, ""
}

// String returns a string representation of MinFreeSpace.
func (s MinFreeSpace) String() string {
	switch s.Type {
	case AsPercent:
		return fmt.Sprintf("%.2f%%", s.Percent)
	default:
		return s.Raw
	}
}

// MustParseMinFreeSpace parses comma-separated argument for min free space setting.
// minFreeSpace has the high priority than minFreeSpacePercent if it is set.
func MustParseMinFreeSpace(minFreeSpace string, minFreeSpacePercent string) (spaces []MinFreeSpace) {
	ss := strings.Split(EmptyTo(minFreeSpace, minFreeSpacePercent), ",")
	for _, freeString := range ss {
		if vv, e := ParseMinFreeSpace(freeString); e == nil {
			spaces = append(spaces, *vv)
		} else {
			glog.Fatalf("The value specified in -minFreeSpace not a valid value %s", freeString)
		}
	}

	return spaces
}

var ErrMinFreeSpaceBadValue = errors.New("minFreeSpace is invalid")

// ParseMinFreeSpace parses min free space expression s as percentage like 1,10 or human readable size like 10G
func ParseMinFreeSpace(s string) (*MinFreeSpace, error) {
	if percent, e := strconv.ParseFloat(s, 32); e == nil {
		if percent < 0 || percent > 100 {
			return nil, ErrMinFreeSpaceBadValue
		}
		return &MinFreeSpace{Type: AsPercent, Percent: float32(percent), Raw: s}, nil
	}

	if directSize, e := ParseBytes(s); e == nil {
		if directSize <= 100 {
			return nil, ErrMinFreeSpaceBadValue
		}
		return &MinFreeSpace{Type: AsBytes, Bytes: directSize, Raw: s}, nil
	}

	return nil, ErrMinFreeSpaceBadValue
}