#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2010 British Broadcasting Corporation and Kamaelia Contributors(1)
#
# (1) Kamaelia Contributors are listed in the AUTHORS file and at
#     http://www.kamaelia.org/AUTHORS - please extend this file,
#     not this notice.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -------------------------------------------------------------------------
 
"""\
=========================================
GUI for Ryans Lothians Bittorrent Package
=========================================
 
A 3D GUI for bittorrent downloading using Ryan Lothians Kamaelia
Bittorrent package. Features torrent fetching from URLs,
starting/stopping of torrents and showing torrent information.
 
To work properly, an installation of Bittorrent 4.20.8 or higher is
required.
"""
 
import Axon
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
 
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.Vector import Vector
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.Button import Button
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.ArrowButton import ArrowButton
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.ProgressBar import ProgressBar
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.Container import Container
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.SkyGrassBackground import SkyGrassBackground
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.Label import Label
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.PygameWrapper import PygameWrapper
from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.Movement import WheelMover, PathMover, LinearPath
 
from Kamaelia.UI.Pygame.Ticker import Ticker
from Kamaelia.Community.RJL.Kamaelia.Protocol.Torrent.TorrentIPC import *
 
from BitTorrent.bencode import bdecode
 
import random
import os
import time
import sys
 
class TorrentOpenGLGUI(Axon.AdaptiveCommsComponent.AdaptiveCommsComponent):
    Inboxes = {
        "inbox":"Receive messages from TorrentPatron",
        "control":"receive shutdown messages",
 
        "nav":"To receive commands from navigation buttons",
 
        "start": "Start commands from buttons",
        "stop": "Stop commands from buttons",
 
        "show_info": "Info commands from buttons",
        "hide_info": "Hide torrent info",
 
        "torrent_url": "For reception of torrent file URLs",
        "torrent_file": "For reception of torrent files",
    }
 
    Outboxes = {
        "outbox":"Send Messages to TorrentPatron",
        "signal" : "",
 
        "fetcher" : "Send URLs to SimpleHttpClient",
 
        "mover_signal": "Send command to WheelMover",
        "mover_switch": "Control WheelMover",
 
        "infomover_commands": "Send commands to info container mover",
        "torrent_info": "For sending info about a torrent"
    }
 
 
    def __init__(self, **argd):
        super(TorrentOpenGLGUI, self).__init__()
 
        self.torrents = []
        self.torrent_from_id = {}
        self.torrent_to_id = {}
        self.torrent_progress_comms = {}
 
        self.requested_files = []
        self.started_torrents = []
 
 
    def main(self):
        self.initUIComponents()        
        self.loadLocalTorrentFiles()
 
        while 1:
            yield 1
 
            while self.dataReady("inbox"):
                msg = self.recv("inbox")
