// Copyright 2018 The CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package list

import (
	"fmt"

	"github.com/cockroachdb/apd/v3"

	"cuelang.org/go/internal"
)

// Avg returns the average value of a non empty list xs.
func Avg(xs []*internal.Decimal) (*internal.Decimal, error) {
	if len(xs) == 0 {
		return nil, fmt.Errorf("empty list")
	}

	s := apd.New(0, 0)
	for _, x := range xs {
		_, err := internal.BaseContext.Add(s, x, s)
		if err != nil {
			return nil, err
		}
	}

	var d apd.Decimal
	l := apd.New(int64(len(xs)), 0)
	_, err := internal.BaseContext.Quo(&d, s, l)
	if err != nil {
		return nil, err
	}
	return &d, nil
}

// Max returns the maximum value of a non empty list xs.
func Max(xs []*internal.Decimal) (*internal.Decimal, error) {
	if len(xs) == 0 {
		return nil, fmt.Errorf("empty list")
	}

	max := xs[0]
	for _, x := range xs[1:] {
		if max.Cmp(x) == -1 {
			max = x
		}
	}
	return max, nil
}

// Min returns the minimum value of a non empty list xs.
func Min(xs []*internal.Decimal) (*internal.Decimal, error) {
	if len(xs) == 0 {
		return nil, fmt.Errorf("empty list")
	}

	min := xs[0]
	for _, x := range xs[1:] {
		if min.Cmp(x) == +1 {
			min = x
		}
	}
	return min, nil
}

// Product returns the product of a non empty list xs.
func Product(xs []*internal.Decimal) (*internal.Decimal, error) {
	d := apd.New(1, 0)
	for _, x := range xs {
		_, err := internal.BaseContext.Mul(d, x, d)
		if err != nil {
			return nil, err
		}
	}
	return d, nil
}

// Range generates a list of numbers using a start value, a limit value, and a
// step value.
//
// For instance:
//
//	Range(0, 5, 2)
//
// results in
//
//	[0, 2, 4]
func Range(start, limit, step *internal.Decimal) ([]*internal.Decimal, error) {
	if step.IsZero() {
		return nil, fmt.Errorf("step must be non zero")
	}

	if !step.Negative && start.Cmp(limit) == +1 {
		return nil, fmt.Errorf("end must be greater than start when step is positive")
	}

	if step.Negative && start.Cmp(limit) == -1 {
		return nil, fmt.Errorf("end must be less than start when step is negative")
	}

	var vals []*internal.Decimal
	num := start
	for {
		if !step.Negative && num.Cmp(limit) != -1 {
			break
		}

		if step.Negative && num.Cmp(limit) != +1 {
			break
		}

		vals = append(vals, num)
		d := apd.New(0, 0)
		_, err := internal.BaseContext.Add(d, step, num)
		if err != nil {
			return nil, err
		}
		num = d
	}
	return vals, nil
}

// Sum returns the sum of a list non empty xs.
func Sum(xs []*internal.Decimal) (*internal.Decimal, error) {
	d := apd.New(0, 0)
	for _, x := range xs {
		_, err := internal.BaseContext.Add(d, x, d)
		if err != nil {
			return nil, err
		}
	}
	return d, nil
}
