{{ define "handlers" }}
{{ $pkg := $.Package }}
{{ template "header" $ }}

type Handler = func(context.Context, Entities, UpdateClass) error

type UpdateDispatcher struct {
    handlers map[uint32]Handler
	fallback Handler
}

func NewUpdateDispatcher() UpdateDispatcher {
    return UpdateDispatcher{
        handlers: map[uint32]Handler{},
    }
}

type Entities struct {
	Short    bool
	Users    map[int64]*User
	Chats    map[int64]*Chat
	Channels map[int64]*Channel
}

func (u *Entities) short() {
	u.Short = true
	u.Users = make(map[int64]*User, 0)
	u.Chats = make(map[int64]*Chat, 0)
	u.Channels = make(map[int64]*Channel, 0)
}

// Handle implements UpdateDispatcher.
func (u UpdateDispatcher) Handle(ctx context.Context, updates UpdatesClass) error {
	var (
		e Entities
		upds []UpdateClass
	)
	switch u := updates.(type) {
	case *Updates:
		upds = u.Updates
		e.Users = u.MapUsers().NotEmptyToMap()
		chats := u.MapChats()
		e.Chats = chats.ChatToMap()
		e.Channels = chats.ChannelToMap()
	case *UpdatesCombined:
		upds = u.Updates
		e.Users = u.MapUsers().NotEmptyToMap()
		chats := u.MapChats()
		e.Chats = chats.ChatToMap()
		e.Channels = chats.ChannelToMap()
	case *UpdateShort:
		upds = []UpdateClass{u.Update}
		e.short()
	default:
		// *UpdateShortMessage
		// *UpdateShortChatMessage
		// *UpdateShortSentMessage
		// *UpdatesTooLong
		return nil
	}

	var err error
	for _, update := range upds {
		multierr.AppendInto(&err, u.dispatch(ctx, e, update))
	}
	return err
}

func (u UpdateDispatcher) dispatch(ctx context.Context, e Entities, update UpdateClass) error {
    if update == nil {
        return nil
    }
    typeID := update.TypeID()
    handler, ok := u.handlers[typeID]
	if ok {
		return handler(ctx, e, update)
	}
	if u.fallback != nil {
		return u.fallback(ctx, e, update)
	}
	return nil
}

{{- range $s := $.Structs }}{{ if eq $s.Interface "UpdateClass" }}
{{ $eventName := trimPrefix $s.Name "Update"}}
// {{ $eventName }}Handler is a {{ $eventName }} event handler.
type {{ $eventName }}Handler func(ctx context.Context, e Entities, update *{{ $s.Name }}) error

// On{{ $eventName }} sets {{ $eventName }} handler.
func (u UpdateDispatcher) On{{ $eventName }}(handler {{ $eventName }}Handler) {
    u.handlers[{{ $s.Name }}TypeID] = func(ctx context.Context, e Entities, update UpdateClass) error {
        return handler(ctx, e, update.(*{{ $s.Name }}))
    }
}
{{- end }}{{ end }}

// OnFallback sets fallback handler.
func (u *UpdateDispatcher) OnFallback(handler Handler) {
    u.fallback = handler
}

{{ end }}
