#!/usr/bin/python
# coding=utf8

import sys
import os
from cgi import parse_qs
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from urllib import unquote_plus, unquote, quote
from ConfigParser import ConfigParser
from opendir import opendir
from xml.sax import saxutils
from shutil import copyfileobj
from bsddb import rnopen
from getopt import getopt
from random import randint
from sphinxsearch import SphinxClient, SPH_MATCH_ANY
from mako.lookup import TemplateLookup
from mako import exceptions
from mimetypes import guess_type

class Config(object):
    data = None
    
    def __getitem__(self, k):
        return Config.data.get(k)
    
    def keys(self):
        return Config.data.keys()
    
    def __init__(self):
        if Config.data is None:
            config_file = "mokopedia.cfg"
            
            opts, args = getopt(sys.argv[1:], "c:", ["config=",])
            for o, a in opts:
                if o in ("-c", "--config"):
                    if os.path.exists(a):
                        config_file = a
            
            config = ConfigParser()
            try:
                if not config.read(config_file):
                    raise
            except:
                sys.exit("config file not valid")
            
            sphinx_port = 3312
            Config.data = {}
            class Dummy(object):
                pass
            
            Config.data["main"] = Dummy()
            Config.data["main"].__dict__["iw"] = []
            
            for section in config.sections():
                if "main" not in section and config.getint(section, "enabled"):
                    Config.data[section] = Dummy()
                    Config.data["main"].__dict__["iw"].append(section)
                    
                    for (name, value) in config.items(section):
                        Config.data[section].__dict__[name] = value
                    
                    Config.data[section].__dict__["lookup"] = TemplateLookup(directories=[os.path.join(section, "templates")], module_directory=os.path.join(section, "modules"), input_encoding='utf-8', output_encoding='utf-8')
                    
                    db = config.get(section, "title_index")
                    if db:
                        print "opening titles bsddb..."
                        Config.data[section].__dict__["titles"] = rnopen(db)
                    
                    if config.get(section, "title_search") or config.get(section, "fulltext_search"):
                        print "starting sphinx daemon for %s wiki..."%section
                        print "-----------------------------"
                        L = ['searchd', '--port', '%s'%sphinx_port, '--config', '%s/sphinx.conf'%section, '--console']
                        os.spawnvp(os.P_NOWAIT, 'searchd', L)
                        print "-----------------------------"
                        print "successfully started"
                        
                        Config.data[section].__dict__["sphinx"] = SphinxClient()
                        Config.data[section].__dict__["sphinx"].SetServer ( "localhost", sphinx_port )
                        Config.data[section].__dict__["sphinx"].SetMatchMode ( SPH_MATCH_ANY )
                        sphinx_port+=1
                elif "main" in section:
                    for (name, value) in config.items(section):
                        Config.data[section].__dict__[name] = value
    
