import base64
import cStringIO
import mimetypes
import os
import re
import tempfile
import time

from quixote import get_publisher, get_request, get_response, get_session
from quixote.http_request import Upload
from quixote.form import *
from quixote.html import htmltext, htmltag, TemplateIO
from quixote.util import randbytes

import misc

QuixoteForm = Form

Widget.REQUIRED_ERROR = N_('required field')
get_error_orig = Widget.get_error

def get_i18n_error(self, request=None):
    error = get_error_orig(self, request)
    if error == Widget.REQUIRED_ERROR:
        return _(error)
    return error

def render(self):
    # quixote/form/widget.py, Widget::render
    r = TemplateIO(html=True)
    classnames = '%s widget' % self.__class__.__name__
    if hasattr(self, 'extra_css_class') and self.extra_css_class:
        classnames += ' ' + self.extra_css_class
    if self.get_error():
        classnames += ' widget-with-error'
    r += htmltext('<div class="%s">') % classnames
    r += self.render_title(self.get_title())
    r += htmltext('<div class="content">')
    r += self.render_content()
    r += self.render_hint(self.get_hint())
    r += self.render_error(self.get_error())
    r += htmltext('</div>')
    r += htmltext('</div>')
    if self.render_br:
        r += htmltext('<br class="%s" />') % classnames
    r += htmltext('\n')
    return r.getvalue()

Widget.get_error = get_i18n_error
Widget.render = render
Widget.cleanup = None

class Form(QuixoteForm):
    TOKEN_NOTICE = N_(
            "The form you have submitted is invalid.  Most "
            "likely it has been successfully submitted once "
            "already.  Please review the form data "
            "and submit the form again.")
    ERROR_NOTICE = N_("There were errors processing your form.  See below for details.")

    info = None

    def add(self, widget_class, name, *args, **kwargs):
        if kwargs and not kwargs.has_key('render_br'):
            kwargs['render_br'] = False
        return QuixoteForm.add(self, widget_class, name, *args, **kwargs)

    def _render_submit_widgets(self):
        return htmltext('<div class="buttons">%s</div>' % QuixoteForm._render_submit_widgets(self))

    def _render_info_notice(self):
        return htmltext('<div class="infonotice">%s</div>' % _(self.info))

    def _render_error_notice(self):
        return htmltext('<div class="errornotice">%s</div>' % _(
                    QuixoteForm._render_error_notice(self)))

    def _render_body(self):
        value = ''
        if self.info:
            value += self._render_info_notice()
        value += QuixoteForm._render_body(self)
        if self.has_errors():
            # if form has errors, focus and select the first widget on error
            value += htmltext("""<script type="text/javascript">
var all_obj = document.getElementsByTagName('DIV');
for (i=0; i<all_obj.length; i++) {
    if ( ( ' ' + all_obj[i].getAttribute("class") + ' ').toLowerCase().match(
                new RegExp ( '^.* widget-with-error .*$', 'g' ) ) ) {
        less_obj = all_obj[i].getElementsByTagName('INPUT');
        for (j=0; j<less_obj.length; j++) {
            less_obj[j].select();
            less_obj[j].focus();
            break;
        }
        break;
    }
}
</script>""")
        return value


class HtmlWidget:
    error = None

    def __init__(self, string):
        self.string = string

    def render(self):
        return self.string

    def has_error(self, request):
        return False

    def parse(self, *args):
        pass


