package sync

import (
	"fmt"
	"plugin"

	"srs.epita.fr/fic-server/libfic"
)

var hooks = &CheckHooks{customHooks: map[string]CustomCheckHook{}}

type CheckFlagChoiceHook func(*fic.FlagChoice, *fic.Exercice, *CheckExceptions) []error
type CheckFlagKeyHook func(*fic.FlagKey, string, *fic.Exercice, *CheckExceptions) []error
type CheckFlagKeyWithChoicesHook func(*fic.FlagKey, string, []*fic.FlagChoice, *fic.Exercice, *CheckExceptions) []error
type CheckFlagLabelHook func(*fic.FlagLabel, *fic.Exercice, *CheckExceptions) []error
type CheckFlagMCQHook func(*fic.MCQ, []*fic.MCQ_entry, *fic.Exercice, *CheckExceptions) []error
type CheckFileHook func(*fic.EFile, *fic.Exercice, *CheckExceptions) []error
type CheckHintHook func(*fic.EHint, *fic.Exercice, *CheckExceptions) []error
type CheckMDTextHook func(string, string, *CheckExceptions, ...string) []error
type CheckExerciceHook func(*fic.Exercice, *CheckExceptions) []error
type CustomCheckHook func(interface{}, *CheckExceptions) []error

type CheckHooks struct {
	flagChoiceHooks         []CheckFlagChoiceHook
	flagKeyHooks            []CheckFlagKeyHook
	flagKeyWithChoicesHooks []CheckFlagKeyWithChoicesHook
	flagLabelHooks          []CheckFlagLabelHook
	flagMCQHooks            []CheckFlagMCQHook
	fileHooks               []CheckFileHook
	hintHooks               []CheckHintHook
	mdTextHooks             []CheckMDTextHook
	exerciceHooks           []CheckExerciceHook
	customHooks             map[string]CustomCheckHook
}

func (h *CheckHooks) RegisterFlagChoiceHook(f CheckFlagChoiceHook) {
	h.flagChoiceHooks = append(h.flagChoiceHooks, f)
}

func (h *CheckHooks) RegisterFlagKeyHook(f CheckFlagKeyHook) {
	h.flagKeyHooks = append(h.flagKeyHooks, f)
}

func (h *CheckHooks) RegisterFlagKeyWithChoicesHook(f CheckFlagKeyWithChoicesHook) {
	h.flagKeyWithChoicesHooks = append(h.flagKeyWithChoicesHooks, f)
}

func (h *CheckHooks) RegisterFlagLabelHook(f CheckFlagLabelHook) {
	h.flagLabelHooks = append(h.flagLabelHooks, f)
}

func (h *CheckHooks) RegisterFlagMCQHook(f CheckFlagMCQHook) {
	h.flagMCQHooks = append(h.flagMCQHooks, f)
}

func (h *CheckHooks) RegisterFileHook(f CheckFileHook) {
	h.fileHooks = append(h.fileHooks, f)
}

func (h *CheckHooks) RegisterHintHook(f CheckHintHook) {
	h.hintHooks = append(h.hintHooks, f)
}

func (h *CheckHooks) RegisterMDTextHook(f CheckMDTextHook) {
	h.mdTextHooks = append(h.mdTextHooks, f)
}

func (h *CheckHooks) RegisterExerciceHook(f CheckExerciceHook) {
	h.exerciceHooks = append(h.exerciceHooks, f)
}

func (h *CheckHooks) RegisterCustomHook(hookname string, f CustomCheckHook) {
	h.customHooks[hookname] = f
}

func (h *CheckHooks) CallCustomHook(hookname string, data interface{}, exceptions *CheckExceptions) []error {
	if v, ok := h.customHooks[hookname]; ok {
		return v(data, exceptions)
	}
	return nil
}

func LoadChecksPlugin(fname string) error {
	p, err := plugin.Open(fname)
	if err != nil {
		return err
	}

	register, err := p.Lookup("RegisterChecksHooks")
	if err != nil {
		return err
	}

	register.(func(*CheckHooks))(hooks)

	return nil
}

type CheckPluginList []string

func (l *CheckPluginList) String() string {
	return fmt.Sprintf("%v", *l)
}

func (l *CheckPluginList) Set(value string) error {
	*l = append(*l, value)
	return nil
}
