# All the functionality needed to connect to Runkeeper

# Python
import oauth2 as oauth
import cgi
import requests
import requests.auth
import json
from django.utils import timezone
from datetime import datetime
import numpy as np
from dateutil import parser
import time
import math
from math import sin,cos,atan2,sqrt
import os,sys

# Django
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect, HttpResponse,JsonResponse
from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required

# Project
# from .models import Profile
from rowingdata import rowingdata
import pandas as pd
from rowers.models import Rower,Workout,checkworkoutuser

from rowsandall_app.settings import (
    C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
    STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
    RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET,RUNKEEPER_REDIRECT_URI,
    )

# Custom error class - to raise a NoTokenError
class RunKeeperNoTokenError(Exception):
    def __init__(self,value):
	self.value=value

    def __str__(self):
	return repr(self.value)

# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
def ewmovingaverage(interval,window_size):
    # Experimental code using Exponential Weighted moving average

    try:
        intervaldf = pd.DataFrame({'v':interval})
        idf_ewma1 = intervaldf.ewm(span=window_size)
        idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
        
        i_ewma1 = idf_ewma1.mean().ix[:,'v']
        i_ewma2 = idf_ewma2.mean().ix[:,'v']

        interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
        interval2 = np.mean( interval2, axis=0) # average
    except ValueError:
        interval2 = interval

    return interval2

from utils import geo_distance


# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):

    response = {
	"errors": [
	    {
		"code": str(exc),
		"detail": message,
		}
	    ]
	}

    res = HttpResponse(message)
    res.status_code = 401
    res.json = json.dumps(response)

    return res

# Checks if user has SportTracks token, renews them if they are expired
def runkeeper_open(user):
    r = Rower.objects.get(user=user)
    if (r.runkeepertoken == '') or (r.runkeepertoken is None):
	s = "Token doesn't exist. Need to authorize"
	raise RunKeeperNoTokenError("User has no token")
    else:
        thetoken = r.runkeepertoken
        
    return thetoken

# Exchange access code for long-lived access token
def get_token(code):
    client_auth = requests.auth.HTTPBasicAuth(RUNKEEPER_CLIENT_ID, RUNKEEPER_CLIENT_SECRET)
    post_data = {"grant_type": "authorization_code",
                 "code": code,
                 "redirect_uri": RUNKEEPER_REDIRECT_URI,
		 "client_secret": RUNKEEPER_CLIENT_SECRET,
		 "client_id":RUNKEEPER_CLIENT_ID,
		 }
    headers = {'user-agent': 'sanderroosendaal'}
    response = requests.post("https://runkeeper.com/apps/token",
                             data=post_data,
			     headers=headers)
    try:
        token_json = response.json()
        thetoken = token_json['access_token']
    except KeyError:
        thetoken = 0

    return thetoken

# Make authorization URL including random string
def make_authorization_url(request):
    # Generate a random string for the state parameter
    # Save it for use later to prevent xsrf attacks
    from uuid import uuid4
    state = str(uuid4())

    params = {"client_id": RUNKEEPER_CLIENT_ID,
              "response_type": "code",
              "redirect_uri": RUNKEEPER_REDIRECT_URI,
	      }
    import urllib
    url = "https://www.runkeeper.com/opps/authorize" +urllib.urlencode(params)

    return HttpResponseRedirect(url)

# Get list of workouts available on Runkeeper
def get_runkeeper_workout_list(user):
    r = Rower.objects.get(user=user)
    if (r.runkeepertoken == '') or (r.runkeepertoken is None):
	s = "Token doesn't exist. Need to authorize"
	return custom_exception_handler(401,s)
    else:
	# ready to fetch. Hurray
	authorizationstring = str('Bearer ' + r.runkeepertoken)
	headers = {'Authorization': authorizationstring,
		   'user-agent': 'sanderroosendaal',
		   'Content-Type': 'application/json'}
	url = "https://api.runkeeper.com/fitnessActivities"
	s = requests.get(url,headers=headers)

    return s

# Get workout summary data by Runkeeper ID
def get_runkeeper_workout(user,runkeeperid):
    r = Rower.objects.get(user=user)
    if (r.runkeepertoken == '') or (r.runkeepertoken is None):
	return custom_exception_handler(401,s)
	s = "Token doesn't exist. Need to authorize"
    else:
	# ready to fetch. Hurray
	authorizationstring = str('Bearer ' + r.runkeepertoken)
	headers = {'Authorization': authorizationstring,
		   'user-agent': 'sanderroosendaal',
		   'Content-Type': 'application/json'}
	url = "https://api.runkeeper.com/fitnessActivities/"+str(runkeeperid)
	s = requests.get(url,headers=headers)

    return s

