## 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 os, string, sys, types, Tkinter

# PySol imports
from mfxtools import *
from mfxutil import destruct, Struct, kwdefault, KwStruct
from util import *
from stats import PysolStatsFormatter

# Toolkit imports
from tkutil import bind, unbind_destroy, getFont, loadImage
from tkwidget import _ToplevelDialog, MfxDialog
from tkwidget import MfxScrolledCanvas


# FIXME - this file a quick hack and needs a rewrite


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

class SingleGame_StatsDialog(MfxDialog):
    def __init__(self, parent, title, app, player, gameid, **kw):
        kw = self.initKw(kw)
        _ToplevelDialog.__init__(self, parent, title, kw.resizable, kw.default)
        top_frame, bottom_frame = self.createFrames(kw)
        self.createBitmaps(top_frame, kw)
        #
        self.player = player or "Demo games"
        self.top.wm_minsize(200, 200)
        self.button = kw.default
        #
        createChart = self.create3DBarChart
        createChart = self.createPieChart
        if parent.winfo_screenwidth() < 800 or parent.winfo_screenheight() < 600:
            createChart = self.createPieChart
            createChart = self.createSimpleChart
        #
        won, lost = app.stats.getStats(player, gameid)
        createChart(top_frame, app, won, lost, "Total")
        g = app.stats.session_games.get(player, [])
        g = filter(lambda a, b=gameid: a[0] == b, g)
        won = len(filter(lambda a, b=gameid: a[2] > 0, g))
        lost = len(filter(lambda a, b=gameid: a[2] == 0, g))
        createChart(top_frame, app, won, lost, "Current session")
        #
        focus = self.createButtons(bottom_frame, kw)
        self.mainloop(focus, kw.timeout)

    #
    # helpers
    #

    def _getPwon(self, won, lost):
        pwon, plost = 0.0, 0.0
        if won + lost > 0:
            pwon = float(won) / (won + lost)
            pwon = min(max(pwon, 0.00001), 0.99999)
            plost = 1.0 - pwon
        return pwon, plost

    def _createChartInit(self, frame, w, h, text):
        tfont = getFont("tree_small")
        #
        frame = Tkinter.Frame(frame)
        c = Tkinter.Canvas(frame, width=w, height=h)
        fg = c.cget("insertbackground")
        frame.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=0, padx=20, pady=10)
        c.pack(side=Tkinter.TOP)
        #
        c.create_rectangle(2, 7, w, h, fill="", outline="#7f7f7f")
        l = Tkinter.Label(c, text=text, font=tfont, bd=0, padx=3, pady=1)
        c.create_window(20, 0, window=l, anchor="nw")
        return c, tfont, fg

    def _createChartTexts(self, c, tx, ty, won, lost):
        tfont = getFont("tree_small")
        fg = c.cget("insertbackground")
        pwon, plost = self._getPwon(won, lost)
        #
        x = tx[0]
        c.create_text(x, ty[0], text="Won:", anchor="nw", font=tfont, fill=fg)
        c.create_text(x, ty[1], text="Lost:", anchor="nw", font=tfont, fill=fg)
        c.create_text(x, ty[2], text="Total:", anchor="nw", font=tfont, fill=fg)
        x = tx[1] - 16
        c.create_text(x, ty[0], text="%d" % won, anchor="ne", font=tfont, fill=fg)
        c.create_text(x, ty[1], text="%d" % lost, anchor="ne", font=tfont, fill=fg)
        c.create_text(x, ty[2], text="%d" % (won + lost), anchor="ne", font=tfont, fill=fg)
        y = ty[2] - 11
        c.create_line(tx[0], y, x, y, fill=fg)
        if won + lost > 0:
            x = tx[2]
            pw = int(round(100.0 * pwon))
            c.create_text(x, ty[0], text="%d%%" % pw, anchor="ne", font=tfont, fill=fg)
            c.create_text(x, ty[1], text="%d%%" % (100-pw), anchor="ne", font=tfont, fill=fg)

    def _createChart3DBar(self, canvas, perc, x, y, p, col):
        if perc < 0.005:
            return
        # translate and scale
        p = list(p[:])
        for i in (0, 1, 2, 3):
            p[i] = (x + p[i][0], y + p[i][1])
            j = i + 4
            dx = int(round(p[j][0] * perc))
            dy = int(round(p[j][1] * perc))
            p[j] = (p[i][0] + dx, p[i][1] + dy)
        # draw rects
        def draw_rect(a, b, c, d, col, canvas=canvas, p=p):
            points = (p[a][0], p[a][1], p[b][0], p[b][1],
                      p[c][0], p[c][1], p[d][0], p[d][1])
            canvas.create_polygon(points, fill=col)
        draw_rect(0, 1, 5, 4, col[0])
        draw_rect(1, 2, 6, 5, col[1])
        draw_rect(4, 5, 6, 7, col[2])
        # draw lines
        def draw_line(a, b, canvas=canvas, p=p):
            ##print a, b, p[a], p[b]
            canvas.create_line(p[a][0], p[a][1], p[b][0], p[b][1])
        draw_line(0, 1)
        draw_line(1, 2)
        draw_line(0, 4)
        draw_line(1, 5)
        draw_line(2, 6)
        ###draw_line(3, 7)     ## test
        draw_line(4, 5)
        draw_line(5, 6)
        draw_line(6, 7)
        draw_line(7, 4)


    #
    # charts
    #

    def createSimpleChart(self, frame, app, won, lost, text):
        c, tfont, fg = self._createChartInit(frame, 300, 100, text)
        #
        tx = (90, 180, 210)
        ty = (21, 41, 75)
        self._createChartTexts(c, tx, ty, won, lost)

    def create3DBarChart(self, frame, app, won, lost, text):
        image = app.gimages.stats[0]
        iw, ih = image.width(), image.height()
        c, tfont, fg = self._createChartInit(frame, iw+160, ih, text)
        pwon, plost = self._getPwon(won, lost)
        #
        tx = (iw+20, iw+110, iw+140)
        yy = ih/2 ## + 7
        ty = (yy+21-46, yy+41-46, yy+75-46)
        #
        c.create_image(0, 7, image=image, anchor="nw")
        #
        p = ((0, 0), (44, 6), (62, -9), (20, -14),
             (-3, -118), (-1, -120), (-1, -114), (-4, -112))
        col = ("#00ff00", "#008200", "#00c300")
        self._createChart3DBar(c, pwon,  102, 145+7, p, col)
        p = ((0, 0), (49, 6), (61, -10), (15, -15),
             (1, -123), (3, -126), (4, -120), (1, -118))
        col = ("#ff0000", "#860400", "#c70400")
        self._createChart3DBar(c, plost, 216, 159+7, p, col)
        #
        self._createChartTexts(c, tx, ty, won, lost)
        c.create_text(tx[0], ty[0]-48, text=self.player, anchor="nw", font=tfont, fill=fg)

    def createPieChart(self, frame, app, won, lost, text):
        c, tfont, fg = self._createChartInit(frame, 300, 100, text)
        pwon, plost = self._getPwon(won, lost)
        #
        tx = (160, 250, 280)
        ty = (21, 41, 75)
        #
        if won + lost > 0:
            ##s, ewon, elost = 90.0, -360.0 * pwon, -360.0 * plost
            s, ewon, elost = 0.0, 360.0 * pwon, 360.0 * plost
            c.create_arc(20, 25+9, 110, 75+9,  fill="#007f00", start=s, extent=ewon)
            c.create_arc(20, 25+9, 110, 75+9,  fill="#7f0000", start=s+ewon, extent=elost)
            c.create_arc(20, 25,   110, 75,    fill="#00ff00", start=s, extent=ewon)
            c.create_arc(20, 25,   110, 75,    fill="#ff0000", start=s+ewon, extent=elost)
            x, y = tx[0] - 25, ty[0]
            c.create_rectangle(x, y, x+10, y+10, fill="#00ff00")
            y = ty[1]
            c.create_rectangle(x, y, x+10, y+10, fill="#ff0000")
        else:
            c.create_oval(20, 25+10, 110, 75+10, fill="#7f7f7f")
            c.create_oval(20, 25,    110, 75,    fill="#f0f0f0")
            c.create_text(65, 50, text="No games", anchor="center", font=tfont, fill="#bfbfbf")
        #
        self._createChartTexts(c, tx, ty, won, lost)

    #
    #
    #

    def initKw(self, kw):
        kw = KwStruct(kw,
            strings=("OK", ("All games...", 102), ("Reset...", 302)), default=0,
            separatorwidth=2, resizable=1,
            padx=10, pady=10,
        )
        return MfxDialog.initKw(self, kw)

    def getDefaultFont(self):
        return getFont("small")


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

