/*
/*
 * Copyright (c) 2024 Augur AI, Inc.
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. 
 * If a copy of the MPL was not distributed with this file, you can obtain one at https://mozilla.org/MPL/2.0/.
 *
 
 * Copyright (c) 2024 Augur AI, Inc.
 *
 * This file is licensed under the Augur AI Proprietary License.
 *
 * Attribution:
 * This work is based on code from https://github.com/hofstadter-io/hof, licensed under the Apache License 2.0.
 */

package connector

import (
	"reflect"
)

// Base is the base implementation
type Base struct {
	name  string
	items []any
}

// New creates a new Base (i.e. Connector) with the given name.
// Name must have a non-empty value.
// It should also be unique, but that is not enforced.
func New(name string, items ...any) *Base {
	if name == "" {
		return nil
	}
	B := &Base{
		name:  name,
		items: []any{},
	}

	if len(items) > 0 {
		B.Add(items)
	}

	return B
}

// Name returns the name of the Base (i.e. Connector)
func (B *Base) Name() string {
	return B.name
}

// Named returns the name of the Base (i.e. Connector)
func (B *Base) Named() []Named {
	// make a copy
	all := []Named{}
	for _, item := range B.items {
		named, ok := item.(Named)
		if ok {
			all = append(all, named)
		}
	}

	return all
}

// Items returns all Items
func (B *Base) Items() []any {
	// make a copy
	all := []any{}
	for _, item := range B.items {
		all = append(all, item)
	}

	return all
}

// Add adds items to a Connector. Input may be a single object, slice, or any mix.
func (B *Base) Add(in ...any) {
	B.add(in)
}

func (B *Base) add(in any) {
	switch it := in.(type) {
	case []any:
		for _, i := range it {
			B.add(i)
		}
	default:
		B.items = append(B.items, it)

		itmzr, ok := in.(Itemizer)
		if ok {
			for _, i := range itmzr.Items() {
				B.items = append(B.items, i)
			}
		}
	}
}

// Del deletes an item or list of items from the connector.
func (B *Base) Del(out any) {
	switch ot := out.(type) {
	case []any:
		for _, o := range ot {
			B.Del(o)
		}
		return
	}

	// TODO otherwise look for objects
}

// Clear clears all items from this Connector.
// Will not effect connectors which this had been added to.
func (B *Base) Clear() {
	B.items = []any{}
}

/*Get will return items which implement a given type or interface.

Once a number of items have been added,
we will want to retrieve some subset of those.
The Get() Function will return all items
that match the given type
*/
func (B *Base) Get(in any) []any {
	typ := reflect.TypeOf(in).Elem()

	// make a copy
	all := []any{}
	for _, item := range B.items {
		it := reflect.TypeOf(item)
		if it.Implements(typ) {
			all = append(all, item)
		}
	}

	return all
}

/*Connect is the main usage function.

Connect() recursively passes a Connector object to all items
so that they may consume or use any items in the Connector.
Typically, we build up a root Connector and then
call Connect with itself as the argument.
*/
func (B *Base) Connect(c Connector) {
	for _, item := range B.Items() {
		switch typ := item.(type) {
		case Connectable:
			typ.Connect(c)
		}
	}
}