# Create Workout Data for upload to SportTracks
def createrunkeeperworkoutdata(w):
    filename = w.csvfilename
    try:
        row = rowingdata(filename)
    except:
        return 0
    
    averagehr = int(row.df[' HRCur (bpm)'].mean())
    maxhr = int(row.df[' HRCur (bpm)'].max())
    duration = w.duration.hour*3600
    duration += w.duration.minute*60
    duration += w.duration.second
    duration += +1.0e-6*w.duration.microsecond

    # adding diff, trying to see if this is valid
    #t = row.df.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)']
    t = row.df.ix[:,'TimeStamp (sec)'].values-row.df.ix[0,'TimeStamp (sec)']
    t[0] = t[1]

    d = row.df.ix[:,'cum_dist'].values
    d[0] = d[1]
    t = t.astype(int)
    d = d.astype(int)
    spm = row.df[' Cadence (stokes/min)'].astype(int)
    spm[0] = spm[1]
    hr = row.df[' HRCur (bpm)'].astype(int)
    
    haslatlon=1
    
    try:
	lat = row.df[' latitude'].values
	lon = row.df[' longitude'].values
        if not lat.std() and not lon.std():
            haslatlon = 0
    except KeyError:
	haslatlon = 0

    # path data
    if haslatlon:
        locdata = [] 
        for e in zip(t,lat,lon):
            point = {'timestamp':e[0],
                     'latitude':e[1],
                     'longitude':e[2],
                     'altitude':0,
                     "type":"gps"}
            locdata.append(point)

    hrdata = []
    for e in zip(t,hr):
        point = {'timestamp':e[0],
                 'heart_rate':e[1]
                 }
        hrdata.append(point)

    distancedata = []
    for e in zip(t,d):
        point = {'timestamp':e[0],
                 'distance':e[1]
                 }
        distancedata.append(point)

    start_time = w.startdatetime.strftime("%a, %d %b %Y %H:%M:%S")

    try:
        newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
    except TypeError:
        newnotes = 'from '+w.workoutsource+' via rowsandall.com'
        
    if haslatlon:
	data = {
	    "type": "Rowing",
	    "start_time": start_time,
	    "total_distance": int(w.distance),
	    "duration": duration,
	    "notes": newnotes,
	    "average_heart_rate": averagehr,
	    "path": locdata,
	    "distance": distancedata,
	    "heart_rate": hrdata,
            "post_to_twitter":"false",
            "post_to_facebook":"false",
	    }
    else:
	data = {
	    "type": "Rowing",
	    "start_time": start_time,
	    "total_distance": int(w.distance),
	    "duration": duration,
	    "notes": newnotes,
	    "avg_heartrate": averagehr,
	    "distance": distancedata,
	    "heart_rate": hrdata,
            "post_to_twitter":"false",
            "post_to_facebook":"false",
	    }


    return data

# Obtain Runkeeper Workout ID from the response returned on successful
# upload
def getidfromresponse(response):
    uri = response.headers["Location"]
    id = uri[len(uri)-9:]
    
    return int(id)

def geturifromid(access_token,id):
    authorizationstring = str('Bearer ' + access_token)
    headers = {'Authorization': authorizationstring,
	       'user-agent': 'sanderroosendaal',
	       'Content-Type': 'application/json'}
    import urllib
    url = "https://api.runkeeper.com/fitnessActivities/"+str(id)
    response = requests.get(url,headers=headers)
    try:
        me_json = response.json()
    except:
        return ''

    try:
        res = me_json['uri']
    except KeyError:
        res = ''

    return res

    
# Get user id, having access token
# Handy for checking if the API access is working
def get_userid(access_token):
    authorizationstring = str('Bearer ' + access_token)
    headers = {'Authorization': authorizationstring,
	       'user-agent': 'sanderroosendaal',
	       'Content-Type': 'application/json'}
    import urllib
    url = "https://api.runkeeper.com/user"
    response = requests.get(url,headers=headers)
    

    try:
        me_json = response.json()
    except:
        return ''

    try:
        res = me_json['userID']
    except KeyError:
        res = ''

    return str(res)

def workout_runkeeper_upload(user,w):
    message = "Uploading to Runkeeper"
    rkid = 0

    r = w.user

    
    thetoken = runkeeper_open(r.user)

    # ready to upload. Hurray

    if (checkworkoutuser(user,w)):
	data = createrunkeeperworkoutdata(w)
        if not data:
            message = "Data error in Runkeeper Upload"
            rkid = 0
            return message, rkid
        
	authorizationstring = str('Bearer ' + thetoken)
	headers = {'Authorization': authorizationstring,
		   'user-agent': 'sanderroosendaal',
		   'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
                   'Content-Length':'nnn'}

	url = "https://api.runkeeper.com/fitnessActivities"
	response = requests.post(url,headers=headers,data=json.dumps(data))

	# check for duplicate error first
	if (response.status_code == 409 ):
	    message = "Duplicate error"
	    w.uploadedtorunkeeper = -1
            rkid = -1
	    w.save()
            return message, rkid
	elif (response.status_code == 201 or response.status_code==200):
	    rkid = getidfromresponse(response)
            rkuri = geturifromid(thetoken,rkid)
	    w.uploadedtorunkeeper = rkid
	    w.save()
            return 'Successfully synchronized to Runkeeper',rkid
	else:
	    s = response
	    message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
            rkid = 0
            return message, rkid
		
    else:
	message = "You are not authorized to upload this workout"
        rkid = 0
        return message, rkid

    return message,rkid