class AllGames_StatsDialogScrolledCanvas(MfxScrolledCanvas):
    pass


class AllGames_StatsDialog(MfxDialog):
    # for font "canvas_fixed"
    CHAR_W, CHAR_H = 7, 16
    if os.name == "mac": CHAR_W = 6
    #
    YVIEW = 0

    def __init__(self, parent, title, app, player, **kw):
        lines = 25
        if parent and parent.winfo_screenheight() < 600:
            lines = 20
        kwdefault(kw, height=lines*self.CHAR_H)
        kw = self.initKw(kw)
        _ToplevelDialog.__init__(self, parent, title, kw.resizable, kw.default)
        top_frame, bottom_frame = self.createFrames(kw)
        self.createBitmaps(top_frame, kw)
        #
        self.app = app
        self.top.wm_minsize(200, 200)
        self.button = kw.default
        self.font = kw.font
        self.sc = AllGames_StatsDialogScrolledCanvas(top_frame, width=kw.width, height=kw.height)
        self.sc.pack(fill=Tkinter.BOTH, expand=1, padx=kw.padx, pady=kw.pady)
        #
        self.nodes = {}
        self.canvas = self.sc.canvas
        self.canvas.dialog = self
        bind(self.canvas, "<ButtonPress-1>", self.singleClick)
        self.fillCanvas(player, title)
        bbox = self.canvas.bbox("all")
        ##print bbox
        ##self.canvas.config(scrollregion=bbox)
        self.canvas.config(scrollregion=(0,0,bbox[2],bbox[3]))
        self.canvas.yview_moveto(self.YVIEW)
        #
        focus = self.createButtons(bottom_frame, kw)
        self.mainloop(focus, kw.timeout)

    def initKw(self, kw):
        kw = KwStruct(kw,
            strings=("OK", ("Save to file", 202), ("Reset all...", 301),), default=0,
            separatorwidth=2, resizable=1,
            padx=10, pady=10,
            width=500,
        )
        return MfxDialog.initKw(self, kw)

    def getDefaultFont(self):
        return getFont("small")

    def destroy(self):
        self.app = None
        self.canvas.dialog = None
        self.nodes = {}
        self.sc.destroy()
        MfxDialog.destroy(self)

    def singleClick(self, event=None):
        id = self.canvas.find_withtag(Tkinter.CURRENT)
        if not id:
            return
        gameid, gamenumber = self.nodes.get(id[0], (None, None))
        ## FIXME / TODO
        return
        if gameid and gamenumber:
            print gameid, gamenumber
        elif gameid:
            print gameid

    #
    #
    #

    def fillCanvas(self, player, header):
        a = PysolStatsFormatter(self.app)
        writer = self.CanvasWriter(self.canvas, self.font, self.CHAR_H)
        if not a.writeStats(writer, player, header):
            writer.p("No entries for player " + ustr(player) + "\n")
        destruct(writer)
        destruct(a)


    class CanvasWriter(PysolStatsFormatter.StringWriter):
        def __init__(self, canvas, font, h):
            self.canvas = canvas
            self.fg = canvas.cget("insertbackground")
            self.font = font
            self.h = h
            self.x = self.y = 0
            self.gameid = None
            self.gamenumber = None
            self.canvas.config(yscrollincrement=h)

        def _addItem(self, id):
            self.canvas.dialog.nodes[id] = (self.gameid, self.gamenumber)

        def p(self, s):
            if self.y > 16000:
                return
            h1, h2 = 0, 0
            while s and s[0] == "\n":
                s = s[1:]
                h1 = h1 + self.h
            while s and s[-1] == "\n":
                s = s[:-1]
                h2 = h2 + self.h
            self.y = self.y + h1
            if s:
                id = self.canvas.create_text(self.x, self.y, text=s, anchor="nw", font=self.font, fill=self.fg)
                self._addItem(id)
            self.y = self.y + h2

        def pheader(self, s):
            pass

        def pstats(self, t1, t2, t3, t4, t5, gameid=None):
            if self.y > 16000:
                return
            self.gameid = gameid
            self.gamenumber = None
            x, y = 1, self.y
            p = self._pstats_text
            h = 0
            h = max(h, p(x    , y, anchor="nw", text=ustr(t1)))
            h = max(h, p(x+200, y, anchor="ne", text=ustr(t2)))
            h = max(h, p(x+250, y, anchor="ne", text=ustr(t3)))
            h = max(h, p(x+300, y, anchor="ne", text=ustr(t4)))
            h = max(h, p(x+350, y, anchor="ne", text=ustr(t5)))
            self.pstats_perc(x+372, y, ustr(t5))
            self.y = self.y + h
            self.gameid = None

        def _pstats_text(self, x, y, **kw):
            kwdefault(kw, font=self.font, fill=self.fg)
            id = apply(self.canvas.create_text, (x, y), kw)
            self._addItem(id)
            return self.h
            ##bbox = self.canvas.bbox(id)
            ##return bbox[3] - bbox[1]

        def pstats_perc(self, x, y, t):
            if not (t and "0" <= t[0] <= "9"):
                return
            perc = int(round(string.atof(str(t))))
            if perc < 1:
                return
            rx, ry, rw, rh = x, y+1, 2 + 8*10, self.h-5
            if 1:
                w = int(round(rw*perc/100.0))
                if 1 and w < 1:
                    return
                if w > 0:
                    w = max(3, w)
                    w = min(rw - 2, w)
                    id = self.canvas.create_rectangle(rx, ry, rx+w, ry+rh, width=1,
                                                      fill="#00ff00", outline="#000000")
                if w < rw:
                    id = self.canvas.create_rectangle(rx+w, ry, rx+rw, ry+rh, width=1,
                                                      fill="#ff0000", outline="#000000")
                return
            ##fill = "#ffffff"
            ##fill = self.canvas["bg"]
            fill = None
            id = self.canvas.create_rectangle(rx, ry, rx+rw, ry+rh, width=1,
                                              fill=fill, outline="#808080")
            if 1:
                rx, rw = rx + 1, rw - 1
                ry, rh = ry + 1, rh - 1
                w = int(round(rw*perc/100.0))
                if w > 0:
                    id = self.canvas.create_rectangle(rx, ry, rx+w, ry+rh, width=0,
                                                      fill="#00ff00", outline="")
                if w < rw:
                    id = self.canvas.create_rectangle(rx+w, ry, rx+rw, ry+rh, width=0,
                                                      fill="#ff0000", outline="")
                return
            p = 1.0
            ix = rx + 2
            for i in (1, 11, 21, 31, 41, 51, 61, 71, 81, 91):
                if perc < i:
                    break
                ##c = "#ff8040"
                r, g, b = 255, 128*p, 64*p
                c = "#%02x%02x%02x" % (int(r), int(g), int(b))
                id = self.canvas.create_rectangle(ix, ry+2, ix+6, ry+rh-2, width=0,
                                                  fill=c, outline=c)
                ix = ix + 8
                p = max(0.0, p - 0.1)

        def plog(self, gamename, gamenumber, date, status, gameid=-1, won=-1):
            if gameid > 0 and "0" <= gamenumber[0:1] <= "9":
                self.gameid = gameid
                self.gamenumber = gamenumber
            self.p("%-25s %-20s  %17s  %s\n" % (gamename, gamenumber, date, status))
            self.gameid = None
            self.gamenumber = None


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

