package hints

import (
	"fmt"
	"regexp"
	"strings"
)

type fileType uint8

const (
	file fileType = iota
	directory
	characterSpecialFile
)

type perms struct {
	Type   fileType
	Owner  access
	Group  access
	World  access
	SetUID bool
	SetGID bool
	Sticky bool
}

type accessType uint8

const (
	owner accessType = iota
	group
	world
)

type access struct {
	Type    accessType
	Read    bool
	Write   bool
	Execute bool
}

func (p perms) Numeric() string {
	return p.Owner.Numeric() + p.Group.Numeric() + p.World.Numeric()
}

func (a access) Nice() string {
	all := []string{}
	if a.Read {
		all = append(all, "read")
	}
	if a.Write {
		all = append(all, "write")
	}
	if a.Execute {
		all = append(all, "execute")
	}

	return strings.Join(all, ", ")
}

func (a access) Numeric() string {
	var n uint8
	if a.Read {
		n += 4
	}
	if a.Write {
		n += 2
	}
	if a.Execute {
		n++
	}
	return fmt.Sprintf("%d", n)
}

func parsePermissionString(s string) (perms, error) {
	if !isPermString(s) {
		return perms{}, fmt.Errorf("Invalid permission string")
	}
	p := perms{}
	switch s[0] {
	case 'c':
		p.Type = characterSpecialFile
	case 'd':
		p.Type = directory
	default:
		p.Type = file
	}

	p.SetUID = s[3] == 's' || s[3] == 'S'
	p.SetGID = s[6] == 's' || s[6] == 'S'
	p.Sticky = s[9] == 't' || s[9] == 'T'

	p.Owner.Type = owner
	p.Owner.Read = s[1] == 'r'
	p.Owner.Write = s[2] == 'w'
	p.Owner.Execute = s[3] == 'x' || s[3] == 's'
	p.Group.Type = group
	p.Group.Read = s[4] == 'r'
	p.Group.Write = s[5] == 'w'
	p.Group.Execute = s[6] == 'x' || s[6] == 's'
	p.World.Type = world
	p.World.Read = s[7] == 'r'
	p.World.Write = s[8] == 'w'
	p.World.Execute = s[9] == 'x'

	return p, nil
}

func init() {
	hinters = append(hinters, hintPerms)
}

func hintPerms(word string, context string, wordX uint16, wordY uint16) *Hint {

	item := &Hint{
		Line:   context,
		Word:   word,
		StartX: wordX,
		StartY: wordY,
	}

	if wordX == 0 {

		p, err := parsePermissionString(word)
		if err != nil {
			return nil
		}

		typ := "file"
		switch p.Type {
		case directory:
			typ = "directory"
		case characterSpecialFile:
			typ = "character special file"
		}

		item.Description = fmt.Sprintf(`Permissions: 
  Type:    %s
  Numeric: %s
  Owner:   %s
  Group:   %s
  World:   %s
  Setuid:  %t
  Setgid:  %t
  Sticky:  %t
  `,
			typ,
			p.Numeric(),
			p.Owner.Nice(),
			p.Group.Nice(),
			p.World.Nice(),
			p.SetUID,
			p.SetGID,
			p.Sticky,
		)

		return item
	}

	return nil
}

func isPermString(s string) bool {
	re := regexp.MustCompile("[cdl\\-sS]{1}[sStTrwx\\-]{9}")
	return re.MatchString(s)
}
