"""Base models for poi application"""

import copy
import datetime
import mptt
import os

from django.conf import settings
from django.db import models
from django.template.defaultfilters import slugify
from django.template.loader import render_to_string
from tagging.fields import TagField
from tagging.models import Tag, TaggedItem

from rldjango.common.htmlfield import HtmlField
from rldjango.apps.imadj.fields import ImageField
from rldjango.apps.imadj.models import Image

from managers import SearchManager

try:
    from south.modelsinspector import add_introspection_rules
    add_introspection_rules([((HtmlField,),[],{"target": ["inline", {"is_value": True}],},),
    ], ["^rlextra\.django\.common\.htmlfield\.HtmlField"])
    add_introspection_rules([], ["^rlextra\.django\.apps\.imadj\.fields\.ImageField"])
except:
    pass

LOCATIONS_CATEGORIES = getattr(settings, 'DESTINFO_LOCATIONS_CATEGORIES', (
    ('unspecified', 'unspecified'),
    ('city', 'city'),
    ('country', 'country'),
    ('continent', 'continent'),
    ))

class ToRmlModel(models.Model):
    """
    Abstract class with universal to_rml method.
    """

    tags = TagField()
    
    class Meta: # pylint: disable-msg=C0111, R0201, W0232, R0903
        abstract = True

    def get_params(self, context):
        """
        Returns layout key from context and category attribute. Override this method if you
        need different behavior.
        """
        return context.get('layout', 'default'), getattr(self, 'category', 'default')

    def get_template_prefix(self): # pylint: disable-msg=R0201
        """
        Returns "obj" by default. Used for building template names. Override if
        you need different behavior.
        """
        return "obj"

    def get_template_suffix(self): # pylint: disable-msg=R0201
        """
        Returns _to_rml.rml Override if you need different behavior.
        """
        return "to_rml.rml"

    def get_default_template(self):
        """
        Returns default tempalte name: prefix + suffix . Override if you need different behavior.
        """
        return "%s_%s" % (self.get_template_prefix(), self.get_template_suffix())


    def get_template_choices(self, context):
        """
        Returns template choices. Based on layout and category. Override if you need different
        behavior.
        """
        
        layout, category = self.get_params(context)
        basic_template = self.get_default_template()
        category_layout_template = "%s_%s_%s_%s" % (self.get_template_prefix(), category, layout,
                                 self.get_template_suffix())
        layout_template = "%s_%s_%s" % (self.get_template_prefix(), layout,
                                 self.get_template_suffix())
        

        
        app_label = self._meta.app_label
        obj_name = self._meta.object_name.lower()
        
        template_choices = [
            'rml/%s/%s/%s' % (app_label, obj_name, category_layout_template),
            'rml/%s/%s' % (app_label, category_layout_template),
            'rml/%s' % category_layout_template,
            'rml/%s/%s/%s' % (app_label, obj_name, layout_template),
            'rml/%s/%s' % (app_label, layout_template),
            'rml/%s' % category_layout_template,
            'rml/%s/%s/%s' % (app_label, obj_name, basic_template),
            'rml/%s/%s' % (app_label, basic_template),
            'rml/%s' % basic_template,
            ]

        return template_choices

    def get_to_rml_images(self): # pylint: disable-msg=R0201
        """
        Returns list of images. You should override this if you want some images.
        """
        return []

    def pre_to_rml(self, context): # pylint: disable-msg=R0201
        """
        Pre hook that allows any modifications before to_rml.
        """
        return context

    def post_to_rml(self, rml): # pylint: disable-msg=R0201
        """
        Post hook that allows any modifications after to_rml.
        """
        return rml

    def get_uniq_id(self):
        """
        Used in toc references.
        """
        return "%s_%s_%s" % (self._meta.app_label, self._meta.object_name.lower(), self.pk)

    def to_rml(self, context=None):
        """
        Converts itself to rml. Template is chosen with similar rules than django admin.
        Template name can include category and layout name:
        <get_template_name_base>_<category>_<layout>.html
        Images are added to context via get_to_rml_images to to_rml_images.
        """
        if not context:
            context = {}

        context = self.pre_to_rml(context)

        template_choices = self.get_template_choices(context)
        context['to_rml_obj'] = self
        context['to_rml_images'] = self.get_to_rml_images()
        rml = render_to_string(template_choices, context).encode('utf-8')
        rml = self.post_to_rml(rml)
        return rml