#                print msg
 
                if isinstance(msg, TIPCNewTorrentCreated):
                    torrent = self.started_torrents.pop(0)
                    self.torrent_from_id[msg.torrentid] = torrent
                    self.torrent_to_id[torrent] = msg.torrentid
 
                elif isinstance(msg, TIPCTorrentStartFail) or isinstance(msg, TIPCTorrentAlreadyDownloading):
                    self.started_torrents.pop(0)
 
                elif isinstance(msg, TIPCTorrentStatusUpdate):
                    self.send(msg.statsdictionary.get("fractionDone","0"), self.torrent_progress_comms[ self.torrent_from_id[msg.torrentid]] )
 
            while self.dataReady("torrent_url"):
                url = self.recv("torrent_url")
                filename = os.path.basename(url)
                self.requested_files.append(filename)
                self.send(url, "fetcher")
 
            while self.dataReady("torrent_file"):
                torrentfile = self.recv("torrent_file")
                # save received file
                filename = self.requested_files.pop()
                fobj = open(os.getcwd()+"/"+filename, 'w')
                fobj.write(torrentfile)
                fobj.close()
                # add torrent
                self.addTorrent(filename, torrentfile)                
 
            while self.dataReady("nav"):
                msg = self.recv("nav")
                if msg == "UP":
                    self.send("NEXT", "mover_switch")
                if msg == "DOWN":
                    self.send("PREVIOUS", "mover_switch")
 
            while self.dataReady("start"):
                torrent = self.recv("start")
                self.send(torrent)
                self.started_torrents.append(torrent)
 
            while self.dataReady("stop"):
                torrent = self.recv("stop")
                try:
                    self.send( TIPCCloseTorrent(torrentid=self.torrent_to_id[torrent]) )
                    self.send(0.0, self.torrent_progress_comms[torrent])
                except KeyError:
                    pass
 
            while self.dataReady("show_info"):
                torrent = self.recv("show_info")
                self.showInfo(torrent)
 
            while self.dataReady("hide_info"):
                msg = self.recv("hide_info")
                self.hideInfo()
 
            while self.dataReady("control"):
                cmsg = self.recv("control")
                if isinstance(cmsg, Axon.Ipc.shutdownMicroprocess):
                    # dirty way to terminate program
                    sys.exit(0)
 
 
    def initUIComponents(self):
        # listen to shutdown events
        ogl_display = OpenGLDisplay().getDisplayService()[0]
        self.link( (ogl_display, "signal"), (self, "control") )
 
        # init mover
        self.mover = WheelMover(radius=15, center=(0,0,-25), steps=500, slots=40).activate()
        self.link( (self, "mover_signal"), (self.mover, "notify") )
        self.link( (self, "mover_switch"), (self.mover, "switch") )
 
        self.background = SkyGrassBackground(size=(5000,5000,0), position=(0,0,-90)).activate()
 
        # create & link nav buttons
        self.up_button = ArrowButton(size=(1,1,0.3), position=(7,5,-15), msg="UP").activate()
        self.down_button = ArrowButton(size=(1,1,0.3), position=(7,-5,-15), rotation=(0,0,180), msg="DOWN").activate()
        self.link( (self.up_button, "outbox"), (self, "nav") )
        self.link( (self.down_button, "outbox"), (self, "nav") )
 
        # init info display
        self.infoticker = Ticker(text_height=21, render_right=250, render_bottom=500, background_colour=(250,250,200), text_colour=(0,0,0), outline_colour=(255,255,255)).activate()
        self.tickerwrapper = PygameWrapper(wrap=self.infoticker, size=(2.4,4.0,0.3)).activate()
        self.hideinfo_button = Button(caption="Hide", fontsize=30).activate()
 
        infocontents = {
            self.tickerwrapper : { "position":(0,0,0) },
            self.hideinfo_button : { "position":(0,-2.4,0) },
        }
 
        self.infocontainer = Container(contents=infocontents, position=(-10, 10, -16)).activate()
        infopath = LinearPath([(-10, 10, -16), (-3,0,-8)], 100)
 
        self.infomover = PathMover(infopath, False).activate()
 
        self.link( (self.infomover, "outbox"), (self.infocontainer, "position") )
        self.link( (self, "infomover_commands"), (self.infomover, "inbox") )
        self.link( (self, "torrent_info"), (self.infoticker, "inbox") )
        self.link( (self.hideinfo_button, "outbox"), (self, "hide_info") )
 
        self.send("Stop", "infomover_commands")
 
 
    def loadLocalTorrentFiles(self):
        print "Loading local torrent files..."
        cwd = os.getcwd()
        files = os.listdir(cwd)
        for f in files:
            if f.endswith(".torrent"):
                print "- ",f
                fobj = open(f)
                torrent = fobj.read() 
                fobj.close()
                self.addTorrent(f, torrent)
 
 
    def addTorrent(self, title, torrent):
        self.torrents.append(torrent)
 
        start = Button(size=(0.8,0.5,0.3), caption="Start", fontsize=35, msg=torrent).activate()
        info  = Button(size=(0.8,0.5,0.3), caption="Info", fontsize=35, msg=torrent).activate()
        stop = Button(size=(0.8,0.5,0.3), caption="Stop", fontsize=35, msg=torrent).activate()
        colour = [ int(random.randint(100,255)) for i in range(3) ]
        progress = ProgressBar(size=(3.2,0.5,0.3), barcolour=colour).activate()
        label = Label( size=(6.0, 0.3, 0.3), caption=title, fontsize=26, bgcolour=colour).activate()
 
        self.link( (start, "outbox"), (self, "start") )
        self.link( (stop, "outbox"), (self, "stop") )
        self.link( (info, "outbox"), (self, "show_info") )
 
        container_elements = {
            progress : { "position":(-0.4,-0.3,0) },
            start : { "position":(1.8,-0.3,0) },
            stop : { "position":(2.7,-0.3,0) },
            info : { "position":(3.6,-0.3,0) },
            label : { "position":(1, 0.3, 0) },
        }
 
        container = Container(contents=container_elements, position=(0,0,-10)).activate()
 
        req = { "APPEND_CONTROL":True,
                "objectid": id(container),
                "control": (container,"position")
        }
        self.send(req, "mover_signal")
 
        # create & link box for progress update
        progbox = self.addOutbox("progress")
        self.torrent_progress_comms[torrent] = progbox
        self.link( (self, progbox), (progress, "progress") )
 
 
    def showInfo(self, torrent):
        decoded = bdecode(torrent)
 
        info = decoded.get("info", None)
        if info is not None:
            name = info.get("name", "??")
            length = info.get("length", "??")
 
        date = decoded.get("creation date", "??")
        creator = decoded.get("created by", "??")
        announce = decoded.get("announce", "??")
        comment = decoded.get("comment", "??")
 
        infotuple = (name, comment, length, date, creator)
 
        infostring = "Name:%s\nComment:%s\nLength:%s\nCreation_Date:%s\nCreated_by:%s\n ----- \n" % infotuple
 
        # move info container in front of everthig
        self.send("Forward", "infomover_commands")
        self.send("Play", "infomover_commands")
        self.send(infostring, "torrent_info")
 
 
    def hideInfo(self):
        self.send("Backward", "infomover_commands")
        self.send("Play", "infomover_commands")
 
 
if __name__ == "__main__":
    from Kamaelia.Chassis.Graphline import Graphline
    from Kamaelia.Util.Console import ConsoleReader
    from Kamaelia.UI.PygameDisplay import PygameDisplay
    from Kamaelia.Community.THF.Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay
    from Kamaelia.Community.RJL.Kamaelia.Protocol.HTTP.HTTPClient import SimpleHTTPClient
    from Kamaelia.Community.RJL.Kamaelia.Protocol.Torrent.TorrentPatron import TorrentPatron
 
    ogl_display = OpenGLDisplay(limit_fps=100).activate()  
    OpenGLDisplay.setDisplayService(ogl_display)
    # override pygame display service
    PygameDisplay.setDisplayService(ogl_display)
 
    Graphline(
        reader = ConsoleReader(prompt="Enter torrent location:", eol=""),
        httpclient = SimpleHTTPClient(),
        gui = TorrentOpenGLGUI(),
        backend = TorrentPatron(),
        linkages = {
            ("gui", "outbox") : ("backend", "inbox"),
            ("reader", "outbox") : ("gui", "torrent_url"),
            ("gui", "fetcher") : ("httpclient", "inbox"),
            ("httpclient", "outbox") : ("gui", "torrent_file"),
            ("backend", "outbox"): ("gui", "inbox")
        }
    ).run()
 
# Licensed to the BBC under a Contributor Agreement: THF