import os
import sys
import re

try:
    import lasso
except ImportError:
    print >> sys.stderr, 'Missing Lasso module, IdWsf 2.0 support disabled'
else:
    if not lasso.WSF_SUPPORT:
        print >> sys.stderr, 'Found Lasso module, but IdWsf 2.0 support not enabled'

from quixote import get_publisher, get_session, get_request, get_response, redirect
from quixote.directory import Directory

from qommon.liberty import SOAPException, soap_call
from qommon import template
from qommon.misc import http_get_page

import misc
from form_prefill import FormPrefill
from field_prefill import FieldPrefill

def cleanup_html_value(value):
    # Ensure the field value can be properly integrated in HTML code
    value = value.replace('"', "'")
    # Conversion to iso-8859-1
    try:
        value = unicode(value, 'utf-8').encode('iso-8859-1')
    except UnicodeEncodeError:
        return None

class IdWsf2(Directory):
    _q_exports = []

    def _q_lookup(self, component):
        if not hasattr(get_session(), 'prefill_form'):
            get_session().prefill_form = component
            get_session().after_url = get_request().get_url()
            if get_request().get_query():
                get_session().after_url += '?' + get_request().get_query()
            return redirect('../saml/login')
        else:
            prefill_form = FormPrefill.get(get_session().prefill_form)
            del get_session().prefill_form
            if prefill_form:
                try:
                    response, status, page, auth_header = http_get_page(prefill_form.url)
                except:
                    return template.error_page(_('Failed connecting to the original site.'))
                try:
                    fields = self.do_prefill_form(prefill_form)
                    if not fields:
                        raise lasso.Error
                    for key, value in get_request().get_fields().iteritems():
                        value = cleanup_html_value(value)
                        if value:
                            fields[key] = value
                except lasso.Error:
                    return page + '<script type="text/javascript">alert("%s")</script>' % \
                         _('Failed getting attributes from the attribute provider.')
                except:
                    return page + '<script type="text/javascript">alert("%s")</script>' % \
                         _('Failed getting attributes for an unknown reason.')

                return self.send_prefilled_form(prefill_form, page, fields)

    def do_prefill_form(self, prefill_form):
        server = misc.get_lasso_server(protocol = 'saml2')
        disco = lasso.IdWsf2Discovery(server)
        if not get_session().lasso_session_dumps or not get_session().lasso_session_dumps[server.providerId]:
            return None
        disco.setSessionFromDump(get_session().lasso_session_dumps[server.providerId])

        disco.initQuery()
        disco.addRequestedServiceType(prefill_form.profile)
        disco.buildRequestMsg()

        try:
            soap_answer = soap_call(disco.msgUrl, disco.msgBody)
        except SOAPException:
            return None
        disco.processQueryResponseMsg(soap_answer)

        service = disco.getService()
        lasso.registerIdWsf2DstService(prefill_form.prefix, prefill_form.profile)

        service.initQuery()

        fields = FieldPrefill.select(lambda x: x.form_id == prefill_form.id)
        for field in fields:
            if field.xpath and field.name:
                service.addQueryItem(field.xpath, field.name)

        service.buildRequestMsg()

        try:
            soap_answer = soap_call(service.msgUrl, service.msgBody)
        except SOAPException:
            return None
        service.processQueryResponseMsg(soap_answer)

        fields_dict = {}
        for field in fields:
            if not field.xpath or not field.name:
                continue
            if field.number > 0:
                number = field.number -1
            try:
                if field.raw_xml:
                    value = service.getAttributeNodes(field.name)[number]
                else:
                    value = service.getAttributeStrings(field.name)[number]
            except (IndexError, TypeError):
                value = ''
                # Log
            if value:
                # Regexp transformation
                if field.regexp_match:
                    value = re.sub(field.regexp_match, field.regexp_replacing, value)
                value = cleanup_html_value(value)
                # Conversion of select field options
                if field.select_options:
                    try:
                        value = field.select_options[value]
                    except (IndexError, KeyError):
                        pass
                if not value:
                    continue
            fields_dict[field.name] = value

        return fields_dict

    def send_prefilled_form(self, prefill_form, page, fields):
        for field_name, new_value in fields.iteritems():
            # Input
            regex = re.compile('(.*)(<input[^>]*? id="%s".*?>)(.*)' % field_name,
                               re.DOTALL | re.IGNORECASE)
            match = regex.match(page)
            if not match:
                regex = re.compile('(.*)(<input[^>]*? name="%s".*?>)(.*)' % field_name,
                                   re.DOTALL | re.IGNORECASE)
                match = regex.match(page)
            if match:
                before, input_field, after = match.groups()
                if 'value="' in input_field.lower():
                    regex_sub = re.compile('value=".*?"', re.DOTALL | re.IGNORECASE)
                    input_field = regex_sub.sub('value="%s"' % new_value, input_field)
                else:
                    input_field = input_field.replace('<input', '<input value="%s"' % new_value)
                page = ''.join([before, input_field, after])
                continue

            # Textarea
            regex = re.compile('(.*<textarea[^>]*? id="%s".*?>)[^<]*(</textarea>.*)' % field_name,
                               re.DOTALL | re.IGNORECASE)
            match = regex.match(page)
            if not match:
                regex = re.compile('(.*<textarea[^>]*? name="%s".*?>)[^<]*(</textarea>.*)' % field_name,
                                   re.DOTALL | re.IGNORECASE)
                match = regex.match(page)
            if match:
                before, after = match.groups()
                page = ''.join([before, new_value, after])
                continue

            # Select
            regex = re.compile('(.*<select[^>]*? id="%s".*?>)(.*?)(</select>.*)' % field_name,
                               re.DOTALL | re.IGNORECASE)
            match = regex.match(page)
            if not match:
                regex = re.compile('(.*<select[^>]*? name="%s".*?>)(.*?)(</select>.*)' % field_name,
                                   re.DOTALL | re.IGNORECASE)
                match = regex.match(page)
            if match:
                before, options, after = match.groups()
                # If the option to select is found, first unselect the previoulsy selected one
                regex2 = re.compile('(.*<option[^>]*? value="%s".*?)(>[^<]*</option>.*)' % new_value,
                               re.DOTALL | re.IGNORECASE)
                match2 = regex2.match(options)
                if match2:
                    before2, after2 = match2.groups()
                    regex3 = re.compile('(.*<option[^>]*?)( selected(="selected")?)(.*?>[^<]*</option>.*)',
                               re.DOTALL | re.IGNORECASE)
                    match3 = regex3.match(options)
                    if match3:
                        before3, selected, selected_value, after3 = match3.groups()
                        options = ''.join([before3, after3])
                regex2 = re.compile('(.*<option[^>]*? value="%s".*?)(>[^<]*</option>.*)' % new_value,
                    re.DOTALL | re.IGNORECASE)
                match2 = regex2.match(options)
                if match2:
                    before2, after2 = match2.groups()
                    options = ''.join([before2, ' selected="selected"', after2])

                page = ''.join([before, options, after])

        return page