class Location(ToRmlModel):
    """
    Generic model for locations.
    """

    name = models.CharField(max_length=200)
    description = HtmlField(target='block', max_length=2000, blank=True,
        default='')
    parent = models.ForeignKey('self', null=True, blank=True,
        related_name='children')
    latitude = models.FloatField(blank=True, null=True)
    longitude = models.FloatField(blank=True, null=True)
    map_zoom = models.PositiveSmallIntegerField(blank=True, null=True, help_text='Used if displaying a Google Map of a location')
    map_pixels_x = models.PositiveSmallIntegerField(blank=True, null=True, help_text='X coordinate for where it should appear on a designed bitmap map')
    map_pixels_y = models.PositiveSmallIntegerField(blank=True, null=True, help_text='Y coordinate for where it should appear on a designed bitmap map')
    

    objects = SearchManager()
    
    class Meta: # pylint: disable-msg=C0111, R0201, W0232, R0903
        ordering = ['tree_id', 'lft']


    def __unicode__(self):
        return self.name

    def get_static_google_map(self, width=550, height=367, zoom=8, maptype='roadmap', format='png'):
        """Return the filename for a Google static map matching the spec.

        As needed, it will fetch and save the map with a suitable filename.
        If we ever need to re-fetch a map, just delete it.

        an example map URL, expanded for readability, is
        http://maps.google.com/staticmap?  \
            markers=48.776958,9.181548,green &
            size=174x144 &
            maptype=roadmap &
            zoom=8 &
            key=YOUR_GOOGLE_MAPS_KEY

        The default values are for Hilton Garden Inn (Feb 2009), the first brand
        to use these in a PDF file.
        """

        def isbad(path=None, data=None):
            bad_images = set((
                '956605b57a4c3bd3cf20e0f3c5d111bb', # 165, 136, 14, jpg
                '2dbbf79df624130e90b9e772564a001e', # 165, 136, 8, jpg
            ))
            if not data:
                data = open(path, 'r').read()
            import md5
            return md5.new(data).hexdigest() in bad_images
        if self.map_zoom:
            zoom = self.map_zoom
        mapName = "location_%s_gmap_%dx%d_%d_%s.%s" % (str(self.id), width, height, zoom, maptype, format[0:3])
        mapDir = os.path.join(settings.MEDIA_ROOT, 'maps')
        fullPath = os.path.join(mapDir, mapName)
        if os.path.isfile(fullPath) and not isbad(path=fullPath):
            return fullPath

        if (not self.latitude) or (not self.longitude):
            return ''
        if not os.path.isdir(mapDir):
            os.makedirs(mapDir)

        gmapurl = u"http://maps.google.com/maps/api/staticmap?center=%0.6f,%0.6f&size=%dx%d&maptype=%s&format=%s&zoom=%d&sensor=false" % (
            self.latitude,
            self.longitude,
            width,
            height,
            maptype,
            format,
            zoom,
            )
        #print "fetching...", gmapurl
        #need to fetch it...
        import urllib2
        try:
            data = urllib2.urlopen(gmapurl).read()
        except urllib2.HTTPError, e:
            #print "error fetching map from %s" % gmapurl
            return ''
        if isbad(data=data):
            return ''

        #save the map for next time
        open(fullPath, 'wb').write(data)
        return fullPath


POI_SOURCES = getattr(settings, 'DESTINFO_POI_SOURCES', (
    ("human","input into our system by a human"),
    ("scrape","scraped from a web site"),
    ("wow_dest","what's on when destination guide"),
    ("wow_event","what's on when event feed"),
    ))

