#!/usr/bin/env python
# -*- coding: Latin-1 -*-

"""
 This scripts starts a small http-server that serves the current-playlist as
 HTML. Start it and point your browser to http://localhost:4773/

 Author: André Kelpe fs111 at web dot de
       : Jonas Drewsen kde at xspect dot dk

 License: GPL

"""
import SimpleHTTPServer
import BaseHTTPServer
from Playlist import Playlist
import Globals

import time

# necessary for <= python2.2 that cannot handle "infds" in var
import string

PLIST = None

# keep track of request ids in order to not repeat
# requests if user refreshes his browser
REQ_IDS = {}

#
# Holding current AmarokStatus. A bunch of init_XXX functions in
# order to begin dcop requests as early as possible to avoid too
# much latency
#
class AmarokStatus:

    EngineEmpty = 1
    EngineIdle = 2
    EnginePause = 3
    EnginePlay = 4

    allowControl = 0
    playState = -1

    dcop_isplaying = None
    dcop_volume = None
    dcop_trackcurrentindex = None
    dcop_trackcurrenttime = None
    dcop_tracktotaltime = None
    
    def __init__(self):
        self.controls_enabled = 0
        self.time_left = None

        self.dcop_isplaying = Globals.PlayerDcop("isPlaying")
        self.dcop_volume = Globals.PlayerDcop("getVolume")
        self.dcop_trackcurrentindex = Globals.PlaylistDcop("getActiveIndex")
        self.dcop_trackcurrenttime = Globals.PlayerDcop("trackCurrentTime")
        self.dcop_tracktotaltime = Globals.PlayerDcop("trackTotalTime")
        
    def isPlaying(self):
        if self.playState != -1:
            res = self.playState == self.EnginePlay
        else:
            res = string.find(self.dcop_isplaying.result(), "true") >= 0
            if res:
                self.playState = self.EnginePlay
            else:
                self.playState = self.EnginePause
        
        return res

    def getActiveIndex(self):
        return int(self.dcop_trackcurrentindex.result())

    def getVolume(self):
        return int(self.dcop_volume.result())

    def timeLeft(self):
        cur =   int(self.dcop_trackcurrenttime.result())
        total = int(self.dcop_tracktotaltime.result())
        return total - cur

    def controlsEnabled(self):
        return self.allowControl

class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    """We need our own 'RequestHandler, to handle the requests, that arrive at
    our server."""
    
    def _amarokPlay(self):
        AmarokStatus.playState = AmarokStatus.EnginePlay
        Globals._dcopCallPlayer("play")

    def _amarokPause(self):
        AmarokStatus.playState = AmarokStatus.EnginePause
        Globals._dcopCallPlayer("pause")

    def _amarokNext(self):
        Globals._dcopCallPlayer("next")

    def _amarokGoto(self,index):
        AmarokStatus.playState = AmarokStatus.EnginePlay
        AmarokStatus.currentTrackIndex = int(index)
        Globals._dcopCallPlaylistArg("playByIndex",index)

    def _amarokPrev(self):
        Globals._dcopCallPlayer("prev")

    def _amarokStop(self):
        Globals._dcopCallPlayer("stop")
        AmarokStatus.playState = AmarokStatus.EngineStop
        
    def _amarokSetVolume(self, val):
        Globals._dcopCallPlayerArg("setVolume",val)

    def _parseQueryVars(self):
        querystr = self.path.split("?")

        qmap = {}

        if len(querystr) <= 1:
            return qmap

        queries = querystr[-1].split("&");

        for query in queries:
            var = query.split("=")
            if len(var) != 2:
                continue
            qmap[var[0]] = var[1]

        return qmap

    def _handleAction(self, qmap):
        global REQ_IDS

        # get the sessions last reqid
        try:
            req_id = REQ_IDS[qmap["sesid"]]
        except:
            return 0
        
        # abort a request that has already been completed
        # probably a refresh from the users browser
        if qmap.has_key("reqid") and req_id == int(qmap["reqid"]):
            return 0

        if qmap.has_key("action"):
            a = qmap["action"]
            if a == "stop":
                self._amarokStop()
            elif a == "play":
                self._amarokPlay()
            elif a == "pause":
                self._amarokPause()
            elif a == "prev":
                self._amarokPrev()
            elif a == "next":
                self._amarokNext()
            elif a == "goto":
                self._amarokGoto(qmap["value"])
            elif a == "setvolume":
                self._amarokSetVolume(qmap["value"])
        return 1
    
    def _amarokStatus(self):
        status = AmarokStatus()
        return status
        
    def _sendFile(self, path):
        # only allow doc root dir access
        elem = self.path.split("/")
        if len(elem):
            path = elem[-1]
            f = open(Globals.EXEC_PATH + "/" + path, 'r')
            self.copyfile(f, self.wfile)

    def do_HEAD(self):
        """Serve a HEAD request."""
        RequestHandler.extensions_map.update({
            '': 'application/octet-stream', # Default
            '.png': 'image/png',
            '.js': 'text/plain',
            '.css': 'text/plain'
            })
        f = self.send_head()
        if f:
            f.close()

    def _getSessionInfo(self, qmap):
        # get the sessions last reqid
        last_req_id = 0
        session_id = None
        if qmap.has_key("sesid"):
            session_id = qmap["sesid"]
            if REQ_IDS.has_key(session_id):
                last_req_id = REQ_IDS[session_id]
            else:
                REQ_IDS[session_id] = last_req_id
        else:
            # Create a session 
            session_id = str(time.time())
            REQ_IDS[session_id] = last_req_id
        return session_id, last_req_id
   
    def do_GET(self):
        """Overwrite the do_GET-method of the super class."""
        RequestHandler.extensions_map.update({
            '': 'application/octet-stream', # Default
            '.png': 'image/png',
            '.js': 'text/plain',
            '.css': 'text/plain'
            })

        global REQ_IDS

        qmap = self._parseQueryVars()
        session_id, last_req_id = self._getSessionInfo(qmap)
            
        if AmarokStatus.allowControl and self._handleAction(qmap):
            last_req_id = last_req_id + 1
            REQ_IDS[session_id] = last_req_id
        
        newreqid = last_req_id + 1
        
        #
        # Surely there must be a better way that this:)
        #
        self.send_response(200)
        if string.find(self.path, ".png") >= 0:
            self.send_header("content-type","image/png")
            self.end_headers()
            self._sendFile(self.path)
        elif string.find(self.path, ".js") >= 0:
            self.send_header("content-type","text/plain")
            self.end_headers()
            self._sendFile(self.path)
        elif string.find(self.path, ".css") >= 0:
            self.send_header("content-type","text/css")
            self.end_headers()
            self._sendFile(self.path)
        else:
            status = self._amarokStatus()
            status.dcop_volume.init()
            status.dcop_trackcurrenttime.init()
            status.dcop_tracktotaltime.init()
            self.send_header("content-type","text/html")
            self.send_header("Cache-Control","no-cache")
            self.end_headers()
            status.reqid = newreqid
            status.sesid = session_id
            self.wfile.write(PLIST.toHtml(status))

def main():
    """main is the starting-point for our script."""
    global PLIST
    PLIST = Playlist()
    srv = BaseHTTPServer.HTTPServer(('',Globals.PORT),RequestHandler)
    srv.serve_forever()


if __name__ == "__main__":
    main()

