package formulae

import (
	"fmt"
	"strings"
)

/* ---------------------------------------------------------------- *
 * TYPE Formula
 * ---------------------------------------------------------------- */

type Formula struct {
	kind        string
	name        string
	expr        string
	valence     int
	subformulae [](*Formula)
}

/* ---------------------------------------------------------------- *
 * METHODS
 * ---------------------------------------------------------------- */

func (fml *Formula) SetKind(kind string) { fml.kind = kind }

func (fml Formula) GetKind() string { return fml.kind }

func (fml *Formula) SetExpr(expr string) { fml.expr = expr }

func (fml Formula) GetExpr() string { return fml.expr }

func (fml Formula) GetName() string { return fml.name }

func (fml *Formula) SetSubformulae(subFmls [](*Formula)) {
	fml.subformulae = subFmls
	fml.valence = len(subFmls)
}

func (fml Formula) GetSubFormulae() []Formula {
	var n int = fml.valence
	var subFmls = make([]Formula, n)
	for i, subFml := range fml.subformulae {
		subFmls[i] = *subFml
	}
	return subFmls
}

func (fml Formula) GetAllSubFormulae() []Formula {
	var subFml = fml.GetSubFormulae()
	var result = []Formula{fml.Copy()}
	for _, child := range subFml {
		result = append(result, child.GetAllSubFormulae()...)
	}
	return result
}

func (fml Formula) GetAllSubFormulaeStrict() []Formula {
	var subFml = fml.GetSubFormulae()
	var result = []Formula{}
	for _, child := range subFml {
		result = append(result, child.GetAllSubFormulae()...)
	}
	return result
}

func (fml Formula) GetChild(indexOpt ...int) Formula {
	var index int = 0
	if len(indexOpt) > 0 {
		index = indexOpt[0]
	}
	var subFml Formula
	if 0 <= index && index < fml.valence {
		subFml = *(fml.subformulae[index])
	} else {
		panic(fmt.Sprintf("Instance has no child of index %d !", index))
	}
	return subFml
}

func (fml Formula) Pretty(preindentOpt ...string) string {
	var preindent string = ""
	if len(preindentOpt) > 0 {
		preindent = preindentOpt[0]
	}
	return fml.pretty(preindent, "  ", "", 0)
}

func (fml Formula) pretty(preindent string, tab string, prepend string, depth int) string {
	var indent string = preindent + strings.Repeat(tab, depth)
	switch fml.valence {
	case 0:
		switch kind := fml.kind; kind {
		case "atom", "generic":
			return indent + prepend + kind + " " + fml.expr
		default:
			return indent + prepend + kind
		}
	default:
		var lines string = indent + prepend + fml.kind
		prepend = "|__ "
		for _, subFml := range fml.subformulae {
			lines += "\n" + subFml.pretty(preindent, tab, prepend, depth+1)
		}
		return lines
	}
}

// shallow copy
func (fml Formula) Copy() Formula {
	return Formula{
		expr:        fml.expr,
		kind:        fml.kind,
		valence:     fml.valence,
		subformulae: fml.subformulae,
	}
}

func (fml Formula) Deepcopy() Formula {
	var subFmls = make([](*Formula), len(fml.subformulae))
	for i, subFml := range fml.subformulae {
		subFmlCopy := subFml.Deepcopy()
		subFmls[i] = &subFmlCopy
	}
	return Formula{
		expr:        fml.expr,
		kind:        fml.kind,
		valence:     fml.valence,
		subformulae: subFmls,
	}
}

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
 * METHODS: Recognition of Formula-Types
 * ---------------------------------------------------------------- */

func (fml Formula) IsIrreducible() bool         { return fml.valence == 0 }
func (fml Formula) IsAtom() bool                { return fml.kind == "atom" }
func (fml Formula) IsPositiveLiteral() bool     { return fml.IsAtom() }
func (fml Formula) IsNegativeLiteral() bool     { return fml.IsNegation() && fml.GetChild().IsAtom() }
func (fml Formula) IsLiteral() bool             { return fml.IsPositiveLiteral() || fml.IsNegativeLiteral() }
func (fml Formula) IsGeneric() bool             { return fml.kind == "generic" }
func (fml Formula) IsTautologySymbol() bool     { return fml.kind == "taut" }
func (fml Formula) IsContradictionSymbol() bool { return fml.kind == "contradiction" }
func (fml Formula) IsConnective() bool          { return fml.valence > 0 }
func (fml Formula) IsNegation() bool            { return fml.kind == "not" }
func (fml Formula) IsConjunction2() bool        { return fml.kind == "and2" }
func (fml Formula) IsConjunction() bool         { return fml.kind == "and" || fml.kind == "and2" }
func (fml Formula) IsDisjunction2() bool        { return fml.kind == "or2" }
func (fml Formula) IsDisjunction() bool         { return fml.kind == "or" || fml.kind == "or2" }
func (fml Formula) IsImplication() bool         { return fml.kind == "implies" }
func (fml Formula) IsDoubleImplication() bool   { return fml.kind == "iff" }
