1// SPDX-License-Identifier: ISC
2// Copyright (c) 2014-2020 Bitmark Inc.
3// Use of this source code is governed by an ISC
4// license that can be found in the LICENSE file.
5
6package mode
7
8import (
9	"sync"
10
11	"github.com/bitmark-inc/bitmarkd/chain"
12	"github.com/bitmark-inc/bitmarkd/fault"
13	"github.com/bitmark-inc/logger"
14)
15
16// Mode - type to hold the mode
17type Mode int
18
19// all possible modes
20const (
21	Stopped Mode = iota
22	Resynchronise
23	Normal
24	maximum
25)
26
27var globalData struct {
28	sync.RWMutex
29	log     *logger.L
30	mode    Mode
31	testing bool
32	chain   string
33
34	// set once during initialise
35	initialised bool
36}
37
38// Initialise - set up the mode system
39func Initialise(chainName string) error {
40
41	// ensure start up in resynchronise mode
42	globalData.Lock()
43	defer globalData.Unlock()
44
45	// no need to start if already started
46	if globalData.initialised {
47		return fault.AlreadyInitialised
48	}
49
50	globalData.log = logger.New("mode")
51	globalData.log.Info("starting…")
52
53	// default settings
54	globalData.chain = chainName
55	globalData.testing = false
56	globalData.mode = Resynchronise
57
58	// override for specific chain
59	switch chainName {
60	case chain.Bitmark:
61		// no change
62	case chain.Testing, chain.Local:
63		globalData.testing = true
64	default:
65		globalData.log.Criticalf("mode cannot handle chain: '%s'", chainName)
66		return fault.InvalidChain
67	}
68
69	// all data initialised
70	globalData.initialised = true
71
72	return nil
73}
74
75// Finalise - shutdown mode handling
76func Finalise() error {
77
78	if !globalData.initialised {
79		return fault.NotInitialised
80	}
81
82	globalData.log.Info("shutting down…")
83	globalData.log.Flush()
84
85	Set(Stopped)
86
87	// finally...
88	globalData.initialised = false
89
90	globalData.log.Info("finished")
91	globalData.log.Flush()
92
93	return nil
94}
95
96// Set - change mode
97func Set(mode Mode) {
98
99	if mode >= Stopped && mode < maximum {
100		globalData.Lock()
101		globalData.mode = mode
102		globalData.Unlock()
103
104		globalData.log.Infof("set: %s", mode)
105	} else {
106		globalData.log.Errorf("ignore invalid set: %d", mode)
107	}
108}
109
110// Is - detect mode
111func Is(mode Mode) bool {
112	globalData.RLock()
113	defer globalData.RUnlock()
114	return mode == globalData.mode
115}
116
117// IsNot - detect mode
118func IsNot(mode Mode) bool {
119	globalData.RLock()
120	defer globalData.RUnlock()
121	return mode != globalData.mode
122}
123
124// IsTesting - special for testing
125func IsTesting() bool {
126	globalData.RLock()
127	defer globalData.RUnlock()
128	return globalData.testing
129}
130
131// ChainName - name of the current chain
132func ChainName() string {
133	globalData.RLock()
134	defer globalData.RUnlock()
135	return globalData.chain
136}
137
138// String - current mode represented as a string
139func String() string {
140	globalData.RLock()
141	defer globalData.RUnlock()
142	return globalData.mode.String()
143}
144
145// String - current mode represented as a string
146func (m Mode) String() string {
147	switch m {
148	case Stopped:
149		return "Stopped"
150	case Resynchronise:
151		return "Resynchronise"
152	case Normal:
153		return "Normal"
154	default:
155		return "*Unknown*"
156	}
157}
158