# 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
from datetime import timedelta
import numpy as np
from dateutil import parser
import time
import math
import gzip
from math import sin,cos,atan2,sqrt
import os,sys
import urllib
import base64
from io import BytesIO
from time import strftime
# 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
#from django.contrib import messages

# 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,
    TP_CLIENT_ID, TP_CLIENT_SECRET,
    TP_REDIRECT_URI,TP_CLIENT_KEY,
    )

tpapilocation = "https://api.trainingpeaks.com"

from celery import Celery,app
from django_rq import job
import time
from async_messages import message_user,messages



# Custom error class - to raise a NoTokenError
class TPNoTokenError(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 UnderArmour token, renews them if they are expired
def tp_open(user):
    r = Rower.objects.get(user=user)
    if (r.tptoken == '') or (r.tptoken is None):
	s = "Token doesn't exist. Need to authorize"
	raise TPNoTokenError("User has no token")
    else:
	if (timezone.now()>r.tptokenexpirydate):
	    res = do_refresh_token(r.tprefreshtoken)
            if res[0] != 0:
                r.tptoken = res[0]
                r.tprefreshtoken = res[2]
                expirydatetime = timezone.now()+timedelta(seconds=res[1])
                r.tptokenexpirydate = expirydatetime
                r.save()
                thetoken = r.tptoken
            else:
                raise TPNoTokenError("Refresh token invalid")
	else:
	    thetoken = r.tptoken

    return thetoken

# Refresh ST token using refresh token
def do_refresh_token(refreshtoken):
    client_auth = requests.auth.HTTPBasicAuth(TP_CLIENT_KEY, TP_CLIENT_SECRET)
    post_data = {"grant_type": "refresh_token",
		 "client_secret": TP_CLIENT_SECRET,
		 "client_id":TP_CLIENT_KEY,
		 "refresh_token": refreshtoken,
		 }
    headers = {'user-agent': 'sanderroosendaal',
	       'Accept': 'application/json',
	       'Content-Type': 'application/x-www-form-urlencoded',
    }
    

    url = "https://oauth.trainingpeaks.com/oauth/token"
    
    response = requests.post(url,
                             data=post_data,
			     headers=headers)

    if response.status_code == 200 or response.status_code == 201:
        token_json = response.json()
        thetoken = token_json['access_token']
        expires_in = token_json['expires_in']
        try:
	    refresh_token = token_json['refresh_token']
        except KeyError:
	    refresh_token = refreshtoken
    else:
        return [0,0,0]

            
    return [thetoken,expires_in,refresh_token]

# Exchange access code for long-lived access token
def get_token(code):
    client_auth = requests.auth.HTTPBasicAuth(TP_CLIENT_KEY, TP_CLIENT_SECRET)
    post_data = {
	"client_id":TP_CLIENT_KEY,
        "grant_type": "authorization_code",
        "code": code,
        "redirect_uri":TP_REDIRECT_URI,
	"client_secret": TP_CLIENT_SECRET,
    }
    headers = {
	'Content-Type': 'application/x-www-form-urlencoded',
    }
    
    response = requests.post("https://oauth.trainingpeaks.com/oauth/token",
                             data=post_data)


    try:
        token_json = response.json()
        thetoken = token_json['access_token']
        expires_in = token_json['expires_in']
        refresh_token = token_json['refresh_token']
    except KeyError:
        thetoken = 0
        expires_in = 0
        refresh_token = 0
        
    return thetoken,expires_in,refresh_token

# 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": TP_CLIENT_KEY,
              "response_type": "code",
              "redirect_uri": TP_REDIRECT_URI,
              "scope": "file:write",
	      }
    url = "https://oauth.trainingpeaks.com/oauth/authorize?" +urllib.urlencode(params)

    return HttpResponseRedirect(url)



def getidfromresponse(response):
    t = json.loads(response.text)

    links = t["_links"]

    id = links["self"][0]["id"]

    return int(id)

def createtpworkoutdata(w):
    filename = w.csvfilename
    row = rowingdata(filename)
    tcxfilename = filename[:-4]+'.tcx'
    try:
        newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
    except TypeError:
        newnotes = 'from '+w.workoutsource+' via rowsandall.com'

    row.exporttotcx(tcxfilename,notes=newnotes)
    
    return tcxfilename


def tp_check(access_token):
    headers = {
        "Content-Type": "application/json",
	'Accept': 'application/json',
        'authorization': 'Bearer %s' % access_token
    }

    resp = requests.post(tpapilocation+"/v1/info/version",
                         headers=headers)

    return resp
    
def uploadactivity(access_token,filename,description='',
                   name='Rowsandall.com workout'):

    data_gz = BytesIO()
    with file(filename,'rb') as inF:
        s = inF.read()
        with gzip.GzipFile(fileobj=data_gz,mode="w") as gzf:
            gzf.write(s)


    headers = {
        'Content-Type': 'application/json',
	'Accept': 'application/json',
        'Authorization': 'Bearer %s' % access_token
    }



    data = {
        "UploadClient": "rowsandall",
        "Filename": filename,
        "SetWorkoutPublic": True,
        "Title":name,
        "Type": "rowing",
        "Comment": description,
        "Data": base64.b64encode(data_gz.getvalue()).decode("ascii")
        }

    resp = requests.post(tpapilocation+"/v1/file",
                        data = json.dumps(data),
                        headers=headers)

    print resp.status_code
    if resp.status_code != 200:
        if settings.DEBUG:
            print resp.status_code
            print resp.reason
            print ""
            print headers
            print ""
        with open("media/tperrors.log","a") as errorlog:
            errorlog.write(str(resp.status_code))
            errorlog.write("\r\n")
            timestr = strftime("%Y%m%d-%H%M%S")
            errorlog.write(timestr+"\r\n")
            errorlog.write("\r\n")
            errorlog.write(str(resp.reason))
            errorlog.write("\r\n")
            try:
                errorlog.write(str(resp.json()))
            except:
                pass
            errorlog.write("\r\n")
        return 0,resp.reason,resp.status_code,headers
    else:
        return resp.json()[0]["Id"],"ok",200,""

    return 0
    
    
def workout_tp_upload(user,w):
    message = "Uploading to TrainingPeaks"
    tpid = 0
    r = w.user

    thetoken = tp_open(r.user)

    if (checkworkoutuser(user,w)):
        tcxfile = createtpworkoutdata(w)
        if tcxfile:
            res,reason,status_code,headers = uploadactivity(
                thetoken,tcxfile,
                name=w.name
            )
            if res == 0:
                message = "Upload to TrainingPeaks failed with status code "+str(status_code)+": "+reason
                w.tpid = -1
                try:
                    os.remove(tcxfile)
                except WindowsError:
                    pass

                return message,tpid

            else: # res != 0
                w.uploadedtotp = res
                tpid = res
                w.save()
                os.remove(tcxfile)
                return 'Successfully synchronized to TrainingPeaks',tpid

        else: # no tcxfile
            message = "Upload to TrainingPeaks failed"
            w.uploadedtotp = -1
            tpid = -1
            w.save()
            return message,tpid
    else: # not allowed to upload
        message = "You are not allowed to export this workout to TP"
        tpid = 0
        return message,tpid
                
    return message,tpid
