import json
import datetime
import os
import io
import urllib.request
import urllib.parse
import urllib.error

from django.template.defaultfilters import slugify
from django.core.exceptions import ObjectDoesNotExist
from django.utils.encoding import force_text
from django.conf import settings

from photos.models import Photo, PhotoGallery

# from https://github.com/alexis-mignon/python-flickr-api
# terribly documented, but offers a good clean OOP approach if you're willing to figure it out...
import flickr_api
import flickrapi

# Required PIL classes may or may not be available from the root namespace depending on the installation
try:
    import Image
    import ImageFile
except ImportError:
    try:
        from PIL import Image
        from PIL import ImageFile
    except ImportError:
        raise ImportError("Could not import the Python Imaging Library.")

ImageFile.MAXBLOCK = 1000000

EXIF_PARAMS = {
    "FNumber": 'f/2.8',
    "Make": 'Apple',
    "Model": 'iPhone',
    "ExposureTime": '',
    "ISO": '',
    "FocalLength": '',
    "LensModel": '',
    'DateTimeOriginal': '2013:09:03 22:44:25'
}

class SyncFlickr():
    
    def __init__(self):
        self.flickr = flickrapi.FlickrAPI(settings.FLICKR_API_KEY, settings.FLICKR_API_SECRET,format='parsed-json')


    def sync_sets(self, *args, **kwargs):
        p = self.flickr.photosets.getList(user_id='85322932@N00')
        disregard = [
            'POTD 2008',
            'Snow Day',
            'Wedding',
            'Some random stuff',
            'Lilah & Olivia',
            '6 months+',
            '6-9 months',
            '9-18 months',
        ]
        for photoset in p['photosets']['photoset']:
            if photoset['title']['_content'] in disregard:
                pass
            else:
                try:
                    row = PhotoGallery.objects.get(set_id__exact=photoset['id'])
                    print(('%s %s %s' % ('already have', row.set_title, 'moving on...')))
                    # okay it already exists, but is it up-to-date?
                    self.get_photos_in_set(photoset['id'],row)
                except ObjectDoesNotExist:
                    s = PhotoGallery.objects.get_or_create(
                        set_id=force_text(photoset['id']),
                        set_title=force_text(photoset['title']['_content']),
                        set_desc=force_text(photoset['description']['_content']),
                        set_slug=slugify(force_text(photoset['title']['_content'])[:40]),
                        primary=force_text(photoset['primary']),
                        pub_date=datetime.datetime.fromtimestamp(float(photoset['date_create']))
                    )

                #get_photos_in_set(photoset, s)
                #create the gallery thumbnail image:
                #photo = Photo.objects.get(flickr_id__exact=str(photoset['primary']))
                #make_gallery_thumb(photo, s)



    def get_photos_in_set(self, flickr_id, photoset):
        photos = self.flickr.photosets.getPhotos(photoset_id=flickr_id)
        for photo in photos['photoset']['photo']:
            try:
                p = Photo.objects.get(flickr_id__exact=str(photo['id']))
            except ObjectDoesNotExist:
                p = self.get_photo(photo['id'])
            if p.is_public:
                pass #photoset.photos.add(p)
                #slideshow_image(p, 1000, 800, 95)
            print(p)

    def get_photo(self, photo_id):
        photo = self.flickr.photos.getInfo(photo_id=photo_id)
        info = photo['photo']
        try:
            geo = self.flickr.photos.geo.getLocation(photo_id=photo_id)
            location, region = self.get_geo(float(geo['photo']['location']['latitude']), float(geo['photo']['location']['longitude']))
        except KeyError:
            print("no effing geodata asshat")
        exif = self.exif_handler(self.flickr.photos.getExif(photo_id=photo_id)['photo']['exif'])
        p, created = Photo.objects.get_or_create(
            title=info['title']['_content'],
            flickr_id=info['id'],
            flickr_owner=info['owner']['nsid'],
            flickr_server=info['server'],
            flickr_secret=info['secret'],
            flickr_originalsecret=info['originalsecret'],
            flickr_farm=info['farm'],
            pub_date=self.flickr_datetime_to_datetime(exif["DateTimeOriginal"].replace(':', '-', 2)),
            description=info['description']['_content'],
            exif_aperture=exif['FNumber'],
            exif_make=exif['Make'],
            exif_model=exif['Model'],
            exif_exposure=exif['ExposureTime'],
            exif_iso=exif['ISO'],
            exif_lens=exif['LensModel'],
            exif_focal_length=exif['FocalLength'],
            exif_date=self.flickr_datetime_to_datetime(exif["DateTimeOriginal"].replace(':', '-', 2)),
            lat=float(geo['photo']['location']['latitude']),
            lon=float(geo['photo']['location']['longitude']),
            region=region,
            location=location,
        )
        if created:
            for tag in info['tags']['tag']:
                p.tags.add(tag['raw'])
            p.save()

        local = FlickrImage()
        local.make_local_copies(p)
        #retina image:
        #slideshow_image(p, 2000, 1600, 75)
        #normal image
        print("grabbing... "+p.title)
        return p


    def sync_flickr_photos(self, *args, **kwargs):
        photos = self.flickr.people.getPhotos(user_id="85322932@N00", extras="date_upload,date_taken,geo")
        for photo in photos['photos']['photo']:
            try:
                row = Photo.objects.get(flickr_id=photo['id'], flickr_secret=photo['secret'])
                print('already have ' + photo['id'] + ' moving on')
            except ObjectDoesNotExist:
                p = self.get_photo(photo['id'])



    """
    ################################################
    ## Various meta data and geo helper functions ##
    ################################################
    """

    def exif_handler(self, data):
        converted = {}
        try:
            for t in data:
                converted[t['tag']] = t['raw']['_content']
        except:
            pass
        for k, v in list(EXIF_PARAMS.items()):
            if k not in converted:
                converted[k] = v
        return converted


    def flickr_datetime_to_datetime(self, fdt):
        from datetime import datetime
        from time import strptime
        date_parts = strptime(fdt, '%Y-%m-%d %H:%M:%S')
        return datetime(*date_parts[0:6])


    def get_geo(self, lat, lon):
        from locations.models import Location, Region
        from django.contrib.gis.geos import Point
        pnt_wkt = Point(lon, lat)
        try:
            location = Location.objects.get(geometry__contains=pnt_wkt)
        except Location.DoesNotExist:
            location = None
        try:
            region = Region.objects.get(geometry__contains=pnt_wkt)
        except Region.DoesNotExist:
            region = None
        return location, region






