## vim:ts=4:et:nowrap
##
##---------------------------------------------------------------------------##
##
## PySol -- a Python Solitaire game
##
## Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
## All Rights Reserved.
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; see the file COPYING.
## If not, write to the Free Software Foundation, Inc.,
## 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
##
## Markus F.X.J. Oberhumer
## <markus@oberhumer.com>
## http://www.oberhumer.com/pysol
##
##---------------------------------------------------------------------------##


# imports
import sys, os, re, string, time, types
import traceback

# PySol imports
from mfxtools import *
from mfxutil import EnvError, latin1_to_html
from util import PACKAGE, VERSION, VERSION_TUPLE, CARDSET, bundle
from util import DataLoader
from resource import Tile
from gamedb import GI
from app import Application
from pysolaudio import thread, pysolsoundserver
from pysolaudio import AbstractAudioClient, PysolSoundServerModuleClient, Win32AudioClient

# Toolkit imports
from pysoltk import tkname, tkversion, wm_withdraw, wm_set_icon
from pysoltk import MfxDialog, MfxExceptionDialog
from pysoltk import TclError, MfxRoot
from pysoltk import PysolProgressBar


# /***********************************************************************
# //
# ************************************************************************/

def fatal_no_cardsets(app):
    app.wm_withdraw()
    d = MfxDialog(app.top, title=PACKAGE + " installation error",
                  text="No "+CARDSET+"s were found !!!\n\nMain data directory is:\n" +
                       app.dataloader.dir + "\n\nPlease check your " + PACKAGE + " installation.",
                  bitmap="error", strings=("Quit",))
    ##raise Exception, "no "+CARDSET+"s found !"


# /***********************************************************************
# //
# ************************************************************************/

def pysol_init(app, args):
    # try to create the config directory
    try: os.makedirs(app.dn.config, 0700)
    except: pass
    try: os.mkdir(os.path.join(app.dn.savegames), 0700)
    except: pass
    try: os.mkdir(os.path.join(app.dn.config, "music"), 0700)
    except: pass
    try: os.mkdir(os.path.join(app.dn.config, "screenshots"), 0700)
    except: pass
    try: os.mkdir(os.path.join(app.dn.config, "tiles"), 0700)
    except: pass

    # init DataLoader
    f = os.path.join("html", "license.html")
    app.dataloader = DataLoader(args[0], f)

    # try to load plugins
    if not "--noplugins" in args[1:]:
        for dir in (os.path.join(app.dataloader.dir, "games"),
                    os.path.join(app.dataloader.dir, "plugins"),
                    app.dn.plugins):
            try:
                app.loadPlugins(dir)
            except:
                pass

    # init commandline options (undocumented)
    wm_command = ""
    opt_nosound = 0
    prog = sys.executable
    argv0 = os.path.normpath(args[0])
    if prog and os.path.isfile(prog):
        prog = os.path.abspath(prog)
        if os.path.isfile(argv0):
            wm_command = prog + " " + os.path.abspath(argv0)
    for a in args[1:]:
        if os.path.isfile(a):
            app.commandline.loadgame = a
        elif a == "--nosound":
            opt_nosound = 1
            wm_command = wm_command + " " + a
        # ignore other commandline options

    # init toolkit 1)
    top = MfxRoot(className=PACKAGE)
    app.top = top
    app.top_bg = top.cget("bg")
    app.top_cursor = top.cget("cursor")

    # print some debug info

    # try to load options
    try:
        app.loadOptions()
    except EnvError, ex:
        pass
    except:
        pass


    # init audio 1)
    warn_thread = 0
    warn_pysolsoundserver = 0
    app.audio = None
    if not opt_nosound:
        if os.name == "nt" and app.opt.sound_mode == 0:
            app.audio = Win32AudioClient()
        elif pysolsoundserver:
            app.audio = PysolSoundServerModuleClient()
        elif os.name == "nt":
            app.audio = Win32AudioClient()
    if app.audio:
        app.audio.startServer()
        if app.audio.server is None:
            if os.name == "nt" and not isinstance(app.audio, Win32AudioClient):
                app.audio.destroy()
                app.audio = Win32AudioClient()
                app.audio.startServer()
    else:
        app.audio = AbstractAudioClient()
    # update sound_mode
    if isinstance(app.audio, PysolSoundServerModuleClient):
        app.opt.sound_mode = 1
    else:
        app.opt.sound_mode = 0

    # init toolkit 2)
    top.wm_group(top)
    top.wm_title(PACKAGE + " " + VERSION)
    top.wm_iconname(PACKAGE + " " + VERSION)
    top.wm_minsize(400, 300)
    top.wm_protocol("WM_DELETE_WINDOW", top.wmDeleteWindow)
    if wm_command:
        top.wm_command(wm_command)
    sw, sh, sd = top.winfo_screenwidth(), top.winfo_screenheight(), top.winfo_screendepth()
    if sw < 640 or sh < 480 or sd < 8:
