func (sis *SegmentInfos) remove(si *SegmentCommitInfo) {
	panic("not implemented yet")
}
/*
Remove the provided SegmentCommitInfo.

WARNING: O(N) cost
package index
import (
	"bytes"
	"errors"
	"fmt"
	"github.com/balzaczyy/golucene/core/codec"
	. "github.com/balzaczyy/golucene/core/codec/spi"
	. "github.com/balzaczyy/golucene/core/index/model"
	"github.com/balzaczyy/golucene/core/store"
	"github.com/balzaczyy/golucene/core/util"
	"strconv"
	"strings"
)
func message(format string, args ...interface{}) {
	fmt.Printf("SIS: %v\n", fmt.Sprintf(format, args...))
}
type FindSegmentsFile struct {
	directory                store.Directory
	doBody                   func(segmentFileName string) (interface{}, error)
	defaultGenLookaheadCount int
}
func NewFindSegmentsFile(directory store.Directory,
	doBody func(segmentFileName string) (interface{}, error)) *FindSegmentsFile {
	return &FindSegmentsFile{directory, doBody, 10}
}
func (fsf *FindSegmentsFile) run(commit IndexCommit) (interface{}, error) {
	if commit != nil {
		if fsf.directory != commit.Directory() {
			return nil, errors.New("the specified commit does not match the specified Directory")
		}
		return fsf.doBody(commit.SegmentsFileName())
	}
	lastGen := int64(-1)
	gen := int64(0)
	genLookaheadCount := 0
	var exc error
	retryCount := 0
	useFirstMethod := true
	for {
		if useFirstMethod {
			genA := int64(-1)
			files, err := fsf.directory.ListAll()
			if err != nil {
				return nil, err
			}
			if files != nil {
				genA = LastCommitGeneration(files)
			}
			genB := int64(-1)
			genInput, err := fsf.directory.OpenChecksumInput(INDEX_FILENAME_SEGMENTS_GEN, store.IO_CONTEXT_READ)
			if err != nil {
				message("segments.gen open: %v", err)
			} else {
				defer genInput.Close()
				var version int32
				if version, err = genInput.ReadInt(); err != nil {
					return nil, err
				}
				if version == FORMAT_SEGMENTS_GEN_47 || version == FORMAT_SEGMENTS_GEN_CURRENT {
					var gen0, gen1 int64
					if gen0, err = genInput.ReadLong(); err != nil {
						return nil, err
					}
					if gen1, err = genInput.ReadLong(); err != nil {
						return nil, err
					}
					message("fallback check: %v; %v", gen0, gen1)
					if version == FORMAT_SEGMENTS_GEN_CHECKSUM {
						if _, err = codec.CheckFooter(genInput); err != nil {
							return nil, err
						}
					} else {
						if err = codec.CheckEOF(genInput); err != nil {
							return nil, err
						}
					}
					if gen0 == gen1 {
						genB = gen0
					}
				} else {
					return nil, codec.NewIndexFormatTooNewError(genInput, version,
						FORMAT_SEGMENTS_GEN_CURRENT, FORMAT_SEGMENTS_GEN_CURRENT)
				}
			}
			message("%v check: genB=%v", INDEX_FILENAME_SEGMENTS_GEN, genB)
			gen = genA
			if genB > gen {
				gen = genB
			}
			if gen == -1 {
				return nil, errors.New(fmt.Sprintf("no segments* file found in %v: files: %#v", fsf.directory, files))
			}
		}
		if useFirstMethod && lastGen == gen && retryCount >= 2 {
			useFirstMethod = false
		}
		if !useFirstMethod {
			if genLookaheadCount < fsf.defaultGenLookaheadCount {
				gen++
				genLookaheadCount++
				message("look ahead increment gen to %v", gen)
			} else {
				return nil, exc
			}
		} else if lastGen == gen {
			retryCount++
		} else {
			retryCount = 0
		}
		lastGen = gen
		segmentFileName := util.FileNameFromGeneration(INDEX_FILENAME_SEGMENTS, "", gen)
		var v interface{}
		var err error
		if v, err = fsf.doBody(segmentFileName); err == nil {
			message("success on %v", segmentFileName)
			return v, nil
		}
		if exc == nil {
			exc = err
		}
		message("primary Exception on '%v': %v; will retry: retryCount = %v; gen = %v",
			segmentFileName, err, retryCount, gen)
		if gen > 1 && useFirstMethod && retryCount == 1 {
			prevSegmentFileName := util.FileNameFromGeneration(INDEX_FILENAME_SEGMENTS, "", gen-1)
			if prevExists := fsf.directory.FileExists(prevSegmentFileName); prevExists {
				message("fallback to prior segment file '%v'", prevSegmentFileName)
				if v, err = fsf.doBody(prevSegmentFileName); err != nil {
					message("secondary Exception on '%v': %v; will retry", prevSegmentFileName, err)
				} else {
					message("success on fallback %v", prevSegmentFileName)
					return v, nil
				}
			}
		}
	}
}
const (
	VERSION_40 = 0
	VERSION_46 = 1
	VERSION_48 = 2
	VERSION_49 = 3
	FORMAT_SEGMENTS_GEN_47       = -2
	FORMAT_SEGMENTS_GEN_CHECKSUM = -3
	FORMAT_SEGMENTS_GEN_START    = FORMAT_SEGMENTS_GEN_47
	FORMAT_SEGMENTS_GEN_CURRENT = FORMAT_SEGMENTS_GEN_CHECKSUM
)
type SegmentInfos struct {
	counter        int
	version        int64
	generation     int64
	lastGeneration int64
	userData       map[string]string
	Segments       []*SegmentCommitInfo
	pendingSegnOutput store.IndexOutput
}
func LastCommitGeneration(files []string) int64 {
	if files == nil {
		return int64(-1)
	}
	max := int64(-1)
	for _, file := range files {
		if strings.HasPrefix(file, INDEX_FILENAME_SEGMENTS) && file != INDEX_FILENAME_SEGMENTS_GEN {
			gen := GenerationFromSegmentsFileName(file)
			if gen > max {
				max = gen
			}
		}
	}
	return max
}
func (sis *SegmentInfos) SegmentsFileName() string {
	return util.FileNameFromGeneration(util.SEGMENTS, "", sis.lastGeneration)
}
func GenerationFromSegmentsFileName(fileName string) int64 {
	switch {
	case fileName == INDEX_FILENAME_SEGMENTS:
		return int64(0)
	case strings.HasPrefix(fileName, INDEX_FILENAME_SEGMENTS):
		d, err := strconv.ParseInt(fileName[1+len(INDEX_FILENAME_SEGMENTS):], 36, 64)
		if err != nil {
			panic(err)
		}
		return d
	default:
		panic(fmt.Sprintf("filename %v is not a segments file", fileName))
	}
}
func writeSegmentsGen(dir store.Directory, generation int64) {
	if err := func() (err error) {
		var genOutput store.IndexOutput
		genOutput, err = dir.CreateOutput(INDEX_FILENAME_SEGMENTS_GEN, store.IO_CONTEXT_READONCE)
		if err != nil {
			return err
		}
		defer func() {
			err = mergeError(err, genOutput.Close())
			err = mergeError(err, dir.Sync([]string{INDEX_FILENAME_SEGMENTS_GEN}))
		}()
		if err = genOutput.WriteInt(FORMAT_SEGMENTS_GEN_CURRENT); err == nil {
			if err = genOutput.WriteLong(generation); err == nil {
				if err = genOutput.WriteLong(generation); err == nil {
					err = codec.WriteFooter(genOutput)
				}
			}
		}
		return err
	}(); err != nil {
		dir.DeleteFile(INDEX_FILENAME_SEGMENTS_GEN)
	}
}
func (sis *SegmentInfos) nextSegmentFilename() string {
	var nextGeneration int64
	if sis.generation == -1 {
		nextGeneration = 1
	} else {
		nextGeneration = sis.generation + 1
	}
	return util.FileNameFromGeneration(util.SEGMENTS, "", nextGeneration)
}
func (sis *SegmentInfos) Read(directory store.Directory, segmentFileName string) (err error) {
	sis.Clear()
	sis.generation = GenerationFromSegmentsFileName(segmentFileName)
	sis.lastGeneration = sis.generation
	var input store.ChecksumIndexInput
	if input, err = directory.OpenChecksumInput(segmentFileName, store.IO_CONTEXT_READ); err != nil {
		return
	}
	var success = false
	defer func() {
		if !success {
			sis.Clear()
			util.CloseWhileSuppressingError(input)
		} else {
			err = input.Close()
		}
	}()
	var format int
	if format, err = asInt(input.ReadInt()); err != nil {
		return
	}
	var actualFormat int
	if format == codec.CODEC_MAGIC {
		if actualFormat, err = asInt(codec.CheckHeaderNoMagic(input, "segments", VERSION_40, VERSION_49)); err != nil {
			return
		}
		if sis.version, err = input.ReadLong(); err != nil {
			return
		}
		if sis.counter, err = asInt(input.ReadInt()); err != nil {
			return
		}
		var numSegments int
		if numSegments, err = asInt(input.ReadInt()); err != nil {
			return
		} else if numSegments < 0 {
			return errors.New(fmt.Sprintf("invalid segment count: %v (resource: %v)", numSegments, input))
		}
		var segName, codecName string
		var fCodec Codec
		var delGen, fieldInfosGen, dvGen int64
		var delCount int
		for seg := 0; seg < numSegments; seg++ {
			if segName, err = input.ReadString(); err != nil {
				return
			}
			if codecName, err = input.ReadString(); err != nil {
				return
			}
			fCodec = LoadCodec(codecName)
			assert2(fCodec != nil, "Invalid codec name: %v", codecName)
			var info *SegmentInfo
			if info, err = fCodec.SegmentInfoFormat().SegmentInfoReader().Read(directory, segName, store.IO_CONTEXT_READ); err != nil {
				return
			}
			info.SetCodec(fCodec)
			if delGen, err = input.ReadLong(); err != nil {
				return
			}
			if delCount, err = asInt(input.ReadInt()); err != nil {
				return
			} else if delCount < 0 || delCount > info.DocCount() {
				return errors.New(fmt.Sprintf(
					"invalid deletion count: %v vs docCount=%v (resource: %v)",
					delCount, info.DocCount(), input))
			}
			fieldInfosGen = -1
			if actualFormat >= VERSION_46 {
				if fieldInfosGen, err = input.ReadLong(); err != nil {
					return
				}
			}
			dvGen = -1
			if actualFormat >= VERSION_49 {
				if dvGen, err = input.ReadLong(); err != nil {
					return
				}
			} else {
				dvGen = fieldInfosGen
			}
			siPerCommit := NewSegmentCommitInfo(info, delCount, delGen, fieldInfosGen, dvGen)
			if actualFormat >= VERSION_46 {
				if actualFormat < VERSION_49 {
					panic("not implemented yet")
				} else {
					var ss map[string]bool
					if ss, err = input.ReadStringSet(); err != nil {
						return err
					}
					siPerCommit.SetFieldInfosFiles(ss)
					var dvUpdatesFiles map[int]map[string]bool
					var numDVFields int
					if numDVFields, err = asInt(input.ReadInt()); err != nil {
						return err
					}
					if numDVFields == 0 {
						dvUpdatesFiles = make(map[int]map[string]bool)
					} else {
						panic("not implemented yet")
					}
					siPerCommit.SetDocValuesUpdatesFiles(dvUpdatesFiles)
				}
			}
			sis.Segments = append(sis.Segments, siPerCommit)
		}
		if sis.userData, err = input.ReadStringStringMap(); err != nil {
			return err
		}
	} else {
		panic("Index format pre-4.0 not supported yet")
	}
	if actualFormat >= VERSION_48 {
		if _, err = codec.CheckFooter(input); err != nil {
			return
		}
	} else {
		var checksumNow = int64(input.Checksum())
		var checksumThen int64
		if checksumThen, err = input.ReadLong(); err != nil {
			return
		}
		if checksumNow != checksumThen {
			return errors.New(fmt.Sprintf(
				"checksum mismatch in segments file: %v vs %v (resource: %v)",
				checksumNow, checksumThen, input))
		}
		if err = codec.CheckEOF(input); err != nil {
			return
		}
	}
	success = true
	return nil
}
func asInt(n int32, err error) (int, error) {
	if err != nil {
		return 0, err
	}
	return int(n), nil
}
func (sis *SegmentInfos) ReadAll(directory store.Directory) error {
	sis.generation, sis.lastGeneration = -1, -1
	_, err := NewFindSegmentsFile(directory, func(segmentFileName string) (obj interface{}, err error) {
		err = sis.Read(directory, segmentFileName)
		return nil, err
	}).run(nil)
	return err
}
func (sis *SegmentInfos) write(directory store.Directory) (err error) {
	segmentsFilename := sis.nextSegmentFilename()
	if sis.generation == -1 {
		sis.generation = 1
	} else {
		sis.generation++
	}
	var segnOutput store.IndexOutput
	var success = false
	defer func() {
		if !success {
			util.CloseWhileSuppressingError(segnOutput)
			directory.DeleteFile(segmentsFilename)  
		}
	}()
	if segnOutput, err = directory.CreateOutput(segmentsFilename, store.IO_CONTEXT_DEFAULT); err != nil {
		return
	}
	if err = codec.WriteHeader(segnOutput, "segments", VERSION_49); err != nil {
		return
	}
	if err = segnOutput.WriteLong(sis.version); err == nil {
		if err = segnOutput.WriteInt(int32(sis.counter)); err == nil {
			err = segnOutput.WriteInt(int32(len(sis.Segments)))
		}
	}
	if err != nil {
		return
	}
	for _, siPerCommit := range sis.Segments {
		si := siPerCommit.Info
		if err = segnOutput.WriteString(si.Name); err == nil {
			if err = segnOutput.WriteString(si.Codec().(Codec).Name()); err == nil {
				if err = segnOutput.WriteLong(siPerCommit.DelGen()); err == nil {
					assert2(siPerCommit.DelCount() >= 0 && siPerCommit.DelCount() <= si.DocCount(),
						"cannot write segment: invalid docCount segment=%v docCount=%v delCount=%v",
						si.Name, si.DocCount(), siPerCommit.DelCount())
					if err = segnOutput.WriteInt(int32(siPerCommit.DelCount())); err == nil {
						if err = segnOutput.WriteLong(siPerCommit.FieldInfosGen()); err == nil {
							if err = segnOutput.WriteLong(siPerCommit.DocValuesGen()); err == nil {
								if err = segnOutput.WriteStringSet(siPerCommit.FieldInfosFiles()); err == nil {
									dvUpdatesFiles := siPerCommit.DocValuesUpdatesFiles()
									if err = segnOutput.WriteInt(int32(len(dvUpdatesFiles))); err == nil {
										for k, v := range dvUpdatesFiles {
											if err = segnOutput.WriteInt(int32(k)); err != nil {
												break
											}
											if err = segnOutput.WriteStringSet(v); err != nil {
												break
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		if err != nil {
			return
		}
		assert(si.Dir == directory)
		if version := si.Version(); len(version) == 0 || !version.OnOrAfter(util.VERSION_4_0) {
			panic("not implemented yet")
		}
	}
	if err = segnOutput.WriteStringStringMap(sis.userData); err != nil {
		return
	}
	sis.pendingSegnOutput = segnOutput
	success = true
	return nil
}
func (sis *SegmentInfos) Clone() *SegmentInfos {
	return sis.clone(false)
}
func (sis *SegmentInfos) clone(cloneSegmentInfo bool) *SegmentInfos {
	clone := &SegmentInfos{
		counter:        sis.counter,
		version:        sis.version,
		generation:     sis.generation,
		lastGeneration: sis.lastGeneration,
		userData:       make(map[string]string),
		Segments:       nil,
	}
	for _, info := range sis.Segments {
		assert(info.Info.Codec() != nil)
		clone.Segments = append(clone.Segments, info.CloneDeep(cloneSegmentInfo))
	}
	for k, v := range sis.userData {
		clone.userData[k] = v
	}
	return clone
}
func (sis *SegmentInfos) updateGeneration(other *SegmentInfos) {
	sis.lastGeneration = other.lastGeneration
	sis.generation = other.generation
}
func (sis *SegmentInfos) rollbackCommit(dir store.Directory) {
	if sis.pendingSegnOutput != nil {
		util.CloseWhileSuppressingError(sis.pendingSegnOutput)
		sis.pendingSegnOutput = nil
		segmentFilename := util.FileNameFromGeneration(INDEX_FILENAME_SEGMENTS, "", sis.generation)
		util.DeleteFilesIgnoringErrors(dir, segmentFilename)
	}
}
func (sis *SegmentInfos) toString(dir store.Directory) string {
	var buffer bytes.Buffer
	buffer.WriteString(sis.SegmentsFileName())
	buffer.WriteString(":")
	for _, info := range sis.Segments {
		buffer.WriteString(info.StringOf(dir, 0))
	}
	return buffer.String()
}
func (sis *SegmentInfos) prepareCommit(dir store.Directory) error {
	assert2(sis.pendingSegnOutput == nil, "prepareCommit was already called")
	return sis.write(dir)
}
func (sis *SegmentInfos) files(dir store.Directory, includeSegmentsFile bool) []string {
	files := make(map[string]bool)
	if includeSegmentsFile {
		if segmentFileName := sis.SegmentsFileName(); segmentFileName != "" {
			files[segmentFileName] = true
		}
	}
	for _, info := range sis.Segments {
		assert(info.Info.Dir == dir)
		for _, file := range info.Files() {
			files[file] = true
		}
	}
	var res = make([]string, 0, len(files))
	for file, _ := range files {
		res = append(res, file)
	}
	return res
}
func (sis *SegmentInfos) finishCommit(dir store.Directory) (fileName string, err error) {
	assert(dir != nil)
	assert2(sis.pendingSegnOutput != nil, "prepareCommit was not called")
	if err = func() error {
		var success = false
		defer func() {
			if !success {
				sis.rollbackCommit(dir)
			} else {
				err := func() error {
					var success = false
					defer func() {
						if !success {
							sis.rollbackCommit(dir)
						} else {
							sis.pendingSegnOutput = nil
						}
					}()
					err := sis.pendingSegnOutput.Close()
					success = err == nil
					return err
				}()
				assertn(err == nil, "%v", err)  
			}
		}()
		if err := codec.WriteFooter(sis.pendingSegnOutput); err != nil {
			return err
		}
		success = true
		return nil
	}(); err != nil {
		return
	}
	fileName = util.FileNameFromGeneration(INDEX_FILENAME_SEGMENTS, "", sis.generation)
	if err = func() error {
		var success = false
		defer func() {
			if !success {
				dir.DeleteFile(fileName)
			}
		}()
		err := dir.Sync([]string{fileName})
		success = err == nil
		return err
	}(); err != nil {
		return
	}
	sis.lastGeneration = sis.generation
	writeSegmentsGen(dir, sis.generation)
	return
}
func (sis *SegmentInfos) replace(other *SegmentInfos) {
	sis.rollbackSegmentInfos(other.Segments)
	sis.lastGeneration = other.lastGeneration
}
func (sis *SegmentInfos) changed() {
	sis.version++
}
func (sis *SegmentInfos) createBackupSegmentInfos() []*SegmentCommitInfo {
	ans := make([]*SegmentCommitInfo, len(sis.Segments))
	for i, info := range sis.Segments {
		assert(info.Info.Codec() != nil)
		ans[i] = info.Clone()
	}
	return ans
}
func (sis *SegmentInfos) rollbackSegmentInfos(infos []*SegmentCommitInfo) {
	if cap(sis.Segments) < len(infos) {
		sis.Segments = make([]*SegmentCommitInfo, len(infos))
		copy(sis.Segments, infos)
	} else {
		n := len(sis.Segments)
		copy(sis.Segments, infos)
		for i, limit := len(infos), n; i < limit; i++ {
			sis.Segments[i] = nil
		}
		sis.Segments = sis.Segments[0:len(infos)]
	}
}
func (sis *SegmentInfos) Clear() {
	for i, _ := range sis.Segments {
		sis.Segments[i] = nil
	}
	sis.Segments = sis.Segments[:0]  
}