#!/usr/bin/env python
#coding:utf-8
import sys,os
from twisted.internet import reactor
from twisted.web import server, wsgi
from twisted.python.logfile import DailyLogFile
from bottle import run as runserver
from toughradius.console.admin.admin import app as mainapp
from toughradius.console.admin.ops import app as ops_app
from toughradius.console.admin.business import app as bus_app
from toughradius.console.admin.card import app as card_app
from toughradius.console.admin.product import app as product_app
from toughradius.console.base import *
from toughradius.console.libs import sqla_plugin,utils
from toughradius.console.libs.smail import mail
from toughradius.console.websock import websock
from toughradius.console import tasks
from toughradius.console import models
from toughradius.console import base
from toughradius.tools.dbengine import get_engine
import toughradius
import bottle
import functools
import time

reactor.suggestThreadPoolSize(30)


###############################################################################
# run server                                                                 
###############################################################################

class AdminServer(object):
    
    def __init__(self,config,db_engine=None,daemon=False,app=None,subapps=[]):
        self.config = config
        self.app = app
        self.subapps = subapps
        self.db_engine = db_engine
        self.daemon = daemon
        self.viewpath = os.path.join(os.path.split(__file__)[0],"admin/views/")
        self.init_config()
        self.init_timezone()
        self.init_db_engine()
        self.init_application()
        self.init_websock()
        self.init_mail()
        self.init_tasks()
        self.init_protocol()
    
    def init_config(self):
        self.logfile = self.config.get('admin','logfile')
        self.standalone = self.config.has_option('DEFAULT','standalone') and \
            self.config.getboolean('DEFAULT','standalone') or False
        self.secret = self.config.get('DEFAULT','secret')
        self.timezone = self.config.has_option('DEFAULT','tz') and self.config.get('DEFAULT','tz') or "CST-8"
        self.debug = self.config.getboolean('DEFAULT','debug')
        self.port = self.config.getint('admin','port')
        self.admin_host = self.config.has_option('admin','host') \
            and self.config.get('admin','host') or  '0.0.0.0'
        bottle.debug(self.debug)
        base.scookie.setup(self.secret)
        # update aescipher
        utils.aescipher.setup(self.secret)
        self.encrypt = utils.aescipher.encrypt
        self.decrypt = utils.aescipher.decrypt
        #parse ssl
        self._check_ssl_config()
        
    def _check_ssl_config(self):
        self.use_ssl = False
        self.privatekey = None
        self.certificate = None
        if self.config.has_option('DEFAULT','ssl') and self.config.getboolean('DEFAULT','ssl'):
            self.privatekey = self.config.get('DEFAULT','privatekey')
            self.certificate = self.config.get('DEFAULT','certificate')
            if os.path.exists(self.privatekey) and os.path.exists(self.certificate):
                self.use_ssl = True
                
    def init_timezone(self):
        try:
            os.environ["TZ"] = self.timezone
            time.tzset()
        except:pass
    
    def init_db_engine(self):
        if not self.db_engine:
            self.db_engine = get_engine(self.config)
        metadata = models.get_metadata(self.db_engine)
        self.sqla_pg = sqla_plugin.Plugin(
            self.db_engine,
            metadata,
            keyword='db',
            create=False,
            commit=False,
            use_kwargs=False
        )
        
    def init_protocol(self):
        self.web_factory = server.Site(wsgi.WSGIResource(reactor, reactor.getThreadPool(), self.app))
        
    
    def _sys_param_value(self,pname):
        with Connect(self.sqla_pg.new_session) as db:
            return get_param_value(db,pname)
     
    def _get_product_name(self,pid):
        with Connect(self.sqla_pg.new_session) as db:
            return get_product_name(db,pid)
            
    def error403(self,error):
        return self.render.render("error",msg=u"Unauthorized access %s"%error.exception)
    
    def error404(self,error):
        return self.render.render("error",msg=u"Not found %s"%error.exception)

    def error500(self,error):
        return self.render.render("error",msg=u"Server Internal error %s"%error.exception)
        
    def init_application(self):
       log.msg("start init application...")
       _lookup = [self.viewpath]
       _context = dict(
           sys_version = toughradius.__version__,
           use_ssl = self.use_ssl,
           get_cookie = get_cookie,
           fen2yuan = utils.fen2yuan,
           fmt_second = utils.fmt_second,
           currdate = utils.get_currdate,
           bps2mbps = utils.bps2mbps,
           mbps2bps = utils.mbps2bps,
           bb2mb = utils.bb2mb,
           bbgb2mb = utils.bbgb2mb,
           kb2mb = utils.kb2mb,
           mb2kb = utils.mb2kb,
           sec2hour = utils.sec2hour,
           request = request,
           sys_param_value = self._sys_param_value,
           get_product_name = self._get_product_name,
           permit = permit,
           all_menus = permit.build_menus(order_cats=[u"系统管理",u"营业管理",u"运维管理"])
       )
       
       self.render = Render(_context,_lookup)
       log.msg("mount app and install plugins...")
       for _app in [self.app]+self.subapps:
           _app.config['render_key'] = 'admin'
           Render.RENDERS['admin'] = self.render
           for _class in ['DEFAULT','database','radiusd','admin','customer']:
               for _key,_val in self.config.items(_class):
                   _app.config['%s.%s'%(_class,_key)] = _val
           _app.error_handler[403] = self.error403
           _app.error_handler[404] = self.error404
           _app.error_handler[500] = self.error500
           _app.install(self.sqla_pg)
           if _app is not self.app:
               self.app.mount(_app.config['__prefix__'],_app)

       log.msg("init operator rules...")
       with Connect(self.sqla_pg.new_session) as session:
           for _super in session.query(models.SlcOperator.operator_name).filter_by(operator_type=0):
               permit.bind_super(_super[0])
    
    def init_mail(self):
        log.msg("init sendmail..")
        mail.setup(
            server=self._sys_param_value('smtp_server'),
            user=self._sys_param_value('smtp_user'),
            pwd=self._sys_param_value('smtp_pwd'),
            fromaddr=self._sys_param_value('smtp_user'),
            sender=self._sys_param_value('smtp_sender')
        )
        
    def init_websock(self):
        # connect radiusd websocket admin port 
        log.msg("init websocket client...")
        websock.use_ssl = self.use_ssl
        wsparam = (
                self._sys_param_value('radiusd_address'),
                self._sys_param_value('radiusd_admin_port')
        )
        reactor.callLater(1, websock.connect,*wsparam)
        
    def init_tasks(self):
        log.msg("init tasks...")
        reactor.callLater(2, tasks.start_online_stat_job, self.sqla_pg.new_session)
        reactor.callLater(3, tasks.start_flow_stat_job, self.sqla_pg.new_session)
        reactor.callLater(4, tasks.start_expire_notify_job, self.sqla_pg.new_session)
    
    def run_normal(self):
        if self.debug:
            log.startLogging(sys.stdout)
        else:
            log.startLogging(DailyLogFile.fromFullPath(self.logfile))
        log.msg('server listen %s'%self.admin_host)
        if self.use_ssl:
            log.msg('Admin SSL Enable!')
            from twisted.internet import ssl
            sslContext = ssl.DefaultOpenSSLContextFactory(self.privatekey, self.certificate)
            reactor.listenSSL(
                self.port,
                self.web_factory,
                contextFactory = sslContext,
                interface=self.admin_host
            )
        else:
            reactor.listenTCP(self.port, self.web_factory,interface=self.admin_host)
        if not self.standalone:
            reactor.run()
        
    def get_service(self):
        from twisted.application import service, internet
        if self.use_ssl:
            log.msg('Admin SSL Enable!')
            from twisted.internet import ssl
            sslContext = ssl.DefaultOpenSSLContextFactory(self.privatekey, self.certificate)
            return internet.SSLServer(
                self.port,
                self.web_factory,
                contextFactory = sslContext,
                interface = self.admin_host
            )
        else: 
            log.msg('Admin SSL Disable!')       
            return internet.TCPServer(self.port,self.web_factory,interface = self.admin_host)    
        

def run(config,db_engine=None,is_service=False):
    print 'running admin server...'
    subapps = [ops_app,bus_app,card_app,product_app]
    admin = AdminServer(config,db_engine,daemon=is_service,app=mainapp,subapps=subapps)
    if is_service:
        return admin.get_service()
    else:
        admin.run_normal()