##    if sw < 800 or sh < 600 or sd < 15:
        app.wm_withdraw()
        d = MfxDialog(top, title=PACKAGE + " init error",
                      text=PACKAGE + " requires a minimum screen resolution\nof 640x480 with 256 colors.\n\nPlease change your display settings.",
##                      text=PACKAGE + " requires a minimum screen resolution\nof 640x480 with 32768 colors.\n\nPlease change your display settings.",
                      bitmap="error", strings=("Quit",))
        return 1
    if 1:
        # set expected window size to assist the layout of the window manager
        top.config(width=min(800,sw-64), height=min(600,sh-64))
    try:
        wm_set_icon(top, app.dataloader.findIcon())
    except: pass

    # set global color scheme
    if os.name == "posix":              # Unix/X11
        ##color = "grey"
        color = "#d9d9d9"               # Tk 8.0p2 default
        ##top.tk_setPalette(color)
        top.tk_setPalette("background", color, "activeBackground", color)
##        top.option_add("*Menu*font", "Helvetica -12", 60)
        top.option_add("*font", "Helvetica -12", 60)
##        top.option_add("*highlightBackground", "#ff0000", 60)
##        top.option_add("*activeForeground", "#ff0000", 60)
##        top.option_add("*activeBackground", "#ff0000", 60)
    if os.name == "mac":
        color, priority = "#d9d9d9", "60"
        classes = (
            "Button", "Canvas", "Checkbutton", "Entry",
            "Frame", "Label", "Listbox", "Menubutton", ### "Menu",
            "Message", "Radiobutton", "Scale", "Scrollbar", "Text",
        )
        for c in classes:
            top.option_add("*" + c + "*background", color, priority)
            top.option_add("*" + c + "*activeBackground", color, priority)

    # check games
    if len(app.gdb.getGamesIdSortedByName()) == 0:
        app.wm_withdraw()
        d = MfxDialog(top, title=PACKAGE + " installation error",
                      text="No games were found !!!\n\nMain data directory is:\n" +
                           app.dataloader.dir + "\n\nPlease check your " + PACKAGE + " installation.",
                      bitmap="error", strings=("Quit",))
        return 1

    # init cardsets
    app.initCardsets()
    cardset = None
    c = app.opt.cardset.get(0)
    if c:
        cardset = app.cardset_manager.getByName(c[0])
        if cardset and c[1]:
            cardset.updateCardback(backname=c[1])
    if not cardset:
        cardset = app.cardset_manager.get(0)
    if app.cardset_manager.len() == 0 or not cardset:
        fatal_no_cardsets(app)
        return 3

    # init tiles
    manager = app.tabletile_manager
    tile = Tile()
    tile.color = app.opt.tablecolor
    tile.name = "None"
    tile.filename = None
    manager.register(tile)
    app.initTiles()
    if app.opt.tabletile_name: ### and top.winfo_screendepth() > 8:
        for tile in manager.getAll():
            if app.opt.tabletile_name == tile.basename:
                app.tabletile_index = tile.index
                break

    # init samples and music resources
    app.initSamples()
    app.initMusic()

    # init audio 2)
    app.audio.connectServer(app)
    if app.audio.audiodev is None:
        app.opt.sound = 0
    if pysolsoundserver and not app.audio.connected:
        print PACKAGE + ": could not connect to pysolsoundserver, sound disabled."
        warn_pysolsoundserver = 1
    app.audio.updateSettings()
    # start up the background music
    if app.audio.audiodev:
        music = app.music_manager.getAll()
        if music:
            app.music_playlist = list(music)[:]
            app.miscrandom.shuffle(app.music_playlist)
            if 1: ## and not app.debug:
                for m in app.music_playlist:
                    if string.lower(m.name) == "bye_for_now":
                        app.music_playlist.remove(m)
                        app.music_playlist.insert(0, m)
                        break
            app.audio.playContinuousMusic(app.music_playlist)


    # prepare the progress bar
    app.progress_bg = "#c0c0c0"
    ##app.progress_bg = None
    app.loadImages1()
    if not app.progress_images:
        app.progress_images = (app.gimages.logos[0], app.gimages.logos[1])
    app.wm_withdraw()

    # warn about audio problems
    if not opt_nosound and os.name == "posix" and pysolsoundserver is None:
        if 1 and app.opt.sound and re.search(r"linux", sys.platform, re.I):
            warn_pysolsoundserver = 1
            if thread is None:
                warn_thread = 1
        if thread is None:
            print PACKAGE + ": Python thread module not found, sound disabled."
        else:
            print PACKAGE + ": pysolsoundserver module not found, sound disabled."
        sys.stdout.flush()
    if warn_thread:
        top.update()
        d = MfxDialog(top, title=PACKAGE + " installation problem",
                      text="Your Python installation is compiled without thread support.\n\nSounds and background music will be disabled.",
                      bitmap="warning", strings=("OK",))
    elif warn_pysolsoundserver:
        top.update()
        d = MfxDialog(top, title=PACKAGE + " installation problem",
                      text="The pysolsoundserver module was not found.\n\nSounds and background music will be disabled.",
                      bitmap="warning", strings=("OK",))

    # create the progress bar
    title = "Welcome to " + PACKAGE
    color = app.opt.tablecolor
    if app.tabletile_index > 0:
        color = "#008200"
    app.intro.progress = PysolProgressBar(app, top, title=title, color=color, bg=app.progress_bg, images=app.progress_images)

    # prepare other images
    app.loadImages2()
    app.loadImages3()
    app.loadImages4()

    # load cardset
    progress = app.intro.progress
    if not app.loadCardset(cardset, progress=progress, update=1):
        for cardset in app.cardset_manager.getAll():
            progress.reset()
            if app.loadCardset(cardset, progress=progress, update=1):
                break
        else:
            fatal_no_cardsets(app)
            return 3

    # ok
    return 0


