// behavorial
package pattern

import (
	"fmt"
)

// 13. Chain of Responsibility Pattern
type Handler interface {
	SetNext(handler Handler)
	Handle(request string) string
}

type AbstractHandler struct {
	next Handler
}

func (h *AbstractHandler) SetNext(handler Handler) {
	h.next = handler
}

type ConcreteHandler1 struct {
	AbstractHandler
}

func (h *ConcreteHandler1) Handle(request string) string {
	if request == "one" {
		return "Handler1: handled"
	}
	if h.next != nil {
		return h.next.Handle(request)
	}
	return ""
}

// 14. Command Pattern
type Command interface {
	Execute() string
}

type Light struct{}

func (l *Light) TurnOn() string  { return "Light is on" }
func (l *Light) TurnOff() string { return "Light is off" }

type LightOnCommand struct {
	light *Light
}

func (c *LightOnCommand) Execute() string {
	return c.light.TurnOn()
}

// 15. Interpreter Pattern
type Expression interface {
	Interpret() bool
}

type TerminalExpression struct {
	data string
}

func (t *TerminalExpression) Interpret() bool {
	return len(t.data) > 0
}

// 16. Iterator Pattern
type Iterator interface {
	HasNext() bool
	Next() interface{}
}

type Collection struct {
	items []interface{}
}

type CollectionIterator struct {
	collection *Collection
	index      int
}

func (i *CollectionIterator) HasNext() bool {
	return i.index < len(i.collection.items)
}

func (i *CollectionIterator) Next() interface{} {
	if i.HasNext() {
		item := i.collection.items[i.index]
		i.index++
		return item
	}
	return nil
}

// 17. Mediator Pattern
type Mediator interface {
	Notify(sender string, event string)
}

type ConcreteMediator struct {
	component1 *Component1
	component2 *Component2
}

type Component1 struct {
	mediator Mediator
}

type Component2 struct {
	mediator Mediator
}

func (m *ConcreteMediator) Notify(sender string, event string) {
	fmt.Printf("Mediator reacts on %s and triggers event: %s\n", sender, event)
}

// 18. Memento Pattern
type Memento struct {
	state string
}

type Originator struct {
	state string
}

func (o *Originator) CreateMemento() *Memento {
	return &Memento{state: o.state}
}

func (o *Originator) RestoreMemento(m *Memento) {
	o.state = m.state
}

// 19. Observer Pattern
type Observer interface {
	Update(string)
}

type Subject2 struct {
	observers []Observer
	state     string
}

func (s *Subject2) Attach(o Observer) {
	s.observers = append(s.observers, o)
}

func (s *Subject2) Notify() {
	for _, observer := range s.observers {
		observer.Update(s.state)
	}
}

// 20. State Pattern
type State interface {
	Handle() string
}

type Context struct {
	state State
}

type ConcreteStateA struct{}
type ConcreteStateB struct{}

func (s *ConcreteStateA) Handle() string { return "State A" }
func (s *ConcreteStateB) Handle() string { return "State B" }

// 21. Strategy Pattern
type Strategy interface {
	Execute() string
}

type ConcreteStrategyA struct{}
type ConcreteStrategyB struct{}

func (s *ConcreteStrategyA) Execute() string { return "Strategy A" }
func (s *ConcreteStrategyB) Execute() string { return "Strategy B" }

// 22. Template Method Pattern
type AbstractClass interface {
	TemplateMethod() string
	PrimitiveOperation1() string
	PrimitiveOperation2() string
}

type ConcreteClass struct {
	PrimitiveOperation2 func() string
}

type ConcreteOperation2 struct {
	//PrimitiveOperation2 func() string
}

func (c *ConcreteClass) TemplateMethod() string {
	return c.PrimitiveOperation1() + " " + c.PrimitiveOperation2()
}

func (c *ConcreteClass) PrimitiveOperation1() string      { return "Step 1" }
func (c *ConcreteOperation2) PrimitiveOperation2() string { return "Step 2" }

// 23. Visitor Pattern
type Visitor interface {
	VisitConcreteElementA(*ConcreteElementA)
	VisitConcreteElementB(*ConcreteElementB)
}

type Element interface {
	Accept(Visitor)
}

type ConcreteElementA struct{}
type ConcreteElementB struct{}

func (e *ConcreteElementA) Accept(v Visitor) { v.VisitConcreteElementA(e) }
func (e *ConcreteElementB) Accept(v Visitor) { v.VisitConcreteElementB(e) }

type ConcreteVisitor struct{}

func (v *ConcreteVisitor) VisitConcreteElementA(e *ConcreteElementA) {
	fmt.Println("Visited ConcreteElementA")
}

func (v *ConcreteVisitor) VisitConcreteElementB(e *ConcreteElementB) {
	fmt.Println("Visited ConcreteElementB")
}
