# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4

##
## Copyright (C) 2005-2012 Async Open Source <http://www.async.com.br>
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., or visit: http://www.gnu.org/.
##
##  Author(s): Stoq Team <stoq-devel@async.com.br>
##

from kiwi.argcheck import argcheck
from kiwi.datatypes import ValidationError
from kiwi.python import AttributeForwarder

from stoqlib.api import api
from stoqlib.database.runtime import StoqlibStore
from stoqlib.domain.address import Address, CityLocation
from stoqlib.domain.person import Person
from stoqlib.gui.editors.baseeditor import BaseEditorSlave
from stoqlib.lib.countries import get_countries
from stoqlib.lib.translation import stoqlib_gettext
from stoqlib.lib.message import info
from stoqlib.lib.parameters import sysparam

_ = stoqlib_gettext


class CityLocationMixin(object):
    """A mixin class for city locations

    Use this mixin in a multiple inheritance editor to have
    it's city location validated and prefilled with right data.

    For this to happen, you need to have:
      - A proxy entry for 'city' accessible at self.city
      - A proxy entry for 'state' accessible at self.state
      - A proxy combo entry for 'country' accessible at self.country
    """

    #
    #  BaseEditorSlave
    #

    def setup_proxies(self):
        self._confirming = False

        self.country.prefill(get_countries())
        self._cache_l10n_fields()

        self._prefill_states()
        self.city.set_exact_completion()
        city_completion = self.city.get_completion()
        city_completion.set_minimum_key_length = 2

    def validate_confirm(self):
        self._confirming = True

        self.force_validation()
        self.city.validate(force=True)

        if self.city.is_valid():
            rv = True
        else:
            info(_("The city is not valid"))
            rv = False

        if not self.country.read():
            info(_("The country is not valid"))
            rv = False

        self._confirming = False
        return rv

    #
    #  Private
    #

    def _cache_l10n_fields(self):
        self._city_l10n = api.get_l10n_field(self.store, 'city',
                                             self.model.country)
        self._state_l10n = api.get_l10n_field(self.store, 'state',
                                              self.model.country)

    def _prefill_states(self):
        self.state.prefill(self._state_l10n.state_list)

    def _prefill_cities(self, force=False):
        completion = self.city.get_completion()
        if not completion:
            # Completion wasn't set yet
            return

        if len(completion.get_model()) and not force:
            return

        self.city.prefill([]) # mimic missing .clear method
        cities = CityLocation.get_cities_by(self.store,
                                            state=self.model.state,
                                            country=self.model.country)
        self.city.prefill(list(cities))

    #
    #  Callbacks
    #

    def on_state__focus_out_event(self, entry, event):
        self._prefill_cities(force=True)
        self.city.validate(force=True)

    def on_state__validate(self, entry, state):
        if not self._state_l10n.validate(state):
            return ValidationError(_("%s is not valid") % self._state_l10n.label)

    def on_city__focus_out_event(self, entry, event):
        self.city.validate(force=True)

    def on_city__validate(self, entry, city):
        if sysparam(self.store).ALLOW_REGISTER_NEW_LOCATIONS:
            return

        if self.city.is_focus() and not self._confirming:
            # Delay the validation until the user typed the whole city
            return

        if not self._city_l10n.validate(city,
                                        self.model.state, self.model.country):
            return ValidationError(_("%s is not valid") % self._city_l10n.label)

    def on_city__content_changed(self, widget):
        city = widget.read()
        if city:
            self._prefill_cities()

    def after_state__content_changed(self, widget):
        if self.state.is_focus():
            # Delay the prefill and validation as those will do a lot
            # of database queries for each letter typed in here.
            return

        self._prefill_cities(force=True)
        self.city.validate(force=True)

    def after_country__content_changed(self, widget):
        self._cache_l10n_fields()
        self._prefill_states()
        self._prefill_cities(force=True)
        self.state.validate(force=True)
        self.city.validate(force=True)


