text: # -*- coding: UTF-8 -*-
  from assopy import settings
  from assopy.clients.gnrbag import Bag
  
  import logging
  import urllib
  from collections import defaultdict
  from urlparse import urlparse, urlunparse
  
  log = logging.getLogger('assopy.genro')
  
  _url = urlparse(settings.BACKEND)
  
  def _build(path, query=None):
      splitted = list(_url)
      if not path.startswith('/'):
          path = '/' + path
      splitted[2] = (splitted[2] + path).replace('//', '/')
      if query:
          splitted[4] = urllib.urlencode(query)
      return urlunparse(splitted)
  
  def _get(subpath, **kwargs):
      url = _build(subpath, kwargs)
      log.debug('get -> %s', url)
      f = urllib.urlopen(url)
      b = Bag()
      b.fromXml(f.read())
      return b
  
  def _post(subpath, bag=None):
      url = settings.BACKEND + subpath
      body = {}
      if bag is not None:
          body['data'] = bag.toXml()
          log.debug('post -> %s data: %s', unicode(url).encode('utf-8'), body['data'] if isinstance(body['data'], str) else body['data'].encode('utf-8'))
      else:
          log.debug('post -> %s', url)
      f = urllib.urlopen(url, urllib.urlencode(body))
      b = Bag()
      b.fromXml(f.read())
      return b
  
  def user_local2remote(user):
      assert user.assopy_id is not None
      o = dict(user_data(user.assopy_id))
  
      data = dict(o)
      data['firstname'] = user.user.first_name
      data['lastname'] = user.user.last_name
      # il backend ha ua dimensione massima per il campo www di 40 caratteri,
      # visto che non è fondamentale passare questa info invece che troncarla
      # preferisco non metterla.
      #data['www'] = user.www
  
      # idem per phone che è di 20 caratteri
      #data['phone'] = user.phone
      if user.card_name:
          data['card_name'] = user.card_name[:80]
      else:
          data['card_name'] = '%s %s' % (user.user.first_name, user.user.last_name)
      data['is_company'] = user.account_type == 'c'
      data['vat_number'] = user.vat_number
      data['tin_number'] = user.tin_number
      data['country'] = user.country_id
      data['zip'] = user.zip_code
      data['address'] = user.address
      data['city'] = user.city
      data['state'] = user.state
  
      if data != o:
          log.info('user local2remote %s -> %s', user.user.email, user.assopy_id)
          set_user_data(user.assopy_id, data)
      return user
  
  def user_remote2local(user):
      assert user.assopy_id is not None
      data = user_data(user.assopy_id)
  
      log.info('user remote2local %s -> %s', user.assopy_id, user.user.email)
      g = lambda k: (data.get(k) or '').strip()
      user.user.first_name = g('firstname')
      user.user.last_name = g('lastname')
      user.user.save()
      user.www = g('www')
      user.phone = g('phone')
      user.card_name = g('card_name')
      user.account_type = 'c' if data.get('is_company') else 'p'
      user.vat_number = g('vat_number')
      user.tin_number = g('tin_number')
  
      from assopy.models import Country
      try:
          country = Country.objects.get(pk=data['country'])
      except Country.DoesNotExist:
          country = None
      user.country = country
      user.zip_code = g('zip')
      user.address = g('address')
      user.city = g('city')
      user.state = g('state')
      user.save()
      return user
  
  def users(email, password=None):
      """
      restituisce gli id degli utenti remoti che hanno l'email passata
      """
      p = {
          'email': email,
      }
      if password is not None:
          p['password'] = password
      return _get('/users/', **p)
  
  def user_data(id):
      return _get('/users/%s' % id)   
  
  def set_user_data(id, data):
      return _post('/users/%s' % id, Bag(data))
  
  def create_user(firstname, lastname, email):
      b = Bag()
      b['firstname'] = firstname
      b['lastname'] = lastname
      b['email'] = email
      return _post('/users/', b)['id']
  
  def create_order(order, return_url=None):
      b = Bag()
      assert order.user.assopy_id
      rows = list(order.orderitem_set.select_related('ticket__fare'))
      b['event'] = rows[0].ticket.fare.conference
      b['user_id'] = order.user.assopy_id
      #b['coupon'] = 
      #b['discount'] = 
      payment_method = {
          'paypal': 'paypal',
          'cc': 'paypal',
          'bank': 'bank',
          'admin': 'bank',
      }[order.method]
      b['payment_method'] = payment_method
  
      # Se order.deducibile() == False l'acquirente ha comprato biglietti ad uso
      # "personale" e non vogliamo che possa dedurli. Questo flag informa il
      # software di fatturazione di riportare in fattura una dicitura apposita.
      # b['personal'] = not order.deductible()
      # Aggiornamento, il flag personal non viene più usato dal backend
      b['personal'] = False
      b['billing_notes'] = order.billing_notes
      b['vat_rate'] = 20.0
      if return_url:
          b['return_url'] = return_url
  
      tickets = defaultdict(lambda: 0)
      for r in rows:
          if r.ticket:
              tickets[r.ticket.fare.code] += 1
      ix = 0
      #for t in tickets:
      #   b['order_rows.r%s.fare_code' % ix] = t
      #   b['order_rows.r%s.quantity' % ix] = tickets[t]
      #   ix += 1
  
      for r in rows:
          if r.ticket:
             b['order_rows.r%s.fare_code' % ix] = r.ticket.fare.code
             b['order_rows.r%s.quantity' % ix] = 1
          b['order_rows.r%s.description' % ix] = r.description
          b['order_rows.r%s.price' % ix] = r.price
          ix += 1
      result = _post('/orders/', b)
      order.assopy_id = result['order_id']
      order.code = result['order_code']
      order.payment_url = result['paypal_url']
      order.save()
  
  def order(id):
      return _get('/orders/%s' % id)
  
  def confirm_order(id, value, date=None):
      b = Bag()
      b['payment_value'] = value
      b['payment_date'] = date
      return _post('/orders/confirm/%s/' % (id,), b)
  
  def update_fare(fare):
      name = fare.name
      if fare.ticket_type == 'conference':
          if fare.recipient_type == 's':
              name += ' (for students only)'
          elif fare.recipient_type == 'p':
              name += ' (for individuals)'
          
      b = Bag()
      b['price'] = fare.price
      b['description'] = name[:140]
      b['personal_fare'] = fare.recipient_type != 'c'
      b['deposit_fare'] = fare.payment_type == 'd'
      b['long_desc'] = fare.description
      b['start_date'] = fare.start_validity
      b['end_date'] = fare.end_validity
      return _post('/fares/%s/%s/' % (fare.conference, fare.code), b)
  
  def user_invoices(id):
      return _get('/invoices/user/%s' % id)
  
  def invoice_url(id):
      return _build('/invoice/%s' % id)
  
  def invoice(id):
      return _get('/invoice/%s/bag' % id)
  
  def order_invoices(id):
      output = []
      data = order(id)
      ix = 0
      while True:
          invoice = data['invoices.i%d' % ix]
          if not invoice:
              break
          output.append(invoice)
          ix += 1
      return output
  