#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# HubSessions © GeezTeem 2010› https://www.geezteem.com/253
# AGPL-3.0-or-later - https://www.gnu.org/licenses/agpl-3.0.txt

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
from appy.all import *
from appy.model.place import Place as BasePlace

from HubSessions import workflows, utils

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class PlaceColumns(utils.Columns):
    '''Manages columns for lists of places'''

    c = utils.Columns
    width = '100px|'

    # Standard columns
    base = [
      Col.Checkbox('_checkbox*24px|', td=f'{c.start};padding-top:0.9em'),
      c.getTitle(first=False),
      Col(f'address*27em', td=c.mid),
      Col(f'created*{width}', td=c.mid),
      Col(f'modified*{width}', td=c.mid),
      Col(f'state*{width}', td=c.end),
    ]

#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class Place:
    '''Represents an address for a meeting'''

    # This class also manages text-based places. Text-based places are an
    # alternative to using Place objects. String "placeObjects" being or not in
    # Config.unusedMeetingfields determines which of the 2 systems is in use.

    # This class does not add any field. Consequently, defining appy=True must
    # be set in order to tell Appy that this class is a Appy class.
    appy = True
    creators = ['Manager']
    Columns = PlaceColumns
    listColumns = Columns.get
    workflow = workflows.ActiveInactive

    @staticmethod
    def update(class_):
        '''Sets a default country'''
        class_.fields['country'].default = utils.defaultCountry

    def onEdit(self, created):
        '''Update local roles when relevant'''
        if self.config.committees.enabled:
            container = self.container
            if not container.isTool:
                container.updateLocalRoles(self)

    def mayDelete(self):
        '''A place can't be deleted as soon as it is referred by at least one
           meeting.'''
        return self.isEmpty('meetings') and self.isEmpty('preMeetings')

    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #                    Object-based class methods
    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    @classmethod
    def showObjectBased(class_, o):
        '''Show object-based places, if configured on p_o being the tool or a
           committee.'''
        # Unlike m_showTextBased (see below), this method defines showability
        # for a distinct page on p_o, containing both fields "placesObjects" and
        # "prePlacesObjects".
        if 'placeObject' in o.config.unusedMeetingFields: return
        return o.showMonoView() if o.isTool else 'view'

    @classmethod
    def showObjectBasedPre(class_, o):
        '''Show object-based pre-places if configured on p_o being the tool or a
           committee.'''
        # Determining if object-based places are in use has already been
        # checked, at the page level, by m_showObjectBased.
        return o.config.preMeetingUsed()

    @classmethod
    def listActive(class_, meeting, name='place'):
        '''Return the list of active Place objects, ready to be selected as
           place for field p_meeting.<p_name> ("place" or "prePlace").'''
        # The list could be on the cache
        cache = meeting.cache
        key = f'active{name.capitalize()}s'
        if key in cache: return cache[key]
        com = meeting.getCommittee()
        r = [p for p in getattr(com, f'{name}sObjects') if p.state == 'active']
        # Ensure the currently selected place on p_meeting is part of the
        # result.
        place = getattr(meeting, f'{name}Object')
        if place and place not in r:
            r.insert(0, place)
        cache[key] = r
        return r

    @classmethod
    def showOn(class_, meeting, name='place'):
        '''Show p_meeting.<name>Object only if configured and if at least one
           Place active object has been found.'''
        # Object-based places or pre-places must be enabled
        if 'placeObject' in meeting.config.unusedMeetingFields: return
        # For field "prePlace", the notion of pre-meeting must be enabled
        if name == 'prePlace' and not meeting.config.preMeetingUsed(): return
        # Get the list of active places: it must not be empty
        places = class_.listActive(meeting, name)
        return Show.V_B if places else None

    @classmethod
    def computableDistances(class_, meeting, checkAF=True):
        '''Returns True if this p_meeting possesses a physical place from which
           distances can be computed, and if it is time to compute these
           distances (when p_checkAF is True).'''
        # If the remun. amout per km is not defined, it means that this remun.
        # type is disabled: do not compute distances.
        if not meeting.remunAmountKm: return
        # p_checkAF means: check than *a*ttendances are *f*rozen. If this must
        # be checked, return False if it is not the case yet.
        if checkAF and not meeting.attendanceIsFrozen(): return
        # Computing such distances is only possible when using a non-virtual
        # place object.
        place = meeting.placeObject
        return place and not place.virtual

    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    #                    Text-based class methods
    #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    @classmethod
    def showTextBased(class_, o, pre=False):
        '''Show text-based places if places objects are not in use'''
        config = o.config
        if 'placeObject' not in config.unusedMeetingFields: return
        return config.preMeetingUsed() if pre else True

    @classmethod
    def showTextOn(class_, meeting, name):
        '''Show, on this p_meeting, the text-based place field having this
           p_name ("place" or "prePlace") when text-based places are enabled
           and defined.'''
        # Show nothing if object-based places are in use
        if not class_.showTextBased(meeting): return
        # Show nothing if no place is encoded in the tool or committee
        if meeting.getCommittee().isEmpty(name + 's'): return
        # Show the field depending on value "_other" being used or not
        place = getattr(meeting, name)
        return Show.V_ if not place or place == '_other' else Show.V_B

    @classmethod
    def showTextOtherOn(class_, meeting, name):
        '''Show, on this p_meeting, the text-based "other" place field having
           this p_name ("place" or "prePlace") when text-based places are
           enabled and defined.'''
        # Show nothing if object-based places are in use
        if not class_.showTextBased(meeting): return
        # Show nothing if no place is encoded in the tool or committee
        if meeting.getCommittee().isEmpty(name + 's'): return
        return Show.V_B if getattr(meeting, name) == '_other' else Show.V_

    @classmethod
    def getDefaultText(class_, meeting, name='places'):
        '''Returns the 1st defined text-based place (from "places" or
           "prePlaces", depending on p_name) as defined on the committee or
           tool.'''
        places = getattr(meeting.getCommittee(), name)
        return places.split(bn)[0] if places else None

    @classmethod
    def list(class_, meeting, name='places'):
        '''Returns the list of places as defined on the committee or tool
           related to this p_meeting, in field "places" or "prePlaces"
           (depending on p_name).'''
        r = []
        container = meeting.getCommittee()
        places = getattr(container, name)
        if not places or not places.strip(): return r
        for place in places.split('\n'):
            cleanPlace = place.strip()
            if cleanPlace:
                r.append((cleanPlace, cleanPlace))
        if r:
            # Add option "other" to let the user encode another place in field
            # "Meeting.placeOther".
            r.append(('_other', meeting.translate('other')))
        return r

    @classmethod
    def getOn(class_, meeting, name='place', empty='?'):
        '''Get this p_meeting's place, as text'''
        place = getattr(meeting, name)
        if place:
            # It is stored in field "place" or "placeOther"
            r = getattr(meeting, f'{name}Other') if place == '_other' else place
        else:
            # It is stored in field "placeObject"
            place = getattr(meeting, f'{name}Object')
            r = place.title if place else None
        return r or empty

    @classmethod
    def cleanOn(class_, meeting):
        '''Ensure text-based fields *Other are cleaned when not used'''
        for name in ('place', 'prePlace'):
            place = getattr(meeting, name)
            if place != '_other':
                setattr(meeting, f'{name}Other', None)
#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