class _AddressModel(AttributeForwarder):
    attributes = [
        'streetnumber',
        'district',
        'street',
        'complement',
        'postal_code',
        'is_main_address',
        'is_valid_model',
        'city_location',
        ]

    @argcheck(Address, StoqlibStore)
    def __init__(self, target, store):
        AttributeForwarder.__init__(self, target)
        self.store = store
        self.city = target.city_location.city
        self.state = target.city_location.state
        self.country = target.city_location.country

    def _city_location_changed(self):
        return (self.city != self.city_location.city or
                self.state != self.city_location.state or
                self.country != self.city_location.country)

    def ensure_address(self):
        changed = self._city_location_changed()
        if changed:
            location = CityLocation.get_or_create(
                city=self.city,
                state=self.state,
                country=self.country,
                store=self.store)
            self.target.city_location = location


class AddressSlave(BaseEditorSlave, CityLocationMixin):
    model_type = _AddressModel
    gladefile = 'AddressSlave'

    proxy_widgets = [
        'streetnumber',
        'district',
        'street',
        'complement',
        'postal_code',
        'streetnumber_check',
        'city',
        'state',
        'country',
        ]

    @argcheck(object, Person, Address, bool, bool, object)
    def __init__(self, store, person, model=None, is_main_address=True,
                 visual_mode=False, db_form=None):
        self.person = person
        self.is_main_address = (model and model.is_main_address
                                or is_main_address)
        self.db_form = db_form
        if model is not None:
            model = store.fetch(model)
            model = _AddressModel(model, store)
        BaseEditorSlave.__init__(self, store, model, visual_mode=visual_mode)

    #
    #  BaseEditorSlave
    #

    def create_model(self, store):
        address = Address(person=self.person,
                          city_location=CityLocation.get_default(store),
                          is_main_address=self.is_main_address,
                          store=store)
        return _AddressModel(address, store)

    def set_model(self, model):
        """ Changes proxy model.  This method is used when this slave is
        attached as a container for the main address and the main address
        needs to be changed, so this slave must reflect the new address
        defined.
        """
        self.model.ensure_address()
        self.model = model
        self.proxy.set_model(self.model)

    def on_confirm(self):
        self.model.ensure_address()

    def setup_proxies(self):
        CityLocationMixin.setup_proxies(self)

        if self.db_form:
            self._update_forms()
        self.proxy = self.add_proxy(self.model,
                                    AddressSlave.proxy_widgets)

        # Not using self._statel10n and self._city_l10n here because we need
        # to get the label name based on SUGGESTED_COUNTRY and not on the
        # country on model.
        for field, label in [
            ('state', self.state_lbl),
            ('city', self.city_lbl),
            ]:
            l10n_field = api.get_l10n_field(self.store, field)
            label.set_text(l10n_field.label + ':')

        # Enable if we already have a number or if we are adding a new address.
        self.streetnumber_check.set_active(bool(self.model.streetnumber)
                                           or not self.edit_mode)
        self._update_streetnumber()

    def validate_confirm(self):
        return (CityLocationMixin.validate_confirm(self) and
                self.model.is_valid_model())

    #
    #  Private
    #

    def _update_streetnumber(self):
        if not self.visual_mode:
            # Don't do that on visual mode. Visual mode will handle
            # the sensitive property on all widgets properly.
            active = self.streetnumber_check.get_active()
            self.streetnumber.set_sensitive(active)

            if not active:
                self.model.streetnumber = None
                self.streetnumber.set_text('')
                return

            if not self.model.streetnumber:
                self.streetnumber.set_text('')

    def _update_forms(self):
        self.db_form.update_widget(self.district, other=self.district_lbl)
        self.db_form.update_widget(self.street, other=self.address_lbl)
        self.db_form.update_widget(self.streetnumber, u'street_number',
                                   other=self.streetnumber_check)
        self.db_form.update_widget(self.postal_code,
                                   other=self.postal_code_lbl)
        self.db_form.update_widget(self.complement,
                                   other=self.complement_lbl)
        self.db_form.update_widget(self.city,
                                   other=self.city_lbl)
        self.db_form.update_widget(self.state,
                                   other=self.state_lbl)
        self.db_form.update_widget(self.country,
                                   other=self.country_lbl)

    #
    # Kiwi callbacks
    #

    def on_streetnumber__validate(self, entry, streetnumber):
        if streetnumber <= 0:
            return ValidationError(_("Number cannot be zero or less than zero"))

    def on_streetnumber_check__clicked(self, check_button):
        self._update_streetnumber()
