1""" The task of this module is to provide easy saving/loading of configurations
2    It also supports gconf like connection, so you get notices when a property
3    has changed. """
4import sys
5import os
6import atexit
7import builtins
8import locale
9from configparser import RawConfigParser
10
11from pychess import MSYS2
12from pychess.Utils.const import FISCHERRANDOMCHESS, LOSERSCHESS, COUNT_OF_SOUNDS, SOUND_MUTE
13from pychess.System.Log import log
14from pychess.System.prefix import addDataPrefix, addUserConfigPrefix, getDataPrefix
15
16section = "General"
17configParser = RawConfigParser(default_section=section)
18
19for sect in ("FICS", "ICC"):
20    if not configParser.has_section(sect):
21        configParser.add_section(sect)
22
23path = addUserConfigPrefix("config")
24encoding = locale.getpreferredencoding()
25if os.path.isfile(path):
26    with open(path, encoding=encoding) as fh:
27        configParser.read_file(fh)
28
29
30def save_config(path=path, encoding=encoding):
31    with open(path, "w", encoding=encoding) as fh:
32        configParser.write(fh)
33
34
35atexit.register(save_config)
36
37if sys.platform == "win32":
38    username = os.environ["USERNAME"]
39else:
40    from os import getuid
41    from pwd import getpwuid
42    userdata = getpwuid(getuid())
43    realname = userdata.pw_gecos.split(",")[0]
44    if realname:
45        username = realname
46    else:
47        username = userdata.pw_name
48
49if getattr(sys, 'frozen', False) and not MSYS2:
50    # pyinstaller specific!
51    if hasattr(sys, "_MEIPASS"):
52        base_path = sys._MEIPASS
53    else:
54        base_path = os.path.dirname(sys.executable)
55    default_book_path = os.path.join(base_path, "pychess_book.bin")
56else:
57    default_book_path = os.path.join(addDataPrefix("pychess_book.bin"))
58
59no_gettext = False
60
61
62idkeyfuncs = {}
63conid = 0
64
65
66def notify_add(key, func, *args, section=section):
67    """The signature for func must be self, client, *args, **kwargs"""
68    assert isinstance(key, str)
69    global conid
70    idkeyfuncs[conid] = (key, func, args, section)
71    conid += 1
72    return conid - 1
73
74
75def notify_remove(conid):
76    del idkeyfuncs[conid]
77
78
79if "_" not in builtins.__dir__():
80    def _(text):
81        return text
82
83
84DEFAULTS = {
85    "General": {
86        "firstName": username,
87        "secondName": _("Guest"),
88        "showEmt": False,
89        "showEval": False,
90        "showBlunder": True,
91        "hideTabs": False,
92        "closeAll": False,
93        "faceToFace": False,
94        "scoreLinearScale": False,
95        "showCaptured": False,
96        "figuresInNotation": False,
97        "moveAnimation": False,
98        "noAnimation": False,
99        "autoPromote": False,
100        "autoRotate": False,
101        "showFICSgameno": False,
102        "fullAnimation": True,
103        "showCords": True,
104        "drawGrid": True,
105        "activateSupportAlgorithm": False,
106        "board_frame": 1,
107        "board_style": 1,
108        "pieceTheme": "Chessicons",
109        "darkcolour": "",
110        "lightcolour": "",
111        "movetextFont": "FreeSerif Regular 12",
112        "autoSave": True,
113        "autoSavePath": os.path.expanduser("~"),
114        "autoSaveFormat": "pychess",
115        "saveEmt": False,
116        "saveEval": False,
117        "saveRatingChange": False,
118        "indentPgn": False,
119        "saveOwnGames": True,
120        "dont_show_externals_at_startup": False,
121        "max_analysis_spin": 3,
122        "variation_threshold_spin": 50,
123        "fromCurrent": True,
124        "shouldWhite": True,
125        "shouldBlack": True,
126        "ThreatPV": False,
127        "infinite_analysis": False,
128        "opening_check": False,
129        "opening_file_entry": default_book_path,
130        "book_depth_max": 13,
131        "book_exact_match": True,
132        "endgame_check": False,
133        "egtb_path": os.path.join(getDataPrefix()),
134        "online_egtb_check": True,
135        "autoCallFlag": True,
136        "hint_mode": False,
137        "spy_mode": False,
138        "ana_combobox": 0,
139        "analyzer_check": True,
140        "inv_ana_combobox": 0,
141        "inv_analyzer_check": False,
142        "newgametasker_playercombo": 0,
143        "ics_combo": 0,
144        "autoLogin": False,
145        "standard_toggle": True,
146        "blitz_toggle": True,
147        "lightning_toggle": True,
148        "variant_toggle": True,
149        "registered_toggle": True,
150        "guest_toggle": True,
151        "computer_toggle": True,
152        "titled_toggle": True,
153        "numberOfFingers": 0,
154        "numberOfTimesLoggedInAsRegisteredUser": 0,
155        "lastdifference-1": -1,
156        "lastdifference-2": -1,
157        "lastdifference-3": -1,
158        "standard_toggle1": True,
159        "blitz_toggle1": True,
160        "lightning_toggle1": True,
161        "variant_toggle1": True,
162        "computer_toggle1": True,
163        "categorycombo": 0,
164        "learncombo0": 0,
165        "learncombo1": 0,
166        "learncombo2": 0,
167        "learncombo3": 0,
168        "welcome_image": addDataPrefix("glade/background.jpg"),
169        "alarm_spin": 15,
170        "show_tip_at_startup": True,
171        "tips_seed": 0,
172        "tips_index": 0,
173        "dont_show_externals_at_startup": False,
174        "download_timestamp": False,
175        "download_chess_db": False,
176        "download_scoutfish": False,
177        "ngvariant1": FISCHERRANDOMCHESS,
178        "ngvariant2": LOSERSCHESS,
179        "useSounds": True,
180        "max_log_files": 10,
181        "show_sidepanels": True,
182        "chat_paned_position": 100,
183        "notimeRadio": 0,
184        "blitzRadio": 0,
185        "ngblitz min": 5,
186        "ngblitz gain": 0,
187        "ngblitz moves": 0,
188        "rapidRadio": 0,
189        "ngrapid min": 15,
190        "ngrapid gain": 5,
191        "ngrapid moves": 0,
192        "normalRadio": 0,
193        "ngnormal min": 45,
194        "ngnormal gain": 15,
195        "ngnormal moves": 0,
196        "classicalRadio": 0,
197        "playNormalRadio": 0,
198        "playVariant1Radio": 0,
199        "playVariant2Radio": 0,
200        "ngclassical min": 3,
201        "ngclassical gain": 0,
202        "ngclassical moves": 40,
203        "whitePlayerCombobox": 0,
204        "blackPlayerCombobox": 0,
205        "skillSlider1": 20,
206        "skillSlider2": 20,
207        "taskerSkillSlider": 20,
208        "seek1Radio": 0,
209        "seek2Radio": 0,
210        "seek3Radio": 0,
211        "challenge1Radio": 0,
212        "challenge2Radio": 0,
213        "challenge3Radio": 0,
214    },
215    "FICS": {
216        "timesealCheck": True,
217        "hostEntry": "freechess.org",
218        "usernameEntry": "",
219        "passwordEntry": "",
220        "asGuestCheck": True,
221    },
222    "ICC": {
223        "timesealCheck": True,
224        "hostEntry": "chessclub.com",
225        "usernameEntry": "",
226        "passwordEntry": "",
227        "asGuestCheck": True,
228    },
229}
230
231for i in range(COUNT_OF_SOUNDS):
232    DEFAULTS["General"]["soundcombo%d" % i] = SOUND_MUTE
233    DEFAULTS["General"]["sounduri%d" % i] = ""
234
235
236def get(key, section=section):
237    try:
238        default = DEFAULTS[section][key]
239    except KeyError:
240        # window attributes has no default values
241        # print("!!! conf get() KeyError: %s %s" % (section, key))
242        default = None
243
244    try:
245        value = configParser.getint(section, key, fallback=default)
246        # print("... conf get %s %s: %s" % (section, key, value))
247        return value
248    except ValueError:
249        pass
250
251    try:
252        value = configParser.getboolean(section, key, fallback=default)
253        # print("... conf get %s %s: %s" % (section, key, value))
254        return value
255    except ValueError:
256        pass
257
258    try:
259        value = configParser.getfloat(section, key, fallback=default)
260        # print("... conf get %s %s: %s" % (section, key, value))
261        return value
262    except ValueError:
263        pass
264
265    value = configParser.get(section, key, fallback=default)
266    # print("... conf get %s %s: %s" % (section, key, value))
267    return value
268
269
270def set(key, value, section=section):
271    # print("---conf set()", section, key, value)
272    try:
273        configParser.set(section, key, str(value))
274        with open(path, "w") as fh:
275            configParser.write(fh)
276    except Exception as err:
277        log.error(
278            "Unable to save configuration '%s'='%s' because of error: %s %s" %
279            (repr(key), repr(value), err.__class__.__name__, ", ".join(
280                str(a) for a in err.args)))
281    for key_, func, args, section_ in idkeyfuncs.values():
282        if key_ == key and section_ == section:
283            func(None, *args)
284
285
286def hasKey(key, section=section):
287    return configParser.has_option(section, key)
288