class FlickrImage():
    """
    ## Photo retrieval functions to pull down images from Flickr servers ##
    """

    def slideshow_image(self, photo, max_width, max_height, quality):
        slide_dir = settings.IMAGES_ROOT + '/slideshow/' + photo.pub_date.strftime("%Y")
        if not os.path.isdir(slide_dir):
            os.makedirs(slide_dir)

        # Is it a retina image or not?
        if max_width >= 1001 or max_height >= 801:
            filename = '%s/%sx2.jpg' % (slide_dir, photo.flickr_id)
        else:
            filename = '%s/%s.jpg' % (slide_dir, photo.flickr_id)

        flickr_photo = photo.get_original_url()
        fname = urllib.request.urlopen(flickr_photo)
        im = io.StringIO(fname.read().decode('UTF-8'))  # constructs a StringIO holding the image
        img = Image.open(im)
        cur_width, cur_height = img.size
        #if image landscape
        if cur_width > cur_height:
            new_width = max_width
            #check to make sure we aren't upsizing
            if cur_width > new_width:
                ratio = float(new_width) / cur_width
                x = (cur_width * ratio)
                y = (cur_height * ratio)
                resized = img.resize((int(x), int(y)), Image.ANTIALIAS)
                resized.save(filename, 'JPEG', quality=quality, optimize=True)
            else:
                img.save(filename)
        else:
            #image portrait
            new_height = max_height
            #check to make sure we aren't upsizing
            if cur_height > new_height:
                ratio = float(new_height) / cur_height
                x = (cur_width * ratio)
                y = (cur_height * ratio)
                resized = img.resize((int(x), int(y)), Image.ANTIALIAS)
                resized.save(filename, 'JPEG', quality=quality, optimize=True)
            else:
                img.save(filename)
        photo.slideshowimage_width = photo.get_width
        photo.slideshowimage_height = photo.get_height
        photo.slideshowimage_margintop = photo.get_margin_top
        photo.slideshowimage_marginleft = photo.get_margin_left
        photo.save()
        #now resize the local copy


    def make_local_copies(self,photo):
        orig_dir = settings.IMAGES_ROOT + '/flickr/full/' + photo.pub_date.strftime("%Y")
        if not os.path.isdir(orig_dir):
            os.makedirs(orig_dir)
        full = photo.get_original_url()
        fname = urllib.request.urlopen(full)
        im = io.StringIO(fname.read().decode('UTF-8'))  # constructs a StringIO holding the image
        img = Image.open(im)
        local_full = '%s/%s.jpg' % (orig_dir, photo.flickr_id)
        img.save(local_full)
        #save large size
        large_dir = settings.IMAGES_ROOT + '/flickr/large/' + photo.pub_date.strftime("%Y")
        if not os.path.isdir(large_dir):
            os.makedirs(large_dir)
        large = photo.get_large_url()
        fname = urllib.request.urlopen(large)
        im = io.StringIO(fname.read().decode('UTF-8'))  # constructs a StringIO holding the image
        img = Image.open(im)
        local_large = '%s/%s.jpg' % (large_dir, photo.flickr_id)
        if img.format == 'JPEG':
            img.save(local_large)
        #save medium size
        med_dir = settings.IMAGES_ROOT + '/flickr/med/' + photo.pub_date.strftime("%Y")
        if not os.path.isdir(med_dir):
            os.makedirs(med_dir)
        med = photo.get_medium_url()
        fname = urllib.request.urlopen(med)
        im = io.StringIO(fname.read().decode('UTF-8'))  # constructs a StringIO holding the image
        img = Image.open(im)
        local_med = '%s/%s.jpg' % (med_dir, photo.flickr_id)
        img.save(local_med)


    def make_gallery_thumb(self, photo, set):
        crop_dir = settings.IMAGES_ROOT + '/gallery_thumbs/'
        if not os.path.isdir(crop_dir):
            os.makedirs(crop_dir)
        remote = photo.get_original_url()
        print(remote)
        fname = urllib.request.urlopen(remote)
        im = io.StringIO(fname.read().decode('UTF-8'))  # constructs a StringIO holding the image
        img = Image.open(im)
        #calculate crop:
        cur_width, cur_height = img.size
        new_width, new_height = 291, 350
        ratio = max(float(new_width) / cur_width, float(new_height) / cur_height)
        x = (cur_width * ratio)
        y = (cur_height * ratio)
        xd = abs(new_width - x)
        yd = abs(new_height - y)
        x_diff = int(xd / 2)
        y_diff = int(yd / 2)
        box = (int(x_diff), int(y_diff), int(x_diff + new_width), int(y_diff + new_height))

        # create resized file
        resized = img.resize((int(x), int(y)), Image.ANTIALIAS).crop(box)
        # save resized file
        resized_filename = '%s/%s.jpg' % (crop_dir, set.id)
        try:
            if img.format == 'JPEG':
                resized.save(resized_filename, 'JPEG', quality=95, optimize=True)
            else:
                resized.save(resized_filename)
        except IOError as e:
            if os.path.isfile(resized_filename):
                os.unlink(resized_filename)
            raise e
        os.unlink(img)