package util

import (
	"context"
	"io"
)

type handleState struct {
	handles []any
	holes   int
}

func (s *handleState) CloseNotify(ctx context.Context, exitCode uint32) {
	for _, h := range s.handles {
		if c, ok := h.(io.Closer); ok {
			c.Close()
		}
	}
	s.handles = nil
	s.holes = 0
}

func GetHandle(ctx context.Context, id uint32) any {
	if id == 0 {
		return nil
	}
	s := ctx.Value(moduleKey{}).(*moduleState)
	return s.handles[^id]
}

func DelHandle(ctx context.Context, id uint32) error {
	if id == 0 {
		return nil
	}
	s := ctx.Value(moduleKey{}).(*moduleState)
	a := s.handles[^id]
	s.handles[^id] = nil
	if l := uint32(len(s.handles)); l == ^id {
		s.handles = s.handles[:l-1]
	} else {
		s.holes++
	}
	if c, ok := a.(io.Closer); ok {
		return c.Close()
	}
	return nil
}

func AddHandle(ctx context.Context, a any) uint32 {
	if a == nil {
		panic(NilErr)
	}

	s := ctx.Value(moduleKey{}).(*moduleState)

	// Find an empty slot.
	if s.holes > cap(s.handles)-len(s.handles) {
		for id, h := range s.handles {
			if h == nil {
				s.holes--
				s.handles[id] = a
				return ^uint32(id)
			}
		}
	}

	// Add a new slot.
	s.handles = append(s.handles, a)
	return -uint32(len(s.handles))
}
