package pb

import "sort"

// ToBurndownSparseMatrix converts a rectangular integer matrix to the corresponding Protobuf object.
// It is specific to hercules.BurndownAnalysis.
func ToBurndownSparseMatrix(matrix [][]int64, name string) *BurndownSparseMatrix {
	if len(matrix) == 0 {
		panic("matrix may not be nil or empty")
	}
	r := BurndownSparseMatrix{
		Name:            name,
		NumberOfRows:    int32(len(matrix)),
		NumberOfColumns: int32(len(matrix[len(matrix)-1])),
		Rows:            make([]*BurndownSparseMatrixRow, len(matrix)),
	}
	for i, status := range matrix {
		nnz := make([]uint32, 0, len(status))
		changed := false
		for j := range status {
			v := status[len(status)-1-j]
			if v < 0 {
				v = 0
			}
			if !changed {
				changed = v != 0
			}
			if changed {
				nnz = append(nnz, uint32(v))
			}
		}
		r.Rows[i] = &BurndownSparseMatrixRow{
			Columns: make([]uint32, len(nnz)),
		}
		for j := range nnz {
			r.Rows[i].Columns[j] = nnz[len(nnz)-1-j]
		}
	}
	return &r
}

// DenseToCompressedSparseRowMatrix takes an integer matrix and converts it to a Protobuf CSR.
// CSR format: https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_.28CSR.2C_CRS_or_Yale_format.29
func DenseToCompressedSparseRowMatrix(matrix [][]int64) *CompressedSparseRowMatrix {
	r := CompressedSparseRowMatrix{
		NumberOfRows:    int32(len(matrix)),
		NumberOfColumns: int32(len(matrix[0])),
		Data:            make([]int64, 0),
		Indices:         make([]int32, 0),
		Indptr:          make([]int64, 1),
	}
	r.Indptr[0] = 0
	for _, row := range matrix {
		nnz := 0
		for x, col := range row {
			if col != 0 {
				r.Data = append(r.Data, col)
				r.Indices = append(r.Indices, int32(x))
				nnz++
			}
		}
		r.Indptr = append(r.Indptr, r.Indptr[len(r.Indptr)-1]+int64(nnz))
	}
	return &r
}

// MapToCompressedSparseRowMatrix takes an integer matrix and converts it to a Protobuf CSR.
// In contrast to DenseToCompressedSparseRowMatrix, a matrix here is already in DOK format.
// CSR format: https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_.28CSR.2C_CRS_or_Yale_format.29
func MapToCompressedSparseRowMatrix(matrix []map[int]int64) *CompressedSparseRowMatrix {
	r := CompressedSparseRowMatrix{
		NumberOfRows:    int32(len(matrix)),
		NumberOfColumns: int32(len(matrix)),
		Data:            make([]int64, 0),
		Indices:         make([]int32, 0),
		Indptr:          make([]int64, 1),
	}
	r.Indptr[0] = 0
	for _, row := range matrix {
		order := make([]int, len(row))
		i := 0
		for col := range row {
			order[i] = col
			i++
		}
		sort.Ints(order)
		for _, col := range order {
			val := row[col]
			r.Data = append(r.Data, val)
			r.Indices = append(r.Indices, int32(col))
		}
		r.Indptr = append(r.Indptr, r.Indptr[len(r.Indptr)-1]+int64(len(row)))
	}
	return &r
}
