// Copyright 2017 Pilosa Corp.
//
// 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 roaring

import (
	"strings"
)

// A Source represents the source a given bitmap gets its data from,
// such as a memory-mapped file. When combining bitmaps, we might
// track them together in a single combined-source of some sort.
type Source interface {
	ID() string
	Dead() bool
}

// MergeSources combines sources. If you have two bitmaps, and you're
// combining them, then the combination's source is a combination of
// those two sources.
func MergeSources(sources ...Source) Source {
	sourceCount := 0
	totalCount := 0
	var lastSource Source
	for _, s := range sources {
		if s == nil {
			continue
		}
		lastSource = s
		if s, ok := s.(combinedSource); ok {
			sourceCount++
			totalCount += len(s)
		} else {
			sourceCount++
			totalCount++
		}
	}
	// if there's no sources (this includes all sources being
	// empty combinedSources), we don't have a source.
	if totalCount == 0 {
		return nil
	}
	// if there's exactly one source, combined or otherwise, that's
	// fine, we'll just return it.
	if sourceCount == 1 {
		return lastSource
	}
	// make a new combinedSource, flattening any combinedSources
	// already present.
	newSources := make([]Source, 0, totalCount)
	for _, s := range sources {
		if s == nil {
			continue
		}
		if s, ok := s.(combinedSource); ok {
			newSources = append(newSources, s...)
		} else {
			newSources = append(newSources, s)
		}
	}
	return combinedSource(newSources)
}

// SetSource tells the bitmap what source to associate with new things it
// creates. This is possibly logically incorrect.
func (b *Bitmap) SetSource(s Source) {
	b.Source = s
}

type combinedSource []Source

func (c combinedSource) ID() string {
	ids := make([]string, len(c))
	for i := range c {
		ids[i] = c[i].ID()
	}
	return strings.Join(ids, ",")
}

func (c combinedSource) Dead() bool {
	for i := range c {
		if c[i].Dead() {
			return true
		}
	}
	return false
}
