#
# Author: Gregory Fleischer (gfleischer@gmail.com)
#
# Copyright (c) 2012 Gregory Fleischer
#
# This file is part of WTFY.
#
# WTFY is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# WTFY is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with WTFY.  If not, see <http://www.gnu.org/licenses/>.
#
import struct
import socket
import select
import traceback
import sys
import uuid
import hmac
import hashlib
from database.dbinterface import dbinterface
from Config import Config
from WebApplication import WebApplication

NOLINGER = struct.pack('ii', 1, 0)

class Framework():
    def __init__(self):
        self.db = None
        self.config = Config(self)
        self.configuration = {}
        self.webapp = None

    def version(self):
        return '0.0.1-alpha'

    def web_application(self):
        if self.webapp is None:
            self.webapp = WebApplication(self)
        return self.webapp

    def get_db(self):
        return self.db

    def debug(self, source, msg, *args):
        sys.stderr.write('%s: %s (%s)\n' % (source, msg, args))

    def warn(self, msg):
        sys.stderr.write('%s\n' % (msg))

    def log_exception(self, ex):
        sys.stderr.write('FIX ME! ERROR: %s\n' % (traceback.format_exc(ex)))

    def read_config(self, config_filename):
        self.config.read(config_filename)

    def get_config(self, name):
        return self.configuration[name]

    def set_config(self, name, value):
        self.configuration[name] = value

    def make_socket(self, family, sock_type, proto):
        sock = socket.socket(family, sock_type, 0)
        sock.setblocking(0)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, NOLINGER)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        if socket.SOCK_STREAM == sock_type:
            sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
        return sock
        
    def make_tcp_server_socket(self, port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setblocking(0)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, NOLINGER)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
        sock.bind(('', port))
        sock.listen(128)
        return sock

    def make_udp_server_socket(self, port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.setblocking(0)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('', port))
        return sock

    def hmac_key(self):
        # TODO: determine if this best approach
        return self.unique_id

    def generate_tracking_id(self):
        tracking_id = uuid.uuid4().hex
        h = hmac.new(self.hmac_key(), digestmod = hashlib.md5) # MD5 is okay
        h.update(tracking_id)
        tracking_digest = h.hexdigest()
        return ('%s.%s' % (tracking_id, tracking_digest), tracking_id, tracking_digest)

    def extract_tracking_data(self, tracking_data):
        if ':' in tracking_data:
            versioning, tracking_data = tracking_data.split(':', 1)
        else:
            versioning = None
        if '.' in tracking_data:
            tracking_id, tracking_digest = tracking_data.split('.', 1)
            h = hmac.new(self.hmac_key(), digestmod = hashlib.md5) # MD5 is okay
            h.update(tracking_id)
            if h.hexdigest() == tracking_digest:
                self.debug('framework', 'extracted tracking_id', tracking_id, tracking_digest)
                return tracking_id, tracking_digest, versioning
            else:
                self.debug('framework', 'bad digest', tracking_data)
                return None, None, None
        else:
            self.debug('framework', 'bad tracking', tracking_data)
            return None, None, None

    def initialize_db(self):
        self.db = dbinterface.create_instance(self)
        self.db.initialize()
        self.unique_id = self.db.get_unique_id()

    def close_db(self):
        self.db.close()
        self.db = None

    def get_flash_crossdomain_policy(self):
        return '''<?xml version="1.0"?>
<cross-domain-policy> 
   <site-control permitted-cross-domain-policies="master-only"/>
   <allow-access-from domain="*" to-ports="*" />
</cross-domain-policy>'''

    def get_flash_crossdomain_xml(self):
        return '''<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
<allow-http-request-headers-from domain="*" headers="*" />
</cross-domain-policy>
'''
    def get_java_crossdomain_xml(self):
        return '''<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*" />
</cross-domain-policy>
'''

    def get_silverlight_clientaccess_policy(self):
        return '''<?xml version="1.0" encoding ="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-methods="*" http-request-headers="*">
        <domain uri="*" />
	<domain uri="http://*" />
	<domain uri="https://*" />
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true" />
      </grant-to>
    </policy>
    <policy>
      <allow-from>
        <domain uri="*" />
      </allow-from>
      <grant-to>
        <socket-resource port="4502-4534" protocol="tcp" />
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>
'''