class MyHandler(BaseHTTPRequestHandler):
    
    def output_wiki_page(self, lang, article):
        try:
            file = open(os.path.join(self.config["main"].dump_folder, lang, article), "r")
        except IOError:
            self.output_error_page(lang, article)
        else:
            self.write_header()
            template = self.config[lang].lookup.get_template("header.mako")
            self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8"), title=unicode(file.readline(), "utf8"), iw=self.config["main"].iw))
            copyfileobj(file, self.wfile )
            template = self.config[lang].lookup.get_template("footer.mako")
            self.wfile.write(template.render())
        
    def output_category_page(self, lang, article, page):
        folder = os.path.join(self.config["main"].dump_folder, lang, article)
        try:
            index = open(os.path.join(folder, "index"), "r")
        except IOError:
            self.output_error_page(lang, article)
        else:
            d = opendir(folder)
            
            #seek to page if necessary
            if page > 1:
                for i in xrange(int(self.config["main"].category_size)*(page-1)):
                    d.read()
            
            last_page = False
            
            items = []
            
            for i in xrange(int(self.config["main"].category_size)):
                filename = d.read()
                if filename:
                    if filename not in [".","..","index"]:
                        items.append(
                            (unicode(saxutils.escape(quote(os.readlink(os.path.join(folder,filename))[3:])), "utf8"),
                            unicode(saxutils.escape(filename), "utf8"))
                        )
                else:
                    last_page = True
                    break
            
            self.write_header()
            template = self.config[lang].lookup.get_template("category.mako")
            self.wfile.write(template.render(
                article=unicode(article, "utf8"),
                title=unicode(index.readline(), "utf8"),
                items=items,
                last_page=last_page,
                page=page,
                article=unicode(article, "utf8"),
                text=unicode(index.read(), "utf8"),
                iw=self.config["main"].iw,
            ))
            
    def output_result_page(self, lang, text, page):
        last_page = False
        
        page_size = int(self.config["main"].result_size)
        
        self.config[lang].sphinx.SetLimits ( page_size*(page-1), page_size)
        res = self.config[lang].sphinx.Query( text )
        
        items = []
        
        for match in res['matches']:
            article = unicode(saxutils.escape(self.config[lang].titles[int(match['id'])]), "utf8")
            items.append((article, article.replace("_", " ")))
        
        if res['total_found']/page_size < page:
            last_page = True
        
        if res['total_found'] is 0:
            page = 0
        
        self.write_header()
        template = self.config[lang].lookup.get_template("result.mako")
        self.wfile.write(template.render(
            items=items,
            last_page=last_page,
            page=page,
            text=unicode(saxutils.escape(text), "utf8"),
            iw=self.config["main"].iw,
        ))
    
    def output_error_page(self, lang, article):
        self.send_response(404)
        self.send_header('Content-type', 'application/xml')
        self.end_headers()
        template = self.config[lang].lookup.get_template("error.mako")
        self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8"), iw=self.config["main"].iw))
        
    def output_language_chooser(self):
        self.write_header()
        self.wfile.write('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head> <body>')
        for i in self.config.keys():
            if "main" not in i:
                self.wfile.write('<a href="/%s">%s</a><br />'%(i,i))
        self.wfile.write('</body></html>')
        
    def output_language_index(self, lang):
        self.write_header()
        template = self.config[lang].lookup.get_template("index.mako")
        self.wfile.write(template.render(iw=self.config["main"].iw))
        
    def output_image_redirect(self, lang, article):
        self.write_header()
        template = self.config[lang].lookup.get_template("image_redirect.mako")
        self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8")))
        
    def output_random_redirect(self, lang):
        article = self.config[lang].titles[randint(1,len(self.config[lang].titles))]
        self.write_header()
        template = self.config[lang].lookup.get_template("random_redirect.mako")
        self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8")))
        
    def output_title_redirect(self, lang, article):
        article = article.replace(' ', '_')
        self.write_header()
        template = self.config[lang].lookup.get_template("title_redirect.mako")
        self.wfile.write(template.render(article=unicode(saxutils.escape(article), "utf8")))
            
    def output_raw(self, lang, file):
        print os.path.join(lang, file)
        if os.path.exists(os.path.join(lang, file)):
            self.send_response(200)
            self.send_header('Content-type', guess_type(file))
            self.end_headers()
            copyfileobj(open(os.path.join(lang, file)), self.wfile)
        else:
            self.output_error_page(lang, file)
    
    def write_header(self):
        #wos.stat = os.fos.stat(file.fileno())
        self.send_response(200)
        self.send_header('Content-type', 'application/xml')
        #self.send_header("Content-Length", str(wos.stat.st_size + os.stat("de/os.static/header").st_size + os.stat("de/os.static/middle").st_size + os.stat("de/os.static/footer").st_size))
        #self.send_header("Last-Modified", self.date_time_string(wos.stat.st_mtime))
        self.end_headers()
        
    def do_GET(self):
        self.config = Config()
        
        i = self.path.rfind('?')
        text = ''
        search = ''
        page = 1
        if i >= 0:
            self.path, query = self.path[:i], self.path[i+1:]
            if query:
                dict = parse_qs(query)
                if "page" in dict.keys():
                    page = int(dict["page"][0])
                if "text" in dict.keys():
                    text = dict["text"][0]
                    search = dict.get("search", "")
        
        addr = [ unquote(i) for i in self.path.split('/') if i ]
        
        if len(addr) > 0 and addr[0] in self.config.keys() and "main" not in addr[0]:
            lang = addr[0]
            if len(addr) > 1:
                addr[1] = '/'.join(addr[1:])
                if addr[1].startswith(self.config[lang].special+":"):
                    action = addr[1].split(':', 1)[1]
                    if action == "Random":
                        self.output_random_redirect(lang=lang)
                    else:
                        self.output_error_page(lang=lang, article=addr[1])
                elif addr[1].startswith(self.config[lang].image+":"):
                    self.output_image_redirect(lang=lang, article=addr[1])
                elif addr[1].startswith(self.config[lang].category+":"):
                    self.output_category_page(lang=lang, article=addr[1], page=page)
                elif addr[1].startswith("static"):
                    self.output_raw(lang=lang, file=addr[1])
                else:
                    self.output_wiki_page(lang=lang, article=addr[1])
            else:
                if text:
                    if search and (self.config[lang].title_search or self.config[lang].fulltext_search):
                        self.output_result_page(lang, text, page)
                    else:
                        self.output_title_redirect(lang, text)
                else:
                    self.output_language_index(lang=lang)
        else:
            self.output_language_chooser()

def main():
    config = Config()
    try:
        server = HTTPServer(('', int(config["main"].port)), MyHandler)
        print 'httpserver started'
        server.serve_forever()
    except KeyboardInterrupt:
        print '^C received, shutting down server'
        server.socket.close()

if __name__ == '__main__':
    main()
