package config

import (
	"strings"

	"golang.org/x/xerrors"
)

// ScanMode has a type of scan mode. fast, fast-root, deep and offline
type ScanMode struct {
	flag byte
}

const (
	// Fast is fast scan mode
	Fast = byte(1 << iota)
	// FastRoot is scanmode
	FastRoot
	// Deep is scanmode
	Deep
	// Offline is scanmode
	Offline

	fastStr     = "fast"
	fastRootStr = "fast-root"
	deepStr     = "deep"
	offlineStr  = "offline"
)

// Set mode
func (s *ScanMode) Set(f byte) {
	s.flag |= f
}

// IsFast return whether scan mode is fast
func (s ScanMode) IsFast() bool {
	return s.flag&Fast == Fast
}

// IsFastRoot return whether scan mode is fastroot
func (s ScanMode) IsFastRoot() bool {
	return s.flag&FastRoot == FastRoot
}

// IsDeep return whether scan mode is deep
func (s ScanMode) IsDeep() bool {
	return s.flag&Deep == Deep
}

// IsOffline return whether scan mode is offline
func (s ScanMode) IsOffline() bool {
	return s.flag&Offline == Offline
}

func (s *ScanMode) ensure() error {
	numTrue := 0
	for _, b := range []bool{s.IsFast(), s.IsFastRoot(), s.IsDeep()} {
		if b {
			numTrue++
		}
	}
	if numTrue == 0 {
		s.Set(Fast)
	} else if s.IsDeep() && s.IsOffline() {
		return xerrors.New("Don't specify both of deep and offline")
	} else if numTrue != 1 {
		return xerrors.New("Specify only one of offline, fast, fast-root or deep")
	}
	return nil
}

func (s ScanMode) String() string {
	ss := ""
	if s.IsFast() {
		ss = fastStr
	} else if s.IsFastRoot() {
		ss = fastRootStr
	} else if s.IsDeep() {
		ss = deepStr
	}
	if s.IsOffline() {
		ss += " " + offlineStr
	}
	return ss + " mode"
}

func setScanMode(server *ServerInfo, d ServerInfo) error {
	if len(server.ScanMode) == 0 {
		server.ScanMode = Conf.Default.ScanMode
	}
	for _, m := range server.ScanMode {
		switch strings.ToLower(m) {
		case fastStr:
			server.Mode.Set(Fast)
		case fastRootStr:
			server.Mode.Set(FastRoot)
		case deepStr:
			server.Mode.Set(Deep)
		case offlineStr:
			server.Mode.Set(Offline)
		default:
			return xerrors.Errorf("scanMode: %s of %s is invalid. Specify -fast, -fast-root, -deep or offline",
				m, server.ServerName)
		}
	}
	if err := server.Mode.ensure(); err != nil {
		return xerrors.Errorf("%s in %s", err, server.ServerName)
	}
	return nil
}
