// Copyright (C) 2019  Nexedi SA and Contributors.
//                     Kirill Smelkov <kirr@nexedi.com>
//
// This program is free software: you can Use, Study, Modify and Redistribute
// it under the terms of the GNU General Public License version 3, or (at your
// option) any later version, as published by the Free Software Foundation.
//
// You can also Link and Combine this program with other software covered by
// the terms of any of the Free Software licenses or any of the Open Source
// Initiative approved licenses and Convey the resulting work. Corresponding
// source of such a combination shall include the source code for all other
// software used.
//
// This program is distributed WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// See COPYING file for full licensing terms.
// See https://www.nexedi.com/licensing for rationale and options.

package main
// ΔFtail - merge btree.ΔTail with history of ZBlk

import (
	"lab.nexedi.com/kirr/neo/go/zodb"
	"lab.nexedi.com/kirr/neo/go/zodb/btree"

	"./internal/xbtree"
)

// ΔFTail is like btree.ΔTail but additionally tracks tree-root -> file relation and
// takes ZBlk history into account.
type ΔFTail struct {
	*xbtree.ΔTail
	fileIdx map[*btree.LOBTree]SetBigFile	// root -> {} BigFile	XXX root -> oid?
}

type ΔFentry struct {
	Rev     zodb.Tid
	Changev []ΔFile
}

type ΔFile struct {
	File *BigFile
	Blkv []int64
}

func NewΔFTail(at0 zodb.Tid) *ΔFTail {
	return &ΔFTail{
		ΔTail:   xbtree.NewΔTail(at0),
		fileIdx: make(map[*btree.LOBTree]SetBigFile),
	}
}

// Track adds tree path to tracked set and associates path root with file.
//
// A root can be associated with several files (each provided on different Track call).
func (δf *ΔFTail) Track(file *BigFile, path []btree.LONode) {
	δf.ΔTail.Track(path)
	root := path[0].(*btree.LOBTree)
	files, ok := δf.fileIdx[root]
	if !ok {
		files = SetBigFile{}
		δf.fileIdx[root] = files
	}
	files.Add(file)
}

// XXX
func (δf *ΔFTail) Update(δZ *zodb.EventCommit) ΔFentry {
	δB := δf.ΔTail.Update(δZ)
	var changev []ΔFile // δB.Changev root -> file (via .fileIdx)
	for _, δ := range δB.Changev {
		files := δf.fileIdx[δ.Root]
		if len(files) == 0 {
			panicf("ΔFTail: root<%s> -> ø file", δ.Root.POid())
		}
		for file := range files {
			changev = append(changev, ΔFile{
				File: file,
				Blkv: δ.Keyv,
			})
		}
	}

	return ΔFentry{
		Rev:     δB.Rev,
		Changev: changev,
	}
}

// LastRevOf return last_rev file[blk] @at	XXX
//
// XXX -> merge into ZBigFile.LoadBlk?
// XXX ZBigFile.LoadBlk(blk) -> blkdata, rev
//
// XXX no - cannot merge - f.LastBlkRev is used for both @head (where we have
// zblk.rev) and for @w.at, where we don't have zblk.rev because we was not
// handling load and just need to pin watcher with the result.
func (δf *ΔFTail) LastRevOf(file *BigFile, blk int64, at zodb.Tid) (_ zodb.Tid, exact bool) {
	// revision of when blktab[blk] entry changed last.
	treeKeyRev, exact := δf.ΔTail.LastRevOf(file.zfile.blktab, blk, at)	// XXX activate?

	// blktab[blk] is only a pointer (to ZBlk) and ZBlk could itself have
	// been changing after treeKeyRev. We have to check for that.
	panic("TODO")
}