POI_CATEGORIES = getattr(settings, 'DESTINFO_POI_CATEGORIES', (
    ('shopping','shopping'),
    ('history','history'),
    ('kids_family', 'kids and family'),
    ('museum', 'museum'),
    ('restaurant', 'restaurant')
    ))

POI_LANGUAGES = getattr(settings, 'DESTINFO_LANGUAGES', (
    ('en','english'),
    ('es','spanish'),
    ('sk','slovak'),
    ))

    
class Poi(ToRmlModel):
    """
    Generic model for points of interest. 
    """

    location = models.ForeignKey(Location, db_index=True, blank=True, null=True)
    name = models.CharField(max_length=200)
    description = HtmlField(target='block', max_length=2000, blank=True,
        default='')
    language = models.CharField(max_length=20, choices=POI_LANGUAGES,
        default="en", db_index=True,
        help_text="Which language?")
    address = HtmlField(target='block', max_length=2000, blank=True,
        default='')
    phone = models.CharField(max_length=255, blank=True)
    fax = models.CharField(max_length=255, blank=True)
    email = models.EmailField(blank=True, null=True)
    website = models.CharField(max_length=255, blank=True)

    main_image = ImageField(size="poi_main_img", blank=True, null=True,
        related_name = 'image')
    thumbnail_image = ImageField(size="poi_thumbnail", blank=True, null=True,
        related_name = 'thumbnail')
    # parent = models.ForeignKey('self', null=True, blank=True,
    #     related_name='children')
    source = models.CharField(max_length=20, choices=POI_SOURCES,
        default="human", db_index=True,
        help_text="Where did the data come from?")
    category = models.CharField(max_length=100, choices=POI_CATEGORIES,
        db_index=True, help_text=("What this fact is about - "
                                  "description, restaurant etc"))
    original_id = models.CharField(max_length=10, blank=True,
        null=True, help_text=("An id which is meaningful in the context of "
                              "this item's original source (e.g. hilton's "
                              "pangea meeting room ID)"))
    original_url = models.URLField(blank=True, null=True,
        help_text=("If this came from a location on the web where it can be "
                   "checked, include it to help human auditors"))
    latitude = models.FloatField(blank=True, null=True)
    longitude = models.FloatField(blank=True, null=True)
    start = models.DateField(blank=True, null=True)
    end = models.DateField(blank=True, null=True)
    ongoing = models.NullBooleanField(blank=True, null=True)
    opening_hours = models.TextField(blank=True, null=True)
    created = models.DateTimeField(auto_now_add=True)
    created_by = models.CharField(max_length=30, default='system',
        help_text="Who first created it (or 'system')")
    updated = models.DateTimeField(auto_now=True)
    updated_by = models.CharField(max_length=30, default='system',
        help_text="Who last modified it")
    ordering = models.PositiveIntegerField(default=0)
    custom1 = models.CharField(max_length=100, null=True, blank=True,
        editable = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[0][2],
        verbose_name = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[0][1],
    )
    custom2 = models.CharField(max_length=100, null=True, blank=True,
        editable = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[1][2],
        verbose_name = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[1][1],
    )
    custom3 = models.CharField(max_length=100, null=True, blank=True,
        editable = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[2][2],
        verbose_name = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[2][1],
    )
    custom4 = models.CharField(max_length=100, null=True, blank=True,
        editable = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[3][2],
        verbose_name = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[3][1],
    )
    custom5 = models.CharField(max_length=100, null=True, blank=True,
        editable = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[4][2],
        verbose_name = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[4][1],
    )
    custom6 = models.CharField(max_length=100, null=True, blank=True,
        editable = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[5][2],
        verbose_name = settings.DESTINFO_POI_CUSTOM_ATTRIBUTES[5][1],
    )
    
    

    class Meta: # pylint: disable-msg=C0111, R0201, W0232, R0903
        verbose_name = 'Point of Interest'
        verbose_name_plural = 'Points of Interest'


    def __unicode__(self):
        return self.name

    def get_to_rml_images(self):
        """
        Overriden method to get images list.
        """
        if self.main_image:
            return [self.main_image, ]

        return []
    
    def __getattr__(self, val):
        try:
            key = [k for k, v, e in settings.DESTINFO_POI_CUSTOM_ATTRIBUTES if v == val][0]
            return getattr(self, key)
        except:
            raise AttributeError
    
    def pre_to_rml(self, context): # pylint: disable-msg=R0201
        """
        Prepare content blocks.
        """
        self.content_blocks = [         
            # {'title': 'Location',
            #  'content': self.location.name,},
            {'title': 'Address',
             'xhtml': self.address,},
            {'title': 'Tel',
             'content': self.phone,},
            {'title': 'Fax',
             'content': self.fax,},
            {'title': 'Email',
             'content': self.email,},
            {'title': 'Web',
             'content': self.original_url,},
            {'title': 'Description',
             'xhtml': self.description,},
            ]
        return context

