from django.db import models
from django.db.models import F
from django.core.exceptions import ObjectDoesNotExist
from django.core.files.base import ContentFile
from navigator.models import Object,genMWO
from django.http import HttpResponse,HttpResponseRedirect
from django.views.decorators.csrf import csrf_protect
from django import forms
from django.shortcuts import render
from obstool import settings
import datetime
#import plot_ams_phases,plot_ams_HA
import plot_skyview_bokeh as plot_skyview
import plot_objs_bokeh as plot_objs
import StringIO
#import Image, ImageDraw
from PIL import Image,ImageDraw
from numpy import argsort,mean
try:
   import ephem3 as ephem
except:
   import ephem
from math import pi
import get_planets
import utils
import query

def type_included(typ, show_types):
   if 'All' in show_types:
      return True
   if typ[0:2] == 'G-':
      if u'Galaxies' in show_types:
         return True
   if typ in ['E/RN','EN','EN-OC','E/DN','RN','SNR'] and \
         u'Nebulae' in show_types:
         return True
   if typ == 'GC' and u'GC' in show_types:
      return True
   if typ == 'SS' and u'Planets' in show_types:
      return True
   if typ == 'PN' and u'PNe' in show_types:
      return True
   if typ == 'OC' and u'OCs' in show_types:
      return True
   if typ == 'QSO' and u'QSOs' in show_types:
      return True
   if typ in ['Double','Triple'] and u"Stars" in show_types:
      return True
   return False

rotations = {'90':Image.ROTATE_90,
             '180':Image.ROTATE_180,
             '270':Image.ROTATE_270,
             '-90':Image.ROTATE_270}

TYPE_CHOICES = (
      ('All','All'),
      ('Galaxies','Galaxies'),
      ('Planets','Planets'),
      ('Stars','Stars'),
      ('PNe','PNe'),
      ('Nebulae','Nebulae'),
      ('GCs','GCs'),
      ('OCs','OCs'),
      ('QSOs','QSOs'))


class FilterForm(forms.Form):
   ha_high = forms.FloatField(min_value=0.0, required=False,
         label='|HA| <', initial=settings.HA_SOFT_LIMIT,
         widget=forms.TextInput(attrs={'size':'5'}))
   rating_low = forms.IntegerField(required=False,
         label='rating >', initial=0,
         widget=forms.TextInput(attrs={'size':'5'}))
   show_types = forms.MultipleChoiceField(required=False,
                      choices=TYPE_CHOICES,
                      initial=['All'],
                      widget=forms.CheckboxSelectMultiple)
   only_visible = forms.BooleanField(required=False, label='Only Visible')
   # Settings
   epoch = forms.DateTimeField(required=False, 
         widget=forms.TextInput(attrs={'size':20}),
         input_formats=['%Y-%m-%d %H:%M'])
   tz_offset = forms.FloatField(required=False, initial=0,
         widget=forms.TextInput(attrs={'size':'5'}))
                
   new_window = forms.BooleanField(required=False, initial=False)
   auto_reload = forms.BooleanField(required=False, initial=True)

class AddObjectForm(forms.Form):
   object_name = forms.CharField(
         required=False, label='Object Name')
   object_coordinates = forms.CharField(
         required=False, label='Object Coordinates (J2000)')
   resolve_choice = forms.ChoiceField(
         choices=(('simbad','Simbad'),('ned','NED')), 
         label='Select search facility', required=True)

   def clean(self):
      cleaned_data = super(AddObjectForm, self).clean()
      name = cleaned_data.get('object_name').strip()
      coords = cleaned_data.get('object_coordinates').strip()
      if (name and coords) or not (name or coords):
         raise forms.ValidationError("Specify only object name or coordinates")
      return cleaned_data


def get_current_time(request):
   '''Get the current time. This could be 'now' or stored in the session'''
   epoch = None
   tz_offset = 0
   if 'object_list_form' in request.session:
      #epoch = request.session['object_list_form']['epoch'].encode('ascii','ignore')
      epoch = request.session['object_list_form']['epoch']
      tz_offset = float(request.session['object_list_form']['tz_offset'])
      if tz_offset is None:
         tz_offset = 0
      if not epoch:
         date = ephem.now()
      else:
         date = ephem.Date(epoch) - tz_offset*ephem.hour
   else:
      date = ephem.now()

   return date