class FileWithPreviewWidget(CompositeWidget):
    """Widget that proposes a File Upload widget but that stores the file
    ondisk so it has a "readonly" mode where the filename is shown."""
    token = None

    def __init__(self, name, value=None, **kwargs):
        CompositeWidget.__init__(self, name, value, **kwargs)
        self.value = value
        preview = kwargs.get('readonly')
        if not preview:
            del kwargs['title']
            if kwargs.has_key('hint'):
                del kwargs['hint']
            self.add(FileWidget, 'file', **kwargs)
        else:
            session = get_session()
            self.add(HiddenWidget, 'token')
            del kwargs['title']
            self.add(StringWidget, 'filename', **kwargs)
            if self.value and self.value.orig_filename:
                    wid = self.get_widget('filename')
                    get_request().form[wid.name] = self.value.orig_filename

            # Is this still mandatory ?
            if hasattr(get_request(), 'tempfiles'):
                if name in get_request().tempfiles:
                    token = get_request().tempfiles[name]
                    wid = self.get_widget('token')
                    get_request().form[wid.name] = token

    def render_content(self):
        r = TemplateIO(html=True)
        if isinstance(self.value, PicklableUpload):
            filetype = mimetypes.guess_type(self.value.orig_filename)
            if filetype and filetype[0] and filetype[0].startswith("image"):
                session = get_session()
                if self.token in session.tempfiles.keys():
                    session.tempfiles[self.token]['value'] = self.value.fp.read()
                    r += htmltext('<img alt="%s" src="tempfile?t=%s" />' % \
                                            (self.value.orig_filename, 
                                             self.token))
                    self.value.fp.seek(0)
        for widget in self.get_widgets():
            r += widget.render()
        return r.getvalue()

    def _parse(self, request):
        session = get_session()
        if self.get('file'):
            self.value = self.get('file')
            self.value.__class__ = PicklableUpload
            # and saves it
            token = randbytes(8)
            if not hasattr(get_request(), 'tempfiles'):
                get_request().tempfiles = {}
            get_request().tempfiles[self.name] = token
            dirname = os.path.join(get_publisher().app_dir, 'tempfiles')
            if not os.path.exists(dirname):
                os.mkdir(dirname)
            filename = os.path.join(dirname, token)
            fd = open(filename, 'w')
            upload = self.value
            fd.write(upload.fp.read())
            fd.close()
            if not session.tempfiles:
                session.tempfiles = {}
            session.tempfiles[token] = {
                'orig_filename': upload.orig_filename,
                'base_filename': upload.base_filename,
                'content_type': upload.content_type,
                'charset': upload.charset
            }
            self.value = None
        else:
            token = self.get('token')

        if session.tempfiles and session.tempfiles.has_key(token):
            self.token = token
            if not isinstance(self.value, PicklableUpload):
                self.value = PicklableUpload(session.tempfiles[token]['orig_filename'],
                    session.tempfiles[token]['content_type'],
                    session.tempfiles[token]['charset'])
                self.value.fp = tempfile.TemporaryFile()
                dirname = os.path.join(get_publisher().app_dir, 'tempfiles')
                filename = os.path.join(dirname, token)
                self.value.fp.write(open(filename).read())
            self.value.fp.seek(0)

    def cleanup(self):
        if self.token:
            dirname = os.path.join(get_publisher().app_dir, 'tempfiles')
            filename = os.path.join(dirname, self.token)
            if os.path.exists(filename):
                os.unlink(filename)

class PicklableUpload(Upload):
    def __getstate__(self):
        odict = self.__dict__.copy()
        del odict['fp']
        odict['data'] = self.fp.read()
        self.fp.seek(0)
        return odict

    def __setstate__(self, dict):
        self.__dict__.update(dict)
        self.fp = cStringIO.StringIO(self.data)
        del self.data

class EmailWidget(StringWidget):
    def __init__(self, *args, **kwargs):
        StringWidget.__init__(self, *args, **kwargs)
        self.attrs['size'] = '35'

    def _parse(self, request):
        StringWidget._parse(self, request)
        if self.value is not None:
            # ultra-basic, needs proper regex ?
            if not '@' in self.value[1:-1]:
                self.error = _('must be a valid email address')
            elif ' ' in self.value:
                self.error = _('must be a valid email address')
            elif self.value.count('@') != 1:
                self.error = _('must be a valid email address')


class WcsExtraStringWidget(StringWidget):
    field = None
    prefill = False

    def render_content(self):
        s = StringWidget.render_content(self)
        if not self.value and self.field and \
                self.field.wsf_prefill_expression and self.field.wsf_prefill_explicit and \
                get_session().lasso_session_dump:
            s += htmltext(
                    '<input class="prefill-button" type="submit" name="f%s-prefill" value="%s">') % (
                        self.field.id, _('Prefill'))
        return s

    def _parse(self, request):
        StringWidget._parse(self, request)
        if self.field and self.field.wsf_prefill_expression:
            self.prefill = (request.get_field('f%s-prefill' % self.field.id) is not None)
            if self.prefill:
                if not request.form.has_key('prefill'):
                    request.form['prefill'] = []
                request.form['prefill'].append(self.field.id)
                self.error = 'MUST PREFILL...' # won't be displayed
        elif self.field and self.field.validation and self.value is not None:
            if not re.match(self.field.validation, self.value):
                self.error = _('wrong format')


class DateWidget(StringWidget):
    '''StringWidget which checks the value entered is a correct date'''
    def __init__(self, *args, **kwargs):
        StringWidget.__init__(self, *args, **kwargs)
        self.attrs['size'] = '10'
        self.attrs['maxlength'] = '10'

    def _parse(self, request):
        StringWidget._parse(self, request)
        if self.value is not None:
            format_string = misc.date_format()
            try:
                value = time.strptime(self.value, format_string)
            except ValueError:
                self.error = _('invalid date')
                self.value = None
            else:
                if value[0] < 1800 or value[0] > 2099:
                    self.error = _('invalid date')
                    self.value = None
    
    def render_content(self):
        resp = get_response()
        if not resp.filter.get('script') or not 'calendar.js' in resp.filter['script']:
            script = resp.filter.get('script', '') or ''
            script += """
    <link rel="stylesheet" type="text/css" href="/css/jscalendar/aqua/theme.css" />
    <script src="/js/jscalendar/calendar.js" type="text/javascript"></script>
    <script src="/js/jscalendar/lang/calendar-fr.js" type="text/javascript"></script>
    <script src="/js/jscalendar/helpers.js" type="text/javascript"></script>"""
            resp.filter['script'] = script

        self.attrs['id'] = "id_%s" % self.name
        onclick = "return showCalendar('%s', " % self.attrs['id']  
        onclick += "'%Y-%m-%d', '24', false);"  
        html = StringWidget.render_content(self)
        html += htmltag("input", xml_end=True,
                       type="button",
                       name="%s_button" % self.name,
                       value="...",
                       onclick=onclick)
        return html

class RegexStringWidget(StringWidget):
    '''StringWidget which checks the value entered is a correct regex'''
    def _parse(self, request):
        StringWidget._parse(self, request)
        if self.value is not None:
            try:
                re.compile(self.value)
            except:
                self.error = _('invalid regular expression')


class CheckboxesWidget(CompositeWidget):
    readonly = False

    def __init__(self, name, value = None, elements = None, **kwargs):
        CompositeWidget.__init__(self, name, value, **kwargs)
        self.element_names = {}

        if kwargs.has_key('title'):
            del kwargs['title']
        if kwargs.has_key('readonly'):
            del kwargs['readonly']
            self.readonly = True

        for v in elements:
            if type(v) is tuple:
                title = v[1]
                key = v[0]
                name = 'element%d' % v[0]
            else:
                title = v
                key = v
                name = 'element%d' % len(self.element_names.keys())

            if value and key in value:
                checked = True
            else:
                checked = False
            self.add(CheckboxWidget, name, title = title, value = checked, **kwargs)
            self.element_names[name] = key

    def _parse(self, request):
        values = []
        for name in self.element_names:
            value = self.get(name)
            if value is True:
                values.append(self.element_names[name])
        self.value = values or None

    def render_content(self):
        r = TemplateIO(html=True)
        r += htmltext('<ul>')
        for widget in self.get_widgets():
            r += htmltext('<li><label>')
            if self.readonly:
                widget.attrs['disabled'] = 'disabled'
                if widget.value:
                    r += htmltext('<input type="hidden" name="%s" value="yes" >') % widget.name
                widget.name = widget.name + 'xx'
            r += widget.render_content()
            r += htmltext('</label>')
            r += widget.title
            r += htmltext('</li>')
        r += htmltext('</ul>')
        return r.getvalue()
