package icecast

import (
	"strings"
	"sync"
)

type RadioTextOptions struct {
	Enabled      bool
	Prefix       string
	MaxLen       int
	OnlyOnChange bool
}

func mapStreamTitleToRadioText(streamTitle string, opts RadioTextOptions) string {
	if !opts.Enabled {
		return ""
	}
	maxLen := opts.MaxLen
	if maxLen <= 0 || maxLen > 64 {
		maxLen = 64
	}
	title := sanitizeASCII(streamTitle)
	if title == "" {
		return ""
	}
	prefixRaw := opts.Prefix
	prefixHadTrailingSpace := strings.TrimRight(prefixRaw, " \t\r\n") != prefixRaw
	prefix := sanitizeASCII(opts.Prefix)
	if prefix != "" && prefixHadTrailingSpace {
		prefix += " "
	}
	rt := title
	if prefix != "" {
		rt = prefix + title
	}
	if len(rt) > maxLen {
		rt = strings.TrimSpace(rt[:maxLen])
	}
	return rt
}

func sanitizeASCII(raw string) string {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return ""
	}
	var b strings.Builder
	b.Grow(len(raw))
	prevSpace := true
	for _, r := range raw {
		switch r {
		case '\n', '\r', '\t':
			r = ' '
		}
		if r < 0x20 || r == 0x7f || r > 0x7e {
			continue
		}
		if r == ' ' {
			if prevSpace {
				continue
			}
			prevSpace = true
			b.WriteByte(' ')
			continue
		}
		prevSpace = false
		b.WriteByte(byte(r))
	}
	return strings.TrimSpace(b.String())
}

type RadioTextRelay struct {
	opts   RadioTextOptions
	apply  func(string) error
	mu     sync.Mutex
	lastRT string
}

func NewRadioTextRelay(opts RadioTextOptions, initialRT string, apply func(string) error) *RadioTextRelay {
	return &RadioTextRelay{
		opts:   opts,
		apply:  apply,
		lastRT: sanitizeASCII(initialRT),
	}
}

func (r *RadioTextRelay) HandleStreamTitle(streamTitle string) error {
	if r == nil || r.apply == nil {
		return nil
	}
	next := mapStreamTitleToRadioText(streamTitle, r.opts)
	if next == "" {
		return nil
	}
	r.mu.Lock()
	skip := r.opts.OnlyOnChange && next == r.lastRT
	if !skip {
		r.lastRT = next
	}
	r.mu.Unlock()
	if skip {
		return nil
	}
	return r.apply(next)
}