def telescope_position(obj_name,date):
   '''Given an object name and date, return the precessed RA, DEC, hour-angle,
   altitude, and azimuth as strings.
   '''
   obj = Object.objects.get(name=obj_name)
   obj.epoch = date
   return (obj.PrecRAh(),
           obj.PrecDecd(),
           ephem.hours(obj.hour_angle()*15.0*pi/180.),
           "%.2f" % (obj.altitude()),
           "%.2f" % (obj.azimuth()))

def index(request):
   only_visible = None
   ha_high = settings.HA_SOFT_LIMIT
   epoch = None
   rating_low = None
   new_window = False
   auto_reload = True
   tz_offset = 0
   #Default:  nothing posted and no session info
   cur_tel_obj = request.session.get('cur_tel_obj', 'Park')
   prev_tel_obj = request.session.get('prev_tel_obj', 'Park')
   selected_tab = request.session.get('selected_tab', 'table')
   show_types = ['All']
   form = FilterForm()

   if request.method == "POST":
      if request.POST.get('action','') == 'Update':
         form = FilterForm(request.POST)
         # save this form info into the session cache
         if form.is_valid():
            request.session['object_list_form'] = form.cleaned_data.copy()
         #newd = {}
         #newd.update(request.POST)
         #newd['show_types'] = request.POST.get('show_types')
         #request.session['object_list_form'] = newd
      elif request.POST.get('action','') == 'Park':
         request.session['cur_tel_obj'] = 'Park'
         cur_tel_obj = 'Park'
      else:
         # The reset button was called
         form = FilterForm()
         if 'object_list_form' in request.session:
            del request.session['object_list_form']
   if 'object_list_form' in request.session:
      form = FilterForm(request.session['object_list_form'])

   if form.is_valid():
      only_visible = form.cleaned_data.get('only_visible',None)
      ha_high = form.cleaned_data.get('ha_high',settings.HA_SOFT_LIMIT)
      show_types = form.cleaned_data.get('show_types', ['All'])
      rating_low = form.cleaned_data.get('rating_low',None)
      epoch = form.cleaned_data.get('epoch',None)
      if epoch is not None: 
         if isinstance(epoch, basestring):
            epoch = epoch.encode('ascii','ignore')
      tz_offset =form.cleaned_data.get('tz_offset', 0)
      new_window = form.cleaned_data.get('new_window',False)
      auto_reload = form.cleaned_data.get('auto_reload',False)

   if tz_offset is None:
      tz_offset = 0
   if epoch:
      date = ephem.Date(ephem.Date(epoch) - tz_offset*ephem.hour)
   else:
      date = ephem.now()

   # Now deal with telescope position
   tel_RA,tel_DEC,tel_ha,tel_alt,tel_az = telescope_position(cur_tel_obj, date)
   # Also see if window is to be displayed:
   module_display = request.session.get('module_display', {});

   obs = genMWO(date)
   sid_time = str(obs.sidereal_time())
   # Let's be a little smarter here. If we are imposing telescope limits
   # or an hour-angle limit, that will cut down on objects
   sql = 'SELECT * from navigator_object'
   RAoffset = obs.sidereal_time()*180/pi    # Now in degrees
   if (only_visible is not None and only_visible):
      sql += " WHERE (RA-%s > %s AND RA-%s < %s AND DEC > %s) or objtype = 'SS'"
      l = [RAoffset, -settings.HA_LIMIT*15, RAoffset, settings.HA_LIMIT*15,
            settings.DEC_LIMIT]
   elif ha_high is not None:
      sql += " WHERE (RA-%s > %s AND RA-%s < %s) OR objtype = 'SS'"
      l = [RAoffset, -ha_high*15, RAoffset, ha_high*15]
   else:
      sql += " WHERE (RA-%s > -90 AND RA-%s < 90) OR DEC > %s OR objtype = 'SS'"
      l = [RAoffset, RAoffset, 90-obs.lat*180/pi]

   if rating_low is not None:
      sql += " AND rating >= %s"
      l.append(rating_low)
   obj_list = Object.objects.raw(sql, l)

   if show_types is not None:
      obj_list = [obj for obj in obj_list if \
            type_included(obj.objtype,show_types)]
   newlist = []
   for obj in obj_list:  
      obj.epoch = date
      if obj.objtype == 'SS':
         if only_visible is not None and only_visible and not obj.visible():
            continue