from formencode import validators, foreach
from formencode.schema import Schema, merge_dicts
from formencode.api import *
from formencode.variabledecode import NestedVariables
import cgi

def d(**kw): return kw

def cgi_parse(qs):
    """
    Parses a query string and returns the usually dictionary.
    """
    d = {}
    for key, value in cgi.parse_qsl(qs, 1):
        if d.has_key(key):
            if isinstance(d[key], list):
                d[key].append(value)
            else:
                d[key] = [d[key], value]
        else:
            d[key] = value
    return d

class DecodeCase(object):

    error_expected = False

    def __init__(self, schema, input, **output):
        self.raw_input = input
        self.schema = schema
        if isinstance(input, str):
            input = cgi_parse(input)
        self.input = input
        self.output = output
        all_cases.append(self)

    def test(self):
        print self.raw_input
        actual = self.schema.to_python(self.input)
        assert actual == self.output        

class BadCase(DecodeCase):

    error_expected = True

    def __init__(self, *args, **kw):
        DecodeCase.__init__(self, *args, **kw)
        if len(self.output) == 1 and self.output.has_key('text'):
            self.output = self.output['text']

    def test(self):
        print self.raw_input
        try:
            self.schema.to_python(self.input)
        except Invalid, e:
            actual = e.unpack_errors()
            assert actual == self.output
        else:
            assert 0, "Exception expected"

class Name(Schema):
    fname = validators.String(not_empty=True)
    mi = validators.String(max=1, if_missing=None, if_empty=None)
    lname = validators.String(not_empty=True)

all_cases = []

DecodeCase(Name, 'fname=Ian&mi=S&lname=Bicking',
           fname='Ian', mi='S', lname='Bicking')

DecodeCase(Name, 'fname=Ian&lname=Bicking',
           fname='Ian', mi=None, lname='Bicking')

BadCase(Name, 'fname=&lname=',
        fname='Please enter a value',
        lname='Please enter a value')

BadCase(Name, 'fname=Franklin&mi=Delano&lname=Roosevelt',
        mi="Enter a value less than 1 characters long")

BadCase(Name, '',
        fname='Missing value',
        lname='Missing value')

class AddressesForm(Schema):
    pre_validators = [NestedVariables()]
    class addresses(foreach.ForEach):
        class schema(Schema):
            name = Name()
            email = validators.Email()

DecodeCase(AddressesForm,
           'addresses-2.name.fname=Jill&addresses-1.name.fname=Bob&'
           'addresses-1.name.lname=Briscoe&'
           'addresses-1.email=bob@bobcom.com&'
           'addresses-2.name.lname=Hill&addresses-2.email=jill@hill.com&'
           'addresses-2.name.mi=J',
           addresses=[d(name=d(fname='Bob', mi=None, lname='Briscoe'),
                        email='bob@bobcom.com'),
                      d(name=d(fname='Jill', mi='J', lname='Hill'),
                        email='jill@hill.com')])

DecodeCase(AddressesForm,
           '',
           addresses=[])

BadCase(AddressesForm,
        'addresses-1.name.fname=&addresses-1.name.lname=x&'
        'addresses-1.email=x@x.com',
        addresses=[d(name=d(fname="Please enter a value"))])
        
BadCase(AddressesForm,
        'whatever=nothing',
        text="The input field 'whatever' was not expected.")

def test_this():
    for case in all_cases:
        yield case.test

def test_merge():
    assert (merge_dicts(dict(a='a'), dict(b='b'))
            == dict(a='a', b='b'))
    assert (merge_dicts(dict(a='a', c='c'), dict(a='a', b='b'))
            == dict(a='a\na', b='b', c='c'))
    assert (merge_dicts(dict(a=['a1', 'a2'], b=['b'], c=['c']),
                        dict(a=['aa1'],
                             b=['bb', 'bbb'],
                             c='foo'))
            == dict(a=['a1\naa1', 'a2'], b=['b\nbb', 'bbb'],
                    c=['c']))
    
