package qb

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
)

type MetricType byte
type GroupBy byte

const (
	Cumulative    MetricType = 1
	Instant       MetricType = 2
	MaxFracDigits byte       = 7

	GroupByHour  GroupBy = 1
	GroupByDay   GroupBy = 2
	GroupByMonth GroupBy = 3

	AggregateMin byte = 1
	AggregateMax byte = 2
	AggregateAvg byte = 4
)

type TimestampCompressor interface {
	// (tmp, timestamp)
	Evaluate([]byte, uint32) TimeEvaluationReport
	// (rewindOffset, change, timestamp)
	Append(int, []byte, uint32)
	Size() int
	CommitedSize() int
	//DeleteLast()
	CaptureState()
	ForgetCapturedState()
	// (offset) => payload
	Tail(int) []byte
	CreateDecompressor() TimestampDecompressor
	//Payload() []byte // для снапшота
	//	Offset() int
	ReplaceBuffer([]byte)
	WriteCommitedTo(io.Writer) error
	LastTimestamp() uint32
	ReplaceSinceWithUntil() uint32
}

type TimeDeltaCapturedState struct {
	H            byte
	LastUnixtime uint32
	LastDelta    uint32
	Payload      []byte
}

type TimeEvaluationReport struct {
	TotalSpace   int
	Offset       int // from payload start
	RewindOffset int // steps to back from pos
	ChangeSize   int
}

type ValueCompressor interface {
	// (tmp, value)
	Evaluate([]byte, float64) ValueEvaluationReport
	// (rewindOffset, change, value, delta)
	Append(int, []byte, float64, uint64)
	Size() int
	CommitedSize() int
	//Chunks() [][]byte
	//DeleteLast()
	CaptureState()
	ForgetCapturedState()
	// (offset) => payload
	Tail(int) []byte
	// fracDigits
	CreateDecompressor(MetricType, byte) ValueDecompressor
	//Payload() []byte // для снапшота
	//Offset() int
	ReplaceBuffer([]byte)
	WriteCommitedTo(io.Writer) error
	LastValue() float64
}

type ValueDeltaCapturedState struct {
	H         byte
	LastDelta uint64
	Payload   []byte
}

type ValueEvaluationReport struct {
	TotalSpace   int
	Offset       int // from payload start
	RewindOffset int // steps to back from pos
	ChangeSize   int
	Delta        uint64
}

type TimestampDecompressor interface {
	NextValue() (uint32, bool)
}

type ValueDecompressor interface {
	NextValue() (float64, bool)
}

type AbortCode int

const (
	// Fatal errors
	WrongPrevPageNo               AbortCode = 1
	WriteToAtreeFailed            AbortCode = 2
	MaxAtreeSizeExceeded          AbortCode = 3
	FailedWriteToTxLog            AbortCode = 4
	ReferenceCountBug             AbortCode = 5
	WrongResultCodeBug            AbortCode = 6
	RemoveREDOFileFailed          AbortCode = 7
	FailedAtreeRequest            AbortCode = 8
	FailedGetPageNumber           AbortCode = 9
	UnknownTxLogRecordTypeBug     AbortCode = 11
	HasTimestampNoValueBug        AbortCode = 12
	NoMetricBug                   AbortCode = 13
	NoLockEntryBug                AbortCode = 14
	NoXLockBug                    AbortCode = 15
	MetricAddedBug                AbortCode = 16
	NoRLockBug                    AbortCode = 17
	XLockBug                      AbortCode = 18
	FailedFreeListSerialize       AbortCode = 19
	UnknownWorkerQueueItemBug     AbortCode = 20
	UnknownMetricWaitQueueItemBug AbortCode = 21
	RepeatableLock                AbortCode = 22
	NoSpaceOnIndexPage            AbortCode = 23

	WriteSnapshotFailed AbortCode = 24
	//
	GetRecoveryRecipeFailed      AbortCode = 26
	LoadSnapshotFailed           AbortCode = 27
	ReplayChangesFailed          AbortCode = 28
	CreateChangesWriterFailed    AbortCode = 29
	RemoveRecipeFileFailed       AbortCode = 30
	DumpSnapshotFailed           AbortCode = 31
	SearchREDOFilesFailed        AbortCode = 32
	ReplayREDOFileFailed         AbortCode = 33
	DeletePrevChangesFileFailed  AbortCode = 34
	DeletePrevSnapshotFileFailed AbortCode = 35

	MetricNotFoundDuringReplay AbortCode = 36

	WALReplayFailed AbortCode = 37
)

func Abort(code AbortCode, err error) {
	fmt.Println(err)
	os.Exit(int(code))
}

func GetIndexFilePath(dir string, databaseName string) string {
	return filepath.Join(dir, fmt.Sprintf("%s.index", databaseName))
}

func GetDataFilePath(dir string, databaseName string) string {
	return filepath.Join(dir, fmt.Sprintf("%s.data", databaseName))
}

func GetWALFilePath(dir string, databaseName string, snapshotNumber int) string {
	return filepath.Join(dir, fmt.Sprintf("%s.wal_%d", databaseName, snapshotNumber))
}

func GetSnapshotFilePath(dir string, databaseName string, snapshotNumber int) string {
	return filepath.Join(dir, fmt.Sprintf("%s.snapshot_%d", databaseName, snapshotNumber))
}

func GetIndexFreeListFilePath(dir string, databaseName string) string {
	return filepath.Join(dir, fmt.Sprintf("%s.index_free", databaseName))
}

func GetIndexFreeListDeltaFilePath(dir string, databaseName string) string {
	return filepath.Join(dir, fmt.Sprintf("%s.index_free_delta", databaseName))
}

func GetDataFreeListFilePath(dir string, databaseName string) string {
	return filepath.Join(dir, fmt.Sprintf("%s.data_free", databaseName))
}

func GetDataFreeListDeltaFilePath(dir string, databaseName string) string {
	return filepath.Join(dir, fmt.Sprintf("%s.data_free_delta", databaseName))
}