NARRATIVE_POI_LAYOUT_CHOICES = (
    ('table', 'table'),
    ('thumbnail', 'thumbnail'),
    )

class Narrative(ToRmlModel):
    """
    Generic model for narrative.
    """
    slug = models.SlugField(null=False, blank=False, editable=False)
    name = models.CharField(max_length=200, null=False, blank=True)
    content = HtmlField(target='block', max_length=2000, blank=True, allowTables=True,
                        default='', help_text='Description content including imadj references')
                               
    parent = models.ForeignKey('self', null=True, blank=True, related_name='children')

    pois = models.ManyToManyField(Poi)
    poi_category = models.CharField(max_length=100, choices=POI_CATEGORIES, db_index=True,
                                    null=True, blank=True)
    poi_tags = models.ManyToManyField(Tag, null=True, blank=True)
    # TODO: choices to settings
    poi_layoyt = models.CharField(max_length=100, choices=NARRATIVE_POI_LAYOUT_CHOICES,
                                  db_index=True, null=True, blank=True)
    
    class Meta: # pylint: disable-msg=C0111, R0201, W0232, R0903
        ordering = ['tree_id', 'lft']

    def __unicode__(self):
        return self.name
        # return '%s: %s...' % (self.slug, self.content[:50])

    def save(self, force_insert=False, force_update=False, using=None):
        """
        Populate slug.
        """
        self.slug = slugify(self.name)
        super(Narrative, self).save(force_insert=force_insert, force_update=force_update,
                                    using=using)

    def get_imadj_tag(self):
        """
        Images are stored in imadj app and referenced via tags
        """
        return "narrative_%(id)s_%(slug)s" % self.__dict__

    def _get_images(self):
        """
        Returns list of images referenced through tag. Used in images property.
        """
        return Image.objects.filter(tags=self.get_imadj_tag)

    def _set_images(self, images):
        """
        Sets uniq tag to imadj objects. Used in images property.
        """
        for image in images:
            image.set_tag_text(self.get_imadj_tag())

    images = property(_get_images, _set_images)

    def get_to_rml_images(self):
        """
        Overriden method used in to_rml
        """
        return self.images

    def pre_to_rml(self, context): # pylint: disable-msg=R0201
        """
        Pre hook that allows any modifications before to_rml.
        """
        if self.poi_category:
            self.related_pois = Poi.objects.filter(category=self.poi_category)
        elif self.pois.all(): # pylint: disable-msg=E1101
            self.related_pois = self.pois.all() # pylint: disable-msg=E1101
        elif self.poi_tags.all():
            self.related_pois = TaggedItem.objects.get_union_by_model(Poi, self.poi_tags.all())
        else:
            self.related_pois = None

        
        
        if self.poi_layoyt:
            self.related_poi_context = copy.deepcopy(context)
            self.related_poi_context['layout'] = self.poi_layoyt
        else:
            self.related_poi_context = context
        
        return context
    
mptt.register(Location,)
mptt.register(Narrative,)
