from twisted.web import resource, static
import cgi, logging
from zope.interface import implements
from twisted.cred import portal, checkers, credentials
from nevow import inevow, rend, tags, guard, loaders
import auth
from lang import lang 
import os, sys
from twisted.python import reflect
#from Database import utils as dbutils


log = logging.getLogger("Webgui")

curdir=os.path.abspath(os.path.dirname(sys.argv[0]))
admin_html_path = curdir+'/Web/html/Admin/'
user_html_path = curdir+'/Web/html/User/'
global_html_path = curdir+'/Web/html/'
guest_html_path = curdir+'/Web/html/Guest/'

import struct
import zlib

class GzipRequest(object):
    """Wrapper for a request that applies a gzip content encoding"""

    def __init__(self, request, compressLevel=6):
        self.request = request
        self.request.setHeader('Content-Encoding', 'gzip')
        # Borrowed from twisted.web2 gzip filter
        self.compress = zlib.compressobj(compressLevel, zlib.DEFLATED,
                                         -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL,0)

    def __getattr__(self, attr):
        if 'request' in self.__dict__:
            return getattr(self.request, attr)
        else:
            raise AttributeError, attr

    def __setattr__(self, attr, value):
        if 'request' in self.__dict__:
            return setattr(self.request, attr, value)
        else:
            self.__dict__[attr] = value

    def write(self, data):
        if not self.request.startedWriting:
            self.crc = zlib.crc32('')
            self.size = self.csize = 0
            # XXX: Zap any length for now since we don't know final size
            if 'content-length' in self.request.headers:
                del self.request.headers['content-length']
            # Borrow header information from twisted.web2 gzip filter
            self.request.write('\037\213\010\000' '\0\0\0\0' '\002\377')

        self.crc = zlib.crc32(data, self.crc)
        self.size += len(data)
        cdata = self.compress.compress(data)
        self.csize += len(cdata)
        if cdata:
            self.request.write(cdata)
        elif self.request.producer:
            # Simulate another pull even though it hasn't really made it
            # out to the consumer yet.
            self.request.producer.resumeProducing()

    def finish(self):
        remain = self.compress.flush()
        self.csize += len(remain)
        if remain:
            self.request.write(remain)
        self.request.write(struct.pack('<LL',
                                       self.crc & 0xFFFFFFFFL,
                                       self.size & 0xFFFFFFFFL))
        self.request.finish()



class StaticFile(static.File):

   def render(self, request):
      accept_encoding = request.getHeader('accept-encoding')
      if accept_encoding:
         encodings = accept_encoding.split(',')
         for encoding in encodings:
            name = encoding.split(';')[0].strip()
            if name == 'gzip':
               request = GzipRequest(request)
               break
      return static.File.render(self, request)



def render_trans(self, ctx, data):
   return data

def data_trans(self, ctx, data):
   l = lang.deflang
   if 'lang' in ctx.locate(inevow.IRequest).args:
      l = ctx.locate(inevow.IRequest).args['lang'][-1]
   return lang.webstring(ctx.tag.children[0], l)


class Translator:

   def translate(self, ctx, what):
      l = lang.deflang
      if 'lang' in ctx.locate(inevow.IRequest).args:
         l = ctx.locate(inevow.IRequest).args['lang'][-1]
      return lang.webstring(what, l)
      


class MyFragment(rend.Fragment):

   render_translator = render_trans
   data_translator = data_trans


class GenericFormalResponsePage(rend.Page):

   addSlash=True

   def __init__(self, data, parent):
      self.data = data
      self.avatarId = parent.avatarId
      self.render_HEAD = parent.render_HEAD
      self.render_HEADER = parent.render_HEADER
      self.render_FOOTER = parent.render_FOOTER
      self.data_translator = parent.data_translator
      self.render_translator = parent.render_translator
      rend.Page.__init__(self)




class permissionDenied(rend.Page):

   addSlash = True

   def renderHTTP( self, ctx):
      log.debug("Rendering Permission Denied")
      request = inevow.IRequest(ctx)
      request.setHeader('Location', '/')
      request.setResponseCode(302)
      html = '<html><head><title>SkyLive Daemon</title></head>'
      html = html+"<b>i can't find the requested page  or "
      html = html+" you don't have permission to view this page!</b></br>"
      html = html+'</body></html>'
      return html

   def childFactory(self, ctx, data):
      return self


