package registry

import (
	"fmt"
	"reflect"
)

//DescType type Description
type DescType struct {
	Type string `json:"type"`
	Name string `json:"name"`
}

// Description of an entry
type Description struct {
	Name string   `json:"name"`
	Tags []string `json:"categories"`

	//InputType
	Inputs []DescType `json:"inputs"`
	Output DescType   `json:"output"`

	Extra map[string]interface{} `json:"extra"`
}

// Entry contains a function description params etc
type Entry struct {
	registry    *R
	fn          interface{}
	Inputs      []reflect.Type
	Output      reflect.Type
	Description *Description
	err         error
}

// NewEntry creates and describes a New Entry
func NewEntry(r *R, fn interface{}) *Entry {
	e := &Entry{registry: r, fn: fn}

	fntyp := reflect.TypeOf(e.fn)
	if fntyp.Kind() != reflect.Func {
		e.err = ErrNotAFunc
		return e
	}
	if fntyp.NumOut() == 0 {
		e.err = ErrOutput
		return e
	}

	fnTyp := reflect.TypeOf(e.fn)
	nInputs := fnTyp.NumIn()
	Inputs := make([]DescType, nInputs)
	for i := 0; i < nInputs; i++ {
		Inputs[i] = DescType{fmt.Sprint(fnTyp.In(i)), ""}
		e.Inputs = append(e.Inputs, fnTyp.In(i)) // ?
	}
	Output := DescType{fmt.Sprint(fnTyp.Out(0)), ""}
	e.Output = fnTyp.Out(0) // ?

	e.Description = &Description{
		Tags:   []string{"generic"},
		Inputs: Inputs,
		Output: Output,
		Extra:  map[string]interface{}{},
	}
	return e
}

// Err returns error of the entry if any
func (e *Entry) Err() error {
	return e.err
}

/////////////
// Descriptions
/////

//Tags of the entry
func (e *Entry) Tags(cat ...string) *Entry {
	if e.err != nil {
		return e
	}
	e.Description.Tags = cat
	return e
}

// DescInputs description for Inputs
func (e *Entry) DescInputs(desc ...string) *Entry {
	if e.err != nil {
		return e
	}
	for i, d := range desc {
		if i >= len(e.Description.Inputs) { // do nothing
			return e
		}
		curDesc := e.Description.Inputs[i]
		e.Description.Inputs[i] = DescType{curDesc.Type, d}
	}
	return e
}

// DescOutput description for Input
func (e *Entry) DescOutput(desc string) *Entry {
	if e.err != nil {
		return e
	}
	e.Description.Output = DescType{
		e.Description.Output.Type,
		desc,
	}
	return e
}

// Extra information on entry
func (e *Entry) Extra(name string, extra interface{}) *Entry {
	if e.err != nil {
		return e
	}
	e.Description.Extra[name] = extra
	return e
}
