package CloudForest

import (
	"fmt"
	"io"
	"log"
	"math"
	"strconv"
	"strings"
	"sync"
)

func ParseFloat(s string) float64 {
	frac, _ := strconv.ParseFloat(s, 64)
	return frac

}

//RunningMean is a thread safe strut for keeping track of running means as used in
//importance calculations. (TODO: could this be made lock free?)
type RunningMean struct {
	mutex sync.Mutex
	Mean  float64
	Count float64
}

//Add add's 1.0 to the running mean in a thread safe way.
func (rm *RunningMean) Add(val float64) {
	rm.WeightedAdd(val, 1.0)
}

//WeightedAdd add's the specified value to the running mean in a thread safe way.
func (rm *RunningMean) WeightedAdd(val float64, weight float64) {
	if !math.IsNaN(val) && !math.IsNaN(weight) {
		rm.mutex.Lock()
		rm.Mean = (rm.Mean*rm.Count + weight*val) / (rm.Count + weight)
		rm.Count += weight
		if rm.Count == 0 {
			log.Print("WeightedAdd reached 0 count!.")
		}
		if math.IsNaN(rm.Mean) || math.IsNaN(rm.Count) {
			log.Print("Weighted add reached nan after adding ", val, weight)
		}

		rm.mutex.Unlock()
	}

}

//Read reads the mean and count
func (rm *RunningMean) Read() (mean float64, count float64) {
	rm.mutex.Lock()
	mean = rm.Mean
	count = rm.Count
	rm.mutex.Unlock()
	return
}

//NewRunningMeans returns an initalized *[]*RunningMean.
func NewRunningMeans(size int) *[]*RunningMean {
	importance := make([]*RunningMean, 0, size)
	for i := 0; i < size; i++ {
		rm := new(RunningMean)
		importance = append(importance, rm)
	}
	return &importance

}

//SparseCounter uses maps to track sparse integer counts in large matrix.
//The matrix is assumed to contain zero values where nothing has been added.
type SparseCounter struct {
	Map   map[int]map[int]int
	mutex sync.Mutex
}

//Add increases the count in i,j by val.
func (sc *SparseCounter) Add(i int, j int, val int) {
	sc.mutex.Lock()
	defer sc.mutex.Unlock()
	if sc.Map == nil {
		sc.Map = make(map[int]map[int]int, 0)
	}

	if v, ok := sc.Map[i]; !ok || v == nil {
		sc.Map[i] = make(map[int]int, 0)
	}
	if _, ok := sc.Map[i][j]; !ok {
		sc.Map[i][j] = 0
	}
	sc.Map[i][j] = sc.Map[i][j] + val

}

//WriteTsv writes the non zero counts out into a three column tsv containing i, j, and
//count in the columns.
func (sc *SparseCounter) WriteTsv(writer io.Writer) {
	sc.mutex.Lock()
	defer sc.mutex.Unlock()
	for i := range sc.Map {
		for j, val := range sc.Map[i] {
			if _, err := fmt.Fprintf(writer, "%v\t%v\t%v\n", i, j, val); err != nil {
				log.Println(err)
				return
			}
		}
	}

}

/*
ParseAsIntOrFractionOfTotal parses strings that may specify an count or a percent of
the total for use in specifying paramaters.
It parses term as a float if it contains a "." and as an int otherwise. If term is parsed
as a float frac it returns int(math.Ceil(frac * float64(total))).
It returns zero if term == "" or if a parsing error occures.
*/
func ParseAsIntOrFractionOfTotal(term string, total int) (parsed int) {
	if term == "" {
		return 0
	}

	if strings.Contains(term, ".") {
		frac, err := strconv.ParseFloat(term, 64)
		if err == nil {
			parsed = int(math.Ceil(frac * float64(total)))
		} else {
			parsed = 0
		}
	} else {
		count, err := strconv.ParseInt(term, 0, 0)
		if err != nil {
			parsed = 0
		} else {
			parsed = int(count)
		}

	}
	return
}