import sys,os
from twisted.web import server, http, resource, xmlrpc
from twisted.internet import reactor, defer, task
from index import *
from utils import *
waitFor = defer.waitForDeferred

VERSION = "$Revision$"

# Fields to display in the search query box
SEARCHFIELDS = [("torrentname", "Torrent name"),
                ("filename", "File name")]

# Rendering functions: these map a Metadatum into a HTML list item to
# display in the results box.

@typechecked(Metadatum)
def renderMetadatum(x):
    return "<LI><B>%s</B>: %s</LI>" % (x.getKey(), x.getValue())

@typechecked(Metadatum)
def renderMagnetURI(x):
    return """<LI><A HREF="%s">Download</A></LI>""" % (x.getValue())

# Mapping of different types of Metadatum keys to the appropriate
# renderer and a priority value. The resulting output is sorted by the
# priority value to determine the display order.
DISPLAYFIELDS = {"magnet" : (renderMagnetURI, 1)}
# Default renderer and priority if none is specified
DISPLAYFIELDS_DEFAULT = ((renderMetadatum, 0))


@typechecked(Metadata)
def metadataToHTML(md):
    """Render a Metadata block in HTML format: a list of key/value
    pairs."""

    html = "<UL>"
    items = []
    for x in md.metadata.values():
        try:
            (renderer, pri) = DISPLAYFIELDS[x.getKey()]
        except KeyError:
            (renderer, pri) = DISPLAYFIELDS_DEFAULT
        items.append((pri, renderer(x)))
    items.sort()
    
    html += "\n".join([x[1] for x in items])
    html += "</UL>"
    return html
        

class XMLRPCInterface(xmlrpc.XMLRPC):
    def __init__(self, indexServer):
        self.indexServer = indexServer

    def unXMLRPCizeMetadata(self, md):
        """Unmarshal metadata from its XMLRPC dictionary form to a
        real Metadata object."""
        r = Metadata()
        for k,v in md.items():
            r.setMetadatum(Metadatum(k, v[0], v[1]))
        return r

    def xmlrpc_version(self):
        """Return the version of arpd (svn revision number)."""
        return int(VERSION.split()[1])

    def xmlrpc_addMetadata(self, mdTable):
        """Recursively add metadata to the indexes."""
        print "Adding metadata", mdTable
        # Convert to metadata
        md = self.unXMLRPCizeMetadata(mdTable)
        return self.indexServer.remote_recursiveAddWithIG(md).addCallback(
            lambda x: float(x))
        
class SearchServer(server.Site):
    """A web interface to the Arpeggio search system. Specifically,
    this class implements a web server that can perform searches and
    display the results."""

    @staticmethod
    def searchForm():
        """Return the HTML code for a form that allows the user to
        enter keywords for a search."""
        r = """<form action="/search" method="post">"""
        
        f = ["""%s: <input type="text" name="%s" size=40</input><br>""" %
             (verbosename,fieldname)
             for fieldname,verbosename in SEARCHFIELDS]

        r += "\n".join(f)
        r += """
             <input type="submit">
             </form>
             """
        return r

    
    class TopLevel(resource.Resource):
        """Top-level page."""
        def render(self, request):
            r = """
            <html><head><title>Arpeggio</title></head>
            <body>
            <h1>Arpeggio</h1>
            <h2>Search</h2>
            """
            r += SearchServer.searchForm()
            r += """
            </body></html>
            """

            return r

    class DebugShowArgs(resource.Resource):
        """Debugging tool that shows arguments to a request."""
        def render(self, request):
            request.write("<html><body>")
            for key, values in request.args.items():
                request.write("<h2>%s</h2>" % key)
                request.write("<ul>")
                for value in values:
                    request.write("<li>%s</li>" % value)
                request.write("</ul>")
            request.write("</body></html>")
            return ""

    class Search(resource.Resource):
        """Search form."""

        def __init__(self, indexServer):
            self.indexServer = indexServer

        @defer.deferredGenerator
        def runSearch(self, request):
            q = Query()
            for key, values in request.args.items():
                if len(values) == 1:
                    q.addKeyword(key, values[0])
                else:
                    print "Invalid values for key %s" % key
                    
            try:
                d = waitFor(self.indexServer.remote_recursiveSearch(q))
                yield d
                res = d.getResult()
                
                    
                request.write("<html><body>")
                request.write("<h1>Arpeggio</h1>")
                request.write("<h2>%d result%s found</h2>" % (len(res),
                                                              If(len(res)==1,
                                                                 "", "s")))
                for x in res:
                    request.write("<hr>")
                    request.write(metadataToHTML(x))

                request.write("<hr>")
                request.write("<h2>Perform another search</h2>")
                request.write(SearchServer.searchForm())
                request.write("</body></html>")
                request.finish()
                return
            except Exception, e:
                print e
                request.write("error!")
                request.write(str(e))
                request.finish()
            
        def render(self, request):
            self.runSearch(request)
            return server.NOT_DONE_YET

    def __init__(self, indexServer):
        self.indexServer = indexServer

        self.root = resource.Resource()
        server.Site.__init__(self, self.root)
        self.root.putChild("", self.TopLevel())
        self.root.putChild("search", self.Search(self.indexServer))
        self.root.putChild("RPC2", XMLRPCInterface(indexServer))
