package formulae

import "logik/core/utils"

/* ---------------------------------------------------------------- *
 * METHOD Get Atoms
 * ---------------------------------------------------------------- */

func schemeGetAtoms(fml Formula, prevValues [](*[]Formula)) *[]Formula {
	if fml.IsAtom() {
		return &[]Formula{fml.Copy()}
	} else {
		var results = []Formula{}
		for _, prevValue := range prevValues {
			results = append(results, *prevValue...)
		}
		return &results
	}
}

var fnGetAtoms = CreateFromSchemeFmlsValued(schemeGetAtoms)

func (fml Formula) GetAtoms() []Formula {
	return *fnGetAtoms(fml)
}

/* ---------------------------------------------------------------- *
 * TYPE FormulaPair, FormulaPairs
 * ---------------------------------------------------------------- */

type FormulaPair struct {
	Pos Formula
	Neg Formula
}

type FormulaPairs []FormulaPair

/* ---------------------------------------------------------------- *
 * Methods for FormulaPairs
 * ---------------------------------------------------------------- */

func NewFormulaPairs(pairs []FormulaPair) FormulaPairs { return pairs }

func (pairs FormulaPairs) Pos() []Formula {
	var fmls = make([]Formula, len(pairs))
	for i, pair := range pairs {
		fmls[i] = pair.Pos
	}
	return fmls
}

func (pairs FormulaPairs) Neg() []Formula {
	var fmls = make([]Formula, len(pairs))
	for i, pair := range pairs {
		fmls[i] = pair.Neg
	}
	return fmls
}

/* ---------------------------------------------------------------- *
 * TYPE KNF, DNF, Horn
 * ---------------------------------------------------------------- */

type FormulaConjunct []Formula
type FormulaDisjunct []Formula
type FormulaKNF []FormulaDisjunct
type FormulaDNF []FormulaConjunct
type FormulaHornClause struct {
	Pos FormulaDisjunct
	Neg FormulaConjunct
}
type FormulaHorn []FormulaHornClause

/* ---------------------------------------------------------------- *
 * Methods for KNF, DNF, Horn
 * ---------------------------------------------------------------- */

func (fml FormulaDisjunct) ToFormula() Formula {
	return Disjunction(fml)
}

func (fml FormulaConjunct) ToFormula() Formula {
	return Conjunction(fml)
}

func (fml FormulaKNF) ToFormula() Formula {
	var termsAsFormulae = make([]Formula, len(fml))
	for i, term := range fml {
		termsAsFormulae[i] = term.ToFormula()
	}
	return Conjunction(termsAsFormulae)
}

func (fml FormulaDNF) ToFormula() Formula {
	var termsAsFormulae = make([]Formula, len(fml))
	for i, term := range fml {
		termsAsFormulae[i] = term.ToFormula()
	}
	return Disjunction(termsAsFormulae)
}

func (fml FormulaHornClause) ToFormula() Formula {
	return Implies(fml.Neg.ToFormula(), fml.Pos.ToFormula())
}

func (fml FormulaHorn) ToFormula() Formula {
	var hornclausesAsFormulae = make([]Formula, len(fml))
	for i, hornclause := range fml {
		hornclausesAsFormulae[i] = hornclause.ToFormula()
	}
	return Conjunction(hornclausesAsFormulae)
}

func getAtomsFromArrayOfLiterals(fmls []Formula) []string {
	var atoms = []string{}
	var m = map[string]bool{}
	for _, fml := range fmls {
		if fml.IsLiteral() {
			m[fml.GetName()] = true
		}
	}
	for name, _ := range m {
		atoms = append(atoms, name)
	}
	utils.SortStrings(&atoms)
	return atoms
}

func getAtomsFromArrayOfArraysOfLiterals(fmlArrays [][]Formula) []string {
	var atoms = []string{}
	var m = map[string]bool{}
	for _, fmls := range fmlArrays {
		for _, atom := range getAtomsFromArrayOfLiterals(fmls) {
			m[atom] = true
		}
	}
	for name, _ := range m {
		atoms = append(atoms, name)
	}
	utils.SortStrings(&atoms)
	return atoms
}

func (fml FormulaConjunct) GetAtoms() []string {
	return getAtomsFromArrayOfLiterals(fml)
}

func (fml FormulaDisjunct) GetAtoms() []string {
	return getAtomsFromArrayOfLiterals(fml)
}

func (fml FormulaHornClause) GetAtoms() []string {
	return getAtomsFromArrayOfArraysOfLiterals([][]Formula{fml.Pos, fml.Neg})
}

func (fml FormulaKNF) GetAtoms() []string {
	var fmlArrays = make([][]Formula, len(fml))
	for i, term := range fml {
		fmlArrays[i] = term
	}
	return getAtomsFromArrayOfArraysOfLiterals(fmlArrays)
}

func (fml FormulaDNF) GetAtoms() []string {
	var fmlArrays = make([][]Formula, len(fml))
	for i, term := range fml {
		fmlArrays[i] = term
	}
	return getAtomsFromArrayOfArraysOfLiterals(fmlArrays)
}

func (fml FormulaHorn) GetAtoms() []string {
	var fmlArrays = make([][]Formula, 2*len(fml))
	for i, hornclause := range fml {
		fmlArrays[i] = append(hornclause.Neg, hornclause.Pos...)
	}
	return getAtomsFromArrayOfArraysOfLiterals(fmlArrays)
}