class Header(MyFragment):

   docFactory = loaders.xmlfile('head.html', templateDir=global_html_path)

class Footer(MyFragment):

   docFactory = loaders.xmlfile('foot.html', templateDir=global_html_path)

class AdminHeader(MyFragment):

   docFactory = loaders.xmlfile('head.html', templateDir=admin_html_path)

class AdminFooter(MyFragment):

   docFactory = loaders.xmlfile('foot.html', templateDir=admin_html_path)

class UserHeader(MyFragment):

   docFactory = loaders.xmlfile('head.html', templateDir=user_html_path)

class UserFooter(MyFragment):

   docFactory = loaders.xmlfile('foot.html', templateDir=user_html_path)

class preBody(MyFragment):

   docFactory = loaders.stan(tags.head[tags.title["Skylive Daemon GUI"],
                                       tags.link(rel='stylesheet', type='text/css',
                                          href='/css/skylived.css'),
                                       tags.link(rel='stylesheet', type='text/css',
                                          href='/css/formal.css')])


class skylivedWebgui(rend.Page):

   addSlash = True
   logged = False
   childvars = {}
   render_translator = render_trans
   data_translator = data_trans
   docFactory = loaders.xmlfile('login.html', templateDir=global_html_path)

   def __init__(self, avatarId=None):
      log.debug("Root page initialized by" + str(avatarId))
      self.curdir=os.path.abspath(os.path.dirname(sys.argv[0]))
      self.admin_html_path = self.curdir+'/Web/Admin/html/'
      self.user_html_path = self.curdir+'/Web/User/html/'
      self.global_html_path = self.curdir+'/Web/html/'
      self.avatarId=avatarId
      rend.Page.__init__(self)
      self.child_css = StaticFile(self.curdir+'/Web/css/')
      self.child_img = StaticFile(self.curdir+'/Web/img/')
      self.child_js = StaticFile(self.curdir+'/Web/js/')
      self.child_qx = StaticFile(self.curdir+'/Web/qx/')
      #self.child_favicon.ico = static.File(self.curdir+'/Web/img/favicon.ico')
      self.putChild('favicon.ico', static.File(self.curdir+'/Web/img/favicon.ico'))


   def render_isLogged(self, context, data):
      log.debug("Called render_isLogged by" + str(self.avatarId))
      q = inevow.IQ(context)
      user_pattern = q.onePattern('User')
      admin_pattern = q.onePattern('Admin')
      false_pattern = q.onePattern('False')
      if self.avatarId: 
         self.logged = True
         log.debug('Utente Loggato regolarmente')
         if self.is_admin:
            log.debug("Render called by an admin")
            return admin_pattern or context.tag().clear()
         return user_pattern or context.tag().clear()
      else: 
         self.logged = False
         sess = inevow.ISession(context)
         self.sess = sess
         log.debug('Sessione Web: '+str(sess.uid)+str(sess))
         log.debug('Utente non loggato')
         return false_pattern or context.tag().clear()

   def render_sessionId(self, context, data):
      sess = inevow.ISession(context)
      self.sess = sess
      log.debug('Sessione Web: '+str(sess.uid)+str(sess)+str(self.avatarId))
      return context.tag[sess.uid]

   def render_username(self, context, data):
      if self.logged:
         return context.tag[self.avatarId]
      return context.tag['Unknown']

   def render_numusers(self, context, data):
      return '1'

   def render_logged_telescopes(self, context, data):
      return '6'

   def render_active_telescopes(self, context, data):
      return '2'


   def render_HEADER(self, ctx, data):
      if self.logged:
         return self.childHeader(ctx, data)
      return Header()

   def render_FOOTER(self, ctx, data):
      if self.logged:
         return self.childFooter(ctx, data)
      return Footer()

   def render_HEAD(self, ctx, data):
      return preBody()

   def childHeader(self, ctx, data):
      if self.is_admin:
         return AdminHeader()
      if self.logged:
         return UserHeader()
      return Header()

   def childFooter(self, ctx, data):
      if self.is_admin:
         return AdminFooter()
      if self.logged:
         return UserFooter()
      return Footer()
   
   def childPermissionDenied(self):
      return permissionDenied()

   def logout(self):
      log.debug("Called logout by" + str(self.avatarId))
      self.logged = False
      print "Bye"

   def not_logged(self, context):
      log.debug("Called not_logged by" + str(self.avatarId))
      q = inevow.IQ(context)
      false_pattern = q.onePattern('False')
      return false_pattern or context.tag().clear()


   def childFactory(self, ctx, name):
      log.debug("Child Context: "+str(ctx))
      if self.logged and self.is_admin and str(name) != 'guest':
         pname = 'Web.WAdmin.'+str(name)
         log.debug("Trying to find admin page called "+str(pname))
         try:
         #if True:
            f = reflect.namedAny(pname)
            log.debug("Admin child Found")
            return self.onChildCall(ctx, f)
         #try:
         #   oiwj=oijwhef
         except:
            pname = 'Web.WUsers.'+str(name)
            #try:
            if True:
               #f = __import__(pname, globals(), locals(), ['Page'])
               f = reflect.namedAny(pname)
               log.debug("Child found")
               return self.onChildCall(ctx, f)
            #except:
            #   log.debug("No child found")
      elif self.logged and str(name) != 'guest' and not self.is_admin:
         pname = 'Web.WUsers.'+str(name)
         try:
            f = reflect.namedAny(pname)
            return self.onChildCall(ctx, f)
         except:
            log.debug("No child found")
      elif str(name) == 'guest':
         pname = 'Web.WGuest.'+str(name)
         try:
         #if True:
            f = reflect.namedAny(pname)
            return self.onChildCall(ctx, f)

         except:
            log.debug("No guest child found (%s)" % pname)
      # if isn't already returned, can only be a shared resource to all users
      # both admin, non admin and guests!
      pname = 'Web.WShared.'+str(name)
      try:
      #if True:
         f = reflect.namedAny(pname)
         return self.onChildCall(ctx, f)
      except:
         log.debug("No shared child found (%s)" % pname)

      # nothing found, return a permission denied!
      log.debug("Permission Denied")
      return permissionDenied()

   def ChildAddCommons(self, child):
      log.debug("Adding common calls to the child")
      child.render_HEADER = self.childHeader
      child.render_FOOTER = self.childFooter
      child.render_HEAD = self.render_HEAD
      child.data_translator = self.data_translator
      child.render_translator = self.render_translator
      child.command = self.command
      child.parent = self
      child.perms = self.perms
      try:
         child.sess = self.sess
      except:
         child.sess = None
      child.avatarId = self.avatarId
      child.childPermissionDenied = self.childPermissionDenied
      if self.is_admin:
         child.template_path = admin_html_path
      elif self.logged and not self.is_admin:
         child.template_path = user_html_path
      else:
         child.template_path = guest_html_path
      return child

   def onChildCall(self, ctx, page):
      p = page.Page(self.avatarId)
      p = self.ChildAddCommons(p)
      return p