# /***********************************************************************
# //
# ************************************************************************/

def pysol_exit(app):
    # clean up
    if app.audio is not None:
        app.audio.destroy()         # shut down audio
        destruct(app.audio)
    app.wm_withdraw()
    if app.canvas is not None:
        app.canvas.destroy()
        destruct(app.canvas)
    if app.toolbar is not None:
        app.toolbar.destroy()
        destruct(app.toolbar)
    if app.menubar is not None:
        destruct(app.menubar)
    top = app.top
    destruct(app)
    app = None
    if top is not None:
        try:
            top.destroy()
        except:
            pass
        destruct(top)


# /***********************************************************************
# // PySol main entry
# ************************************************************************/

def pysol_main(args):
    # create the application
    app = Application()
    try:
        r = pysol_init(app, args)
        if r != 0:
            return r
        # let's go - enter the mainloop
        app.mainloop()
    except KeyboardInterrupt, ex:
        print "Exiting on SIGINT."
        pass
    except StandardError, ex:
        if not app.top:
            raise
        t = str(ex.__class__)
        if str(ex): t = t + ":\n" + str(ex)
        d = MfxDialog(app.top, title=PACKAGE + " internal error",
                      text="Internal errror. Please report this bug:\n\n"+t,
                      strings=("Quit",), bitmap="error")
    try:
        pysol_exit(app)
    except:
        pass
    return 0


# /***********************************************************************
# // main
# ************************************************************************/

def main(args=None):

    # setup (mainly for JPython)
    if not hasattr(sys, "platform"):
        sys.platform = "unknown"
    if not hasattr(sys, "executable"):
        sys.executable = None
    if not hasattr(os, "defpath"):
        os.defpath = ""

    # check versions
    if sys.platform[:4] != "java":
        if sys.version[:5] < "1.5.2":
            print "%s needs Python 1.5.2 or better (you have %s)" % (PACKAGE, sys.version)
            return 1
    assert len(tkversion) == 4
    if tkname == "tk":
        import Tkinter
        if tkversion < (8, 0, 0, 0):
            print "%s needs Tcl/Tk 8.0 or better (you have %s)" % (PACKAGE, str(tkversion))
            return 1
        # check that Tkinter bindings are also at version 1.5.2
        if not hasattr(Tkinter.Wm, "wm_aspect") or not hasattr(Tkinter.Canvas, "tag_lower"):
            print "%s: please update the Python-Tk bindings (aka Tkinter) to version 1.5.2 or better" % (PACKAGE,)
            return 1
    # check Python
    if -1 % 13 != 12:
        raise Exception, "-1 % 13 != 12"

    # run it
    r = pysol_main(args)
    ##print "FINAL\n"; dumpmem()
    return r

