# ===========================================================================
# Copyright (C) 2006 Ancient World Mapping Center
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# 
# About Pleiades
# --------------
#
# Pleiades is an international research network and associated web portal and
# content management system devoted to the study of ancient geography. 
#
# See http://icon.stoa.org/trac/pleiades/wiki.
#
# Funding for the creation of this software was provided by a grant from the 
# U.S. National Endowment for the Humanities (http://www.neh.gov).
# ===========================================================================

from zope.interface import implements
from zope.app.annotation.interfaces import IAnnotations

from persistent.dict import PersistentDict

from Products.ATContentTypes.interface.topic import IATTopic
from Products.ATContentTypes.interface.folder import IATFolder
from Products.ATContentTypes.interface.folder import IATBTreeFolder

from Products.PleiadesGeocoder.interfaces import IGeoItemSimple \
    , IGeoCollectionSimple, IGeoreferencedEvent

import logging

logger = logging.getLogger('PleiadesGeocoder.geo')


class GeoreferencedEvent(object):
    """Event to notify that object has been georeferenced.
    """
    implements(IGeoreferencedEvent)
    
    def __init__(self, context):
        self.context = context


ANNO_KEY = 'geocoder.simple.item'

class GeoItemSimple(object):
    
    """Python expression of a GeoRSS simple item.
    """
    implements(IGeoItemSimple)
   
    def __init__(self, context):
        """Initialize adapter."""
        self.context = context
        annotations = IAnnotations(context)
        self.georef = annotations.get(ANNO_KEY, None)
        if not self.georef:
            annotations[ANNO_KEY] = PersistentDict()
            self.georef = annotations[ANNO_KEY]
            self.georef['geometryType'] = None
            self.georef['spatialCoordinates'] = None

    def isGeoreferenced(self):
        """Return True if the object is "on the map"."""
        g = self.georef
        return bool(g['geometryType']) and bool(g['spatialCoordinates'])

    @property
    def geom_type(self):
        gtype = self.georef['geometryType']
        if gtype == 'point':
            return 'Point'
        elif gtype == 'line':
            return 'LineString'
        elif gtype == 'polygon':
            return 'Polygon'
        else:
            return None

    @property
    def coords(self):
        if not self.isGeoreferenced():
            return None
        values = [float(v) for v in self.georef['spatialCoordinates'].split()]
        nvalues = len(values)
        npoints = nvalues/3
        coords = []
        for i in range(npoints):
            coords.append((values[3*i+1], values[3*i], values[3*i+2]))
        geomtype = self.georef['geometryType']
        if geomtype == 'point':
            return coords[0]
        elif geomtype == 'line':
            return tuple(coords)
        elif geomtype == 'polygon':
            return (tuple(coords),)

    def setGeoInterface(self, geomtype, coordinates):
        # set geometry type
        if geomtype not in ['Point', 'LineString', 'Polygon']:
            raise ValueError, \
            """%s is invalid. Supported geometry types are: 'Point', 'LineString', 'Polygon'""" % (geomtype)
        gtype = geomtype.lower()
        if gtype == 'linestring':
            gtype = 'line'
        self.georef['geometryType'] = gtype

        if geomtype == 'Point':
            longlat_coords = (coordinates,)
        elif geomtype == 'Polygon':
            longlat_coords = coordinates[0]
        else:
            longlat_coords = coordinates

        value = ''
        for p in longlat_coords:
            if len(p) == 3:
                value = ' '.join([value, "%f %f %f" % (p[1], p[0], p[2])])
            elif len(p) == 2:
                value = ' '.join([value, "%f %f 0.0" % (p[1], p[0])])
            else:
                raise ValueError, \
                "Insufficient number of ordinates: %s" % str(p)
        self.georef['spatialCoordinates'] = value.lstrip()

    @property
    def __geo_interface__(self):
        context = self.context
        return {
            'id': context.getId(),
            'properties': {
                'title': context.title_or_id(),
                'description': context.Description(),
                'link': context.absolute_url(),
                },
            'geometry': {'type': self.geom_type, 'coordinates': self.coords}
            }


class GeoCollectionSimple(object):
    
    """Adapter for Folderish collections of GeoItemSimple.
    """
    implements(IGeoCollectionSimple)
    
    def __init__(self, context):
        """Initialize."""
        self.context = context
        
    def geoItems(self):
        if IATTopic.providedBy(self.context):
            for ob in self.context.queryCatalog():
                item = self._adapt(ob.getObject())
                if item is not None:
                    yield item
                else:
                    continue
        elif (IATFolder.providedBy(self.context) or
              IATBTreeFolder.providedBy(self.context)):
            for ob in self.context.listFolderContents():
                item = self._adapt(ob)
                if item is not None:
                    yield item
                else:
                    continue
        # Last resort: single objects may yield geoItems
        else:
            item = self._adapt(self.context)
            if item is not None:
                yield item

    def _adapt(self, ob):
        try:
            item = IGeoItemSimple(ob)
            if item.isGeoreferenced():
                return item
            else:
                logger.debug("%s is not georeferenced" % item)
        except TypeError:
            logger.debug("Could not adapt %s to IGeoItemSimple" % self.context)
        return None