### Authentication
def noLogout():
   return None


class MyRealm:
   """A simple implementor of cred's IRealm.
      For web, this gives us the LoggedIn page.
   """
   implements(portal.IRealm)

   def requestAvatar(self, authres, mind, *interfaces):
      try:
         avatarId = authres[0].username
         perms = authres[1]
         is_admin = authres[1]['general']['admin']
      except:
         avatarId = authres
         perms = {}
         is_admin = False

      log.debug("Avatar is: "+str(avatarId)+' and mind + interfaces '+str(mind)+' '+str(interfaces))
      for iface in interfaces:
         if iface is inevow.IResource:
            # do web stuff
            if avatarId is checkers.ANONYMOUS:
               resc = skylivedWebgui()
               resc.realm = self
               resc.command = self.command
               resc.perms = perms
               resc.is_admin = is_admin
               return (inevow.IResource, resc, noLogout)
            else:
               resc = skylivedWebgui(avatarId)
               resc.realm = self
               resc.command = self.command
               resc.perms = perms
               resc.is_admin = is_admin
               return (inevow.IResource, resc, resc.logout)

      raise NotImplementedError("Can't support that interface.")


### Application setup

def createResource(command):
   realm = MyRealm()
   realm.command = command
   porta = portal.Portal(realm)

   # XXX Spostare nel conf ( ma c'e' gia' in quello del chatserver ) l'uri!
   #myChecker = auth.clientAuth('http://www.skylive.it/forum5/checkuserid.asp')
   myChecker = auth.DBClientAuth()
   porta.registerChecker(checkers.AllowAnonymousAccess(), credentials.IAnonymous)
   porta.registerChecker(myChecker)
   res = guard.SessionWrapper(porta)

   return res

