1#!/usr/bin/env python
2# -*- mode: python; coding: utf-8; -*-
3# ---------------------------------------------------------------------------##
4#
5#  Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
6#  Copyright (C) 2003 Mt. Hood Playing Card Co.
7#  Copyright (C) 2005-2009 Skomoroh
8#
9#  This program is free software: you can redistribute it and/or modify
10#  it under the terms of the GNU General Public License as published by
11#  the Free Software Foundation, either version 3 of the License, or
12#  (at your option) any later version.
13#
14#  This program is distributed in the hope that it will be useful,
15#  but WITHOUT ANY WARRANTY; without even the implied warranty of
16#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17#  GNU General Public License for more details.
18#
19#  You should have received a copy of the GNU General Public License
20#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21#
22# ---------------------------------------------------------------------------##
23
24import locale
25import os
26import re
27import sys
28import time
29import webbrowser
30from pickle import Pickler, Unpickler
31
32from pysollib.settings import PACKAGE, TOOLKIT
33
34import six
35from six import print_
36
37Image = ImageTk = ImageOps = None
38if TOOLKIT == 'tk':
39    try:  # PIL
40        from PIL import Image
41        from PIL import ImageTk  # noqa: F401
42        from PIL import ImageOps  # noqa: F401
43    except ImportError:
44        Image = None
45    else:
46        # for py2exe
47        from PIL import GifImagePlugin  # noqa: F401
48        from PIL import PngImagePlugin  # noqa: F401
49        from PIL import JpegImagePlugin  # noqa: F401
50        from PIL import BmpImagePlugin  # noqa: F401
51        from PIL import PpmImagePlugin  # noqa: F401
52        Image._initialized = 2
53USE_PIL = False
54if TOOLKIT == 'tk' and Image:
55    USE_PIL = True
56
57# debug
58# Image = None
59# USE_PIL = False
60
61# ************************************************************************
62# * exceptions
63# ************************************************************************
64
65
66class SubclassResponsibility(Exception):
67    pass
68
69
70# ************************************************************************
71# * misc. util
72# ************************************************************************
73
74
75def latin1_to_ascii(n):
76    if sys.version_info > (3,):
77        return n
78    # return n
79    n = n.encode('iso8859-1', 'replace')
80    # FIXME: rewrite this for better speed
81    return (n.replace("\xc4", "Ae")
82             .replace("\xd6", "Oe")
83             .replace("\xdc", "Ue")
84             .replace("\xe4", "ae")
85             .replace("\xf6", "oe")
86             .replace("\xfc", "ue"))
87
88
89def latin1_normalize(n):
90    normal = re.sub(r"[^\w]", "", latin1_to_ascii(n).lower())
91    # Some game names end in a +, and would have the same normalized
92    # name as their counterpart.  This is a failsafe to avoid duplicate
93    # name conflicts.  Though there is probably a better way to do this.
94    if n.endswith("+"):
95        normal += "plus"
96    return normal
97
98
99def format_time(t):
100    # print 'format_time:', t
101    if t <= 0:
102        return "0:00"
103    if t < 3600:
104        return "%d:%02d" % (t // 60, t % 60)
105    return "%d:%02d:%02d" % (t // 3600, (t % 3600) // 60, t % 60)
106
107
108def print_err(s, level=1):
109    if level == 0:
110        ss = PACKAGE+': ERROR:'
111    elif level == 1:
112        ss = PACKAGE+': WARNING:'
113    elif level == 2:
114        ss = PACKAGE+': DEBUG WARNING:'
115    try:
116        print_(ss, s, file=sys.stderr)
117    except Exception:
118        print_(ss, s.encode(locale.getpreferredencoding()), file=sys.stderr)
119    sys.stderr.flush()
120
121
122# ************************************************************************
123# * misc. portab stuff
124# ************************************************************************
125
126def getusername():
127    if os.name == "nt":
128        return win32_getusername()
129    user = os.environ.get("USER", "").strip()
130    if not user:
131        user = os.environ.get("LOGNAME", "").strip()
132    return user
133
134
135def getprefdir(package):
136
137    if (TOOLKIT == 'kivy'):
138        from pysollib.kivy.LApp import get_platform
139        plat = get_platform()
140        if plat == 'android':
141            os.environ['HOME'] = '/sdcard'
142
143    if os.name == "nt":
144        return win32_getprefdir(package)
145    home = os.environ.get("HOME", "").strip()
146    if not home or not os.path.isdir(home):
147        home = os.curdir
148    return os.path.join(home, ".PySolFC")
149
150
151# high resolution clock() and sleep()
152try:
153    uclock = time.perf_counter
154except Exception:
155    uclock = time.clock
156
157usleep = time.sleep
158if os.name == "posix":
159    uclock = time.time
160
161# ************************************************************************
162# * MSWin util
163# ************************************************************************
164
165
166def win32_getusername():
167    user = os.environ.get('USERNAME', '').strip()
168    try:
169        user = six.text_type(user, locale.getpreferredencoding())
170    except Exception:
171        user = ''
172    return user
173
174
175def win32_getprefdir(package):
176    portprefdir = 'config'      # portable varsion
177    if os.path.isdir(portprefdir):
178        return portprefdir
179    # %USERPROFILE%, %APPDATA%
180    hd = os.environ.get('APPDATA')
181    if not hd:
182        hd = os.path.expanduser('~')
183        if hd == '~':  # win9x
184            hd = os.path.abspath('/windows/Application Data')
185            if not os.path.exists(hd):
186                hd = os.path.abspath('/')
187    return os.path.join(hd, 'PySolFC')
188
189
190# ************************************************************************
191# * memory util
192# ************************************************************************
193
194def destruct(obj):
195    if TOOLKIT == 'kivy':
196        return
197
198    # assist in breaking circular references
199    if obj is not None:
200        for k in obj.__dict__.keys():
201            obj.__dict__[k] = None
202            # del obj.__dict__[k]
203
204
205# ************************************************************************
206# *
207# ************************************************************************
208
209class Struct:
210    def __init__(self, **kw):
211        self.__dict__.update(kw)
212
213    def __str__(self):
214        return str(self.__dict__)
215
216    def __setattr__(self, key, value):
217        if key not in self.__dict__:
218            raise AttributeError(key)
219        self.__dict__[key] = value
220
221    def addattr(self, **kw):
222        for key in kw.keys():
223            if hasattr(self, key):
224                raise AttributeError(key)
225        self.__dict__.update(kw)
226
227    def update(self, dict):
228        for key in dict.keys():
229            if key not in self.__dict__:
230                raise AttributeError(key)
231        self.__dict__.update(dict)
232
233    def clear(self):
234        for key in self.__dict__.keys():
235            if isinstance(key, list):
236                self.__dict__[key] = []
237            elif isinstance(key, tuple):
238                self.__dict__[key] = ()
239            elif isinstance(key, dict):
240                self.__dict__[key] = {}
241            else:
242                self.__dict__[key] = None
243
244    def copy(self):
245        c = self.__class__()
246        c.__dict__.update(self.__dict__)
247        return c
248
249
250# ************************************************************************
251# * keyword argument util
252# ************************************************************************
253
254# update keyword arguments with default arguments
255def kwdefault(kw, **defaults):
256    for k, v in defaults.items():
257        if k not in kw:
258            kw[k] = v
259
260
261class KwStruct:
262    def __init__(self, kw={}, **defaults):
263        if isinstance(kw, KwStruct):
264            kw = kw.__dict__
265        if isinstance(defaults, KwStruct):
266            defaults = defaults.__dict__
267        if defaults:
268            kw = kw.copy()
269            for k, v in defaults.items():
270                if k not in kw:
271                    kw[k] = v
272        self.__dict__.update(kw)
273
274    def __setattr__(self, key, value):
275        if key not in self.__dict__:
276            raise AttributeError(key)
277        self.__dict__[key] = value
278
279    def __getitem__(self, key):
280        return getattr(self, key)
281
282    def get(self, key, default=None):
283        return self.__dict__.get(key, default)
284
285    def getKw(self):
286        return self.__dict__
287
288
289# ************************************************************************
290# * pickling support
291# ************************************************************************
292
293def pickle(obj, filename, protocol=0):
294    try:
295        with open(filename, "wb") as fh:
296            Pickler(fh, protocol).dump(obj)
297        # print "Pickled", filename
298    finally:
299        pass
300
301
302def unpickle(filename):
303    obj = None
304    try:
305        with open(filename, "rb") as fh:
306            x = Unpickler(fh).load()
307        obj = x
308        # print "Unpickled", filename
309    finally:
310        pass
311    return obj
312
313
314# ************************************************************************
315# *
316# ************************************************************************
317
318def openURL(url):
319    try:
320        webbrowser.open(url)
321    except OSError:                  # raised on windows if link is unreadable
322        pass
323    except Exception:
324        return False
325    return True
326