// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
// All rights reserved. Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package stats

import (
	"time"

	"github.com/syncthing/syncthing/protocol"
	"github.com/syndtr/goleveldb/leveldb"
)

const (
	nodeStatisticTypeLastSeen = iota
)

var nodeStatisticsTypes = []byte{
	nodeStatisticTypeLastSeen,
}

type NodeStatistics struct {
	LastSeen time.Time
}

type NodeStatisticsReference struct {
	db   *leveldb.DB
	node protocol.NodeID
}

func NewNodeStatisticsReference(db *leveldb.DB, node protocol.NodeID) *NodeStatisticsReference {
	return &NodeStatisticsReference{
		db:   db,
		node: node,
	}
}

func (s *NodeStatisticsReference) key(stat byte) []byte {
	k := make([]byte, 1+1+32)
	k[0] = keyTypeNodeStatistic
	k[1] = stat
	copy(k[1+1:], s.node[:])
	return k
}

func (s *NodeStatisticsReference) GetLastSeen() time.Time {
	value, err := s.db.Get(s.key(nodeStatisticTypeLastSeen), nil)
	if err != nil {
		if err != leveldb.ErrNotFound {
			l.Warnln("NodeStatisticsReference: Failed loading last seen value for", s.node, ":", err)
		}
		return time.Unix(0, 0)
	}

	rtime := time.Time{}
	err = rtime.UnmarshalBinary(value)
	if err != nil {
		l.Warnln("NodeStatisticsReference: Failed parsing last seen value for", s.node, ":", err)
		return time.Unix(0, 0)
	}
	if debug {
		l.Debugln("stats.NodeStatisticsReference.GetLastSeen:", s.node, rtime)
	}
	return rtime
}

func (s *NodeStatisticsReference) WasSeen() {
	if debug {
		l.Debugln("stats.NodeStatisticsReference.WasSeen:", s.node)
	}
	value, err := time.Now().MarshalBinary()
	if err != nil {
		l.Warnln("NodeStatisticsReference: Failed serializing last seen value for", s.node, ":", err)
		return
	}

	err = s.db.Put(s.key(nodeStatisticTypeLastSeen), value, nil)
	if err != nil {
		l.Warnln("Failed serializing last seen value for", s.node, ":", err)
	}
}

// Never called, maybe because it's worth while to keep the data
// or maybe because we have no easy way of knowing that a node has been removed.
func (s *NodeStatisticsReference) Delete() error {
	for _, stype := range nodeStatisticsTypes {
		err := s.db.Delete(s.key(stype), nil)
		if debug && err == nil {
			l.Debugln("stats.NodeStatisticsReference.Delete:", s.node, stype)
		}
		if err != nil && err != leveldb.ErrNotFound {
			return err
		}
	}
	return nil
}

func (s *NodeStatisticsReference) GetStatistics() NodeStatistics {
	return NodeStatistics{
		LastSeen: s.GetLastSeen(),
	}
}
