from MoinMoin import wikiutil, user, formacl
from MoinMoin.Page import Page
from MoinMoin.widget.datatypes import *

from MoinMoin.util import pysupport
modules = pysupport.getPackageModules(__file__)

class ErrorMask:
    pass # XXX to do

class ErrorMessage:
    """returned from DialogBase.get_data/save_data on error
    """

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

    def render(self, fmt):
        return (fmt.sysmsg(True) + 
                fmt.text(self.message) +
                fmt.sysmsg(False))

class DialogBase:
    arg_prototypes = [] # list of (name, defaultvalue)

    acl = "AdminGroup:read,write Owner:read,write Member:read"
    methods = ["view", "edit", "save", "delete"] # allowed methods, 
                       # these and action_* must be implementend
    
    nonmacro_methods = ["save"] # force these to be invoked as an action

    def __init__(self, request):
        self.request = request
        _ = request.getText
        self.targeturl = self.request.getBaseURL() + wikiutil.url_quote(self.request.path_info) 
        
        self.savebuttontext = ' %s ' % _('Save')
        self.editbuttontext = ' %s ' % _('Edit')
        # self.button now defined by macro or action
        # self.button = GenericButton("Edit", text=self.editbuttontext, {"method":"edit"})
        self.show_buttons = True
        
        self.mask = None    # set in child class
        self.acl = formacl.AccessControlList(request, self.acl, self)
    

    
    def check_acl(self, dowhat):
        return self.acl.may(self.request, self.request.user.name, dowhat)
    
    def is_Owner(self, name):
        return False

    def is_Member(self, name):
        return False
        
    def macro(self, macroargs):
        """ if called as macro """
        i = 0
        args = {}
        for name, default in self.arg_prototypes:
            if i < len(macroargs):
                args[name] = macroargs[i].strip()
            else:
                args[name] = default
            i += 1
        # handle form parameter 'method' (e. g. from URL)
        method = self.request.form.get('method', self.methods)[0]
        if method not in self.methods or method in self.nonmacro_methods:
            method = self.methods[0]
        
        self.method = method
        self.args = args
        self.as_macro = True

        # define generic button that passes args
        fields = {"method":"edit"}
        fields.update(args)
        action = self.request.page.url(self.request)
        self.button = GenericButton("Edit", action=action, text=self.editbuttontext, fields=fields)
        
        return getattr(self, method)(args)

    def action(self, pagename):
        """ if called as action """
        request = self.request 

        args = {}
        for name, default in self.arg_prototypes:
            args[name] = request.form.get(name, [default])[0]

        # handle form parameter 'method' (e. g. from URL)
        method = request.form.get('method', self.methods)[0]
        if method not in self.methods:
            method = self.methods[0]            
        
        self.method = method
        self.args = args
        self.as_macro = False
        self.called_from_macro = bool(request.form.get('called',[''])[0])

        # define generic button that passes args
        fields = {"method":"edit"}
        fields.update(args)
        self.button = GenericButton("Edit", text=self.editbuttontext, fields=fields)
        
        return getattr(self, "action_"+method)(args)
            
    def get_data(self, filters):
        """ implement this to give data to dialog """
        return (None,)

    def save_data(self, dataset):
        """ implement this to save data from dialog 
        """
        return None

    def delete_data(self, dataset):
        """ implement this to delete data from database """
        self.errormsg = 'Deletion is not implemented.'
        return None

    def render_edit_button(self):
        if not self.check_acl('write'):
            return ''
        if not self.editbuttontext:
            return ""
        if not self.as_macro:
            value = {"fields" : {
                "action" : "form",
                "do" : self.name,
            }}
        else:
            value = {}
        return self.button.render_value(value, self.request.formatter)

    def render_value(self, value):
        """
            returns html output of data
            overload to add edit/delete buttons
        """
        result = self.mask.render_value(value, self.request.formatter, 
                self.name)
        return result + self.render_edit_button()
    
    def render_editform(self, value):
        """
            returns html output for editing
            uses self.savebuttontext
            savebutton links to self.targeturl with
            action_save (default: current page)
        """
        result = []
        fmt = self.request.formatter
        result.append(fmt.rawHTML('''<form action="%s" method="post">
<input type="hidden" name="action" value="form" />
<input type="hidden" name="do", value="%s" /> 
<input type="hidden" name="method" value="save" />
''' % (self.targeturl, self.name)))

        if self.as_macro:
            result.append(fmt.rawHTML('''<input type="hidden" name="called" value="as_macro"'''))

        for k,v in self.args.items():
            result.append(fmt.rawHTML('''
<input type="hidden" name="%(k)s" value="%(v)s" />
''' % {'k' : k, 'v' : v}))

        result.append(self.mask.render_editform(value, fmt, self.name))

        result.append(fmt.paragraph(1))
        result.append(fmt.rawHTML("""
<input type="submit" value="%s">
</form>""" % self.savebuttontext ))
        result.append(fmt.paragraph(0))
                        
        return ''.join(result)

    def render_savedata(self, data, errormsg=None):
        """
            default: returns html of savedata
            overload this to give other feedback after save
            called from self.save
        """
        # XXX errormsg
        if self.called_from_macro:
            self.request.page.send_page(self.request,msg=errormsg)
        else:
            return self.render_value(data)

    def render_delete(self, data, errormsg=None):
        """
            default: renders page
            overload this to give other feedback after deletion
            called from self.action_delete
        """
        if not errormsg:
            msg = '%s deleted.' % self.name
        else:
            msg = errormsg
        if self.request.form.has_key('method'):
            del self.request.form['method']
        self.request.page.send_page(self.request,msg=msg)

    def decorate_action(self, method, args):
        """
            renders header and footer
            calls method (view, edit, save)
        """
        self.request.http_headers()
        # This action generate data using the user language 
        self.request.setContentLanguage(self.request.lang) 
        
        _ = self.request.getText

        # XXX 
        page = self.request.page
        wikiutil.send_title(self.request, _('Database'), page=page) 
        self.request.write(self.request.formatter.startContent("content")) 
        self.request.write(method(args))
        
        # End content and send footer 
        self.request.write(self.request.formatter.endContent()) 
        wikiutil.send_footer(self.request, page) 

    def action_view(self, args):
        self.decorate_action(self.view, args)

    def view(self, args):   
        """
            returns html
            does access control, parameter handling, ...
            calls render_value
        """
        if not self.check_acl("read"):
            return "You are not allowed" # XXX
        edit = True
        filters = args
        data = self.get_data(filters)
        if isinstance(data, ErrorMessage):
            return data.render(self.request.formatter)
        return self.render_value(data)
    
    def action_edit(self, args):
        self.decorate_action(self.edit, args)
        
    def edit(self, args):
        """
            returns html
            does access control, parameter handling, ...
            calls render_editform
        """
        if not self.check_acl("write"):
            return "You are not allowed" # XXX
        filters = args
        data = self.get_data(filters)
        if isinstance(data, ErrorMessage):
            return data.render(self.request.formatter)
        return self.render_editform(data)

    def action_save(self, args):
        """
           default: decorates save-result
           overload this (but call self.save) to get
           undecorated result
        """
        if self.called_from_macro:
            self.save(args)
        else:
            self.decorate_action(self.save, args)
        
    def save(self, args):
        """
            returns html: either dialog data on self.targeturl
            does access control, parameter handling, ...
            calls self.mask.get_value and save_data
            renders dialog data afterwards (render_savedata)
        """
        if not self.check_acl("write"):
            return "You are not allowed" # XXX
        error_msg = []
        request = self.request
        data = self.mask.get_value(request.form, msg=error_msg, name=self.name)
        if error_msg:
            result = []
            fmt = request.formatter
            result.append(fmt.sysmsg(1))
            for msg in error_msg:
                result.append(fmt.paragraph(1))
                result.append(fmt.text(msg))
                result.append(fmt.paragraph(0))
            result.append(fmt.sysmsg(0))
            result.append(self.render_editform(data))
            return ''.join(result)
        else:
            errormsg = self.save_data(data)
            if isinstance(errormsg, ErrorMessage):
                return errormsg.render(self.request.formatter) + self.render_editform(data)
            return self.render_savedata(data, errormsg)

    def action_delete(self, args):
        """
             deletes current data set
        """
        return self.delete(args)
        
    def delete(self, args):
        """
            deletes current data set
        """
        if not self.check_acl("write"):
            return "You are not allowed" # XXX
        self.delete_data(args)
        self.render_delete(args)