class FullLog_StatsDialog(AllGames_StatsDialog):
    YVIEW = 1

    def fillCanvas(self, player, header):
        a = PysolStatsFormatter(self.app)
        writer = self.CanvasWriter(self.canvas, self.font, self.CHAR_H)
        if not a.writeFullLog(writer, player, header):
            writer.p("No log entries for " + ustr(player) + "\n")
        destruct(a)

    def initKw(self, kw):
        kw = KwStruct(kw,
            strings=("OK", ("Session log...", 104), ("Save to file", 203)), default=0,
            font=getFont("canvas_fixed"),
            width=76*self.CHAR_W,
        )
        return AllGames_StatsDialog.initKw(self, kw)


class SessionLog_StatsDialog(FullLog_StatsDialog):
    def fillCanvas(self, player, header):
        a = PysolStatsFormatter(self.app)
        writer = self.CanvasWriter(self.canvas, self.font, self.CHAR_H)
        if not a.writeSessionLog(writer, player, header):
            writer.p("No current session log entries for " + ustr(player) + "\n")
        destruct(a)

    def initKw(self, kw):
        kw = KwStruct(kw,
            strings=("OK",  ("Full log...", 103), ("Save to file", 204)), default=0,
        )
        return FullLog_StatsDialog.initKw(self, kw)

