package indicatorv2

import (
	"github.com/c9s/bbgo/pkg/types"
)

type BOLLStream struct {
	// the band series
	*types.Float64Series

	UpBand, DownBand *types.Float64Series

	window int
	k      float64

	SMA    *SMAStream
	StdDev *StdDevStream
}

// BOOL2 is bollinger indicator
// the data flow:
//
// priceSource ->
//
//	-> calculate SMA
//	-> calculate stdDev -> calculate bandWidth -> get latest SMA -> upBand, downBand
func BOLL(source types.Float64Source, window int, k float64) *BOLLStream {
	// bind these indicators before our main calculator
	sma := SMA(source, window)
	stdDev := StdDev(source, window)

	s := &BOLLStream{
		Float64Series: types.NewFloat64Series(),
		UpBand:        types.NewFloat64Series(),
		DownBand:      types.NewFloat64Series(),
		window:        window,
		k:             k,
		SMA:           sma,
		StdDev:        stdDev,
	}

	// on band update
	s.Float64Series.OnUpdate(func(band float64) {
		mid := s.SMA.Last(0)
		s.UpBand.PushAndEmit(mid + band)
		s.DownBand.PushAndEmit(mid - band)
	})

	s.Bind(source, s)
	return s
}

func (s *BOLLStream) Calculate(v float64) float64 {
	stdDev := s.StdDev.Last(0)
	band := stdDev * s.k
	return band
}

func (s *BOLLStream) Truncate() {
	s.Slice = generalTruncate(s.Slice)
	s.UpBand.Slice = generalTruncate(s.UpBand.Slice)
	s.DownBand.Slice = generalTruncate(s.DownBand.Slice)
}
