package parser

import (
	"text/scanner"
)

type MakeThing interface {
	AsAssignment() (Assignment, bool)
	AsComment() (Comment, bool)
	AsDirective() (Directive, bool)
	AsRule() (Rule, bool)
	AsVariable() (Variable, bool)
	Dump() string
	Pos() scanner.Position
	EndPos() scanner.Position
}

type Assignment struct {
	makeThing
	Name   *MakeString
	Value  *MakeString
	Target *MakeString
	Type   string
}

type Comment struct {
	makeThing
	Comment string
}

type Directive struct {
	makeThing
	Name string
	Args *MakeString
}

type Rule struct {
	makeThing
	Target        *MakeString
	Prerequisites *MakeString
	Recipe        string
}

type Variable struct {
	makeThing
	Name *MakeString
}

type makeThing struct {
	pos    scanner.Position
	endPos scanner.Position
}

func (m makeThing) Pos() scanner.Position {
	return m.pos
}

func (m makeThing) EndPos() scanner.Position {
	return m.endPos
}

func (makeThing) AsAssignment() (a Assignment, ok bool) {
	return
}

func (a Assignment) AsAssignment() (Assignment, bool) {
	return a, true
}

func (a Assignment) Dump() string {
	target := ""
	if a.Target != nil {
		target = a.Target.Dump() + ": "
	}
	return target + a.Name.Dump() + a.Type + a.Value.Dump()
}

func (makeThing) AsComment() (c Comment, ok bool) {
	return
}

func (c Comment) AsComment() (Comment, bool) {
	return c, true
}

func (c Comment) Dump() string {
	return "#" + c.Comment
}

func (makeThing) AsDirective() (d Directive, ok bool) {
	return
}

func (d Directive) AsDirective() (Directive, bool) {
	return d, true
}

func (d Directive) Dump() string {
	return d.Name + " " + d.Args.Dump()
}

func (makeThing) AsRule() (r Rule, ok bool) {
	return
}

func (r Rule) AsRule() (Rule, bool) {
	return r, true
}

func (r Rule) Dump() string {
	recipe := ""
	if r.Recipe != "" {
		recipe = "\n" + r.Recipe
	}
	return "rule:       " + r.Target.Dump() + ": " + r.Prerequisites.Dump() + recipe
}

func (makeThing) AsVariable() (v Variable, ok bool) {
	return
}

func (v Variable) AsVariable() (Variable, bool) {
	return v, true
}

func (v Variable) Dump() string {
	return "$(" + v.Name.Dump() + ")"
}

type byPosition []MakeThing

func (s byPosition) Len() int {
	return len(s)
}

func (s byPosition) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s byPosition) Less(i, j int) bool {
	return s[i].Pos().Offset < s[j].Pos().Offset
}