// Copyright (C) 2016-2021  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 xneo provides base and common functionality for package neo.
package xneo

//go:generate gotrace gen .

import (
//	"sync"

	"lab.nexedi.com/kirr/go123/xnet"
	"lab.nexedi.com/kirr/neo/go/neo/proto"
)

// ClusterState represent state of a cluster.
type ClusterState struct {
	NodeTab *NodeTable         // information about nodes in the cluster
	PartTab *PartitionTable    // information about data distribution in the cluster
	Code    proto.ClusterState // master idea about cluster state
}

func (cs *ClusterState) IsOperational() bool {
	// XXX py client does not wait for cluster state==RUNNING
	return /* cs.Code == proto.ClusterRunning && */ cs.PartTab.OperationalWith(cs.NodeTab)
}

// ClusterStateSnapshot is snapshot of ClusterState.
// XXX place
type ClusterStateSnapshot struct {
	NodeTab proto.NotifyNodeInformation
	PartTab proto.SendPartitionTable
	Code    proto.ClusterState
}

func (cs *ClusterState) Snapshot() *ClusterStateSnapshot {
	S := &ClusterStateSnapshot{Code: cs.Code}

	nodev := cs.NodeTab.All()
	nodeiv := make([]proto.NodeInfo, len(nodev))
	for i, node := range nodev {
		// NOTE .NodeInfo is data not pointers - so won't change after we copy it to nodeiv
		nodeiv[i] = node.NodeInfo
	}
	S.NodeTab.IdTime = proto.IdTimeNone // XXX what here?
	S.NodeTab.NodeList = nodeiv

	S.PartTab = *cs.PartTab.ToMsg()

	return S
}


// Node provides base functionality underlying implementation of any NEO node.
//
// XXX vvv was moved to to _MasteredNode
// Every node knows how to talk to master and receives master idea about:
//
// - current node table       (all nodes in the cluster),
// - current partition table  (how data is split around storage nodes),
// - current cluster state.
type Node struct {
	MyInfo          proto.NodeInfo // type, laddr, nid, state, idtime
	ClusterName     string
	Net             xnet.Networker // network AP we are sending/receiving on
	MasterAddr      string         // address of current master  TODO -> masterRegistry
	MasterAddrSlice []string       // address of all known masters

	// XXX reconsider not using State and have just .NodeTab, .PartTab, .ClusterState
//	StateMu	sync.RWMutex	// <- XXX unexport ?   XXX not used -> move to MasteredNode ?
	State   ClusterState // nodeTab/partTab/stateCode	XXX unexport?
//	NodeTab		*NodeTable	   // information about nodes in the cluster
//	PartTab		*PartitionTable	   // information about data distribution in the cluster
//	ClusterState	proto.ClusterState // master idea about cluster state
}

// NewNode creates new node.
func NewNode(typ proto.NodeType, clusterName string, net xnet.Networker, masterAddrSlice []string) *Node {
	node := &Node{
		MyInfo:	proto.NodeInfo{
			Type:   typ,
			Addr:   proto.Address{},
			NID:    proto.NID(typ, 0), // temp, e.g. S?  TODO use "temp" bit in NodeID
			IdTime: proto.IdTimeNone,
		},
		ClusterName: clusterName,

		Net:             net,
		MasterAddrSlice: masterAddrSlice,

		State: ClusterState{
			NodeTab: &NodeTable{},
			PartTab: &PartitionTable{},
			Code:    -1, // invalid
		},
	}

	node.State.NodeTab.localNode = node
	return node
}
