from flask import Flask
from flask import render_template
from flask import request
from flask import jsonify
from flask import redirect
from flask import url_for
from flask import session
from werkzeug.utils import secure_filename

import numpy as np
import pandas as pd

import os
import math
import sys
import random
import string
import urllib
import urllib.parse
import requests
import json
from pathlib import Path
import base64


CLIENT_ID = 'pontificia-universidad-catolica-de-chile'
CLIENT_SECRET = '6i9TY3I3Rw1KzXgW5piLRTeQUq7EcsSCJcHizX03w'
AUTHORIZE_URL = 'https://oauth.sandbox.trainingpeaks.com/OAuth/Authorize'
TOKEN_URL = 'https://oauth.sandbox.trainingpeaks.com/oauth/token'
WEBAPP_REDIRECT_URI = 'http://trainingpeaksapp.robert.chaski.fit/login'
AUTHORIZATION_REDIRECT_URL = AUTHORIZE_URL + '?response_type=code&client_id=' + CLIENT_ID + '&redirect_uri=' + WEBAPP_REDIRECT_URI + '&scope=athlete:profile'


app = Flask(__name__)
app.secret_key = 'super_secret_key'

dirApp = os.path.dirname(os.path.abspath(__file__))
dirUploads = os.path.join(dirApp, 'uploads')


def solvePiecewiseLinearRegression(xArray, yArray, listSolverIndices, listTotalIndices):

    xINI, yINI = xArray[0], yArray[0]
    xEND, yEND = xArray[-1], yArray[-1]
    N = len(xArray)
    eMatrix = np.zeros([N, N])
    
    for m in range(1, N - 2):
        xVT1, yVT1 = xArray[m], yArray[m]
        
        for n in range(m + 1, N - 1):
            xVT2, yVT2 = xArray[n], yArray[n]
            # Compute the error:
            yArray1, xArray1 = yArray[0: m], xArray[0: m]
            yArray2, xArray2 = yArray[m + 1: n], xArray[m + 1: n]
            yArray3, xArray3 = yArray[n + 1: N - 1], xArray[n + 1: N - 1]
            yArrayRef1 = yINI + (xArray1 - xINI) * (yVT1 - yINI) / (xVT1 - xINI)
            yArrayRef2 = yVT1 + (xArray2 - xVT1) * (yVT2 - yVT1) / (xVT2 - xVT1)
            yArrayRef3 = yVT2 + (xArray3 - xVT2) * (yEND - yVT2) / (xEND - xVT2)
            eMatrix[m, n] = np.sum(np.abs(yArray1 - yArrayRef1)) + np.sum(np.abs(yArray2 - yArrayRef2)) + np.sum(np.abs(yArray3 - yArrayRef3))
    
    eMatrixMax = np.max(eMatrix)
    for m in range(1, N - 2):
        for n in range(m + 1, N - 1):
            eMatrix[m, n] -= eMatrixMax
            
    mOpt, nOpt = np.where(eMatrix == np.min(eMatrix))
    xVT1, yVT1 = xArray[mOpt[0]], yArray[mOpt[0]]
    xVT2, yVT2 = xArray[nOpt[0]], yArray[nOpt[0]]
    indexMachineThresholdVT1 = listSolverIndices[mOpt[0]]
    indexMachineThresholdVT2 = listSolverIndices[nOpt[0]]
    eMatrix = np.transpose(eMatrix)
    eDfMatixShort = pd.DataFrame(data=eMatrix, index=listSolverIndices, columns=listSolverIndices)
    eDfMatrix = pd.DataFrame(data=np.zeros([len(listTotalIndices), len(listTotalIndices)]), index=listTotalIndices, columns=listTotalIndices)
    for m in listSolverIndices:
        eDfMatrix[m].loc[listSolverIndices] = -eDfMatixShort[m].loc[listSolverIndices]

    return xINI, yINI, xVT1, yVT1, xVT2, yVT2, xEND, yEND, eDfMatrix, indexMachineThresholdVT1, indexMachineThresholdVT2


@app.route('/')
def index():
    #return "This is just a test"
    return render_template('public/index.html', authorization_redirect_url=AUTHORIZATION_REDIRECT_URL)


@app.route('/login')
def endpointLogin():

    if 'code' in request.args:

        # Get the TrainingPeaks Authorization-Code:
        authorization_code_encoded = request.args['code']
        authorization_code = urllib.parse.unquote(authorization_code_encoded)
        print('authorization_code:', authorization_code)

        # Request the TrainingPeaks Access-Token:
        data = {'grant_type': 'authorization_code', 'code': authorization_code, 'redirect_uri': WEBAPP_REDIRECT_URI}
        access_token_response = requests.post(TOKEN_URL, data=data, verify=False, allow_redirects=False, auth=(CLIENT_ID, CLIENT_SECRET))
        tokens = json.loads(access_token_response.text)
        access_token = tokens['access_token']
        print('access_token:', access_token)

        # Store the Access-Token in the WebApp Session:
        session['access_token'] = access_token

    return redirect(url_for('endpointInit'))


@app.route('/init')
def endpointInit():
    if 'access_token' in session:
        test_api_url = 'https://api.sandbox.trainingpeaks.com/v1/metrics/2021-01-01/2021-01-01'
        api_call_headers = {'Authorization': 'Bearer ' + session['access_token']}
        api_call_response = requests.get(test_api_url, headers=api_call_headers, verify=False)
        strUserAccount = 'Premium'
        if api_call_response.status_code == 403:
            strUserAccount = 'Basic'
        return strUserAccount
    else:
        return 'No token in session'


@app.route('/user')
def endpointUser():

    if 'access_token' in session:
        test_api_url = 'https://api.sandbox.trainingpeaks.com/v1/athlete/profile'
        api_call_headers = {'Authorization': 'Bearer ' + session['access_token']}
        api_call_response = requests.get(test_api_url, headers=api_call_headers, verify=False)
        return api_call_response.text
    else:
        return 'No token in session'


@app.route('/v1/athlete/profile')
def endpointAthleteProfile():

    if 'access_token' in session:
        test_api_url = 'https://api.sandbox.trainingpeaks.com/v1/athlete/profile'
        api_call_headers = {'Authorization': 'Bearer ' + session['access_token']}
        api_call_response = requests.get(test_api_url, headers=api_call_headers, verify=False)
        return api_call_response.text
    else:
        return 'No token in session'


@app.route('/v1/athlete/profile/zones')
def endpointAthleteProfileZones():

    if 'access_token' in session:
        test_api_url = 'https://api.sandbox.trainingpeaks.com/v1/athlete/profile/zones'
        api_call_headers = {'Authorization': 'Bearer ' + session['access_token']}
        api_call_response = requests.get(test_api_url, headers=api_call_headers, verify=False)
        return api_call_response.text
    else:
        return 'No token in session'


@app.route('/v1/workouts/id/<workoutId>/details')
def endpointWorkoutsIdWorkoutidDetails(workoutId):
    if 'access_token' in session:
        test_api_url = 'https://api.sandbox.trainingpeaks.com/v1/workouts/id/' + workoutId + '/details'
        api_call_headers = {'Authorization': 'Bearer ' + session['access_token']}
        api_call_response = requests.get(test_api_url, headers=api_call_headers, verify=False)
        return api_call_response.text
    else:
        return 'No token in session'


@app.route('/v1/workouts/<startDate>/<endDate>')
def endpointWorkoutsStartdateEnddate(startDate, endDate):
    if 'access_token' in session:
        test_api_url = 'https://api.sandbox.trainingpeaks.com/v1/workouts/' + startDate + '/' + endDate
        api_call_headers = {'Authorization': 'Bearer ' + session['access_token']}
        api_call_response = requests.get(test_api_url, headers=api_call_headers, verify=False)
        return api_call_response.text
    else:
        return 'No token in session'


@app.route('/v1/file', methods=['POST'])
def endpointFile():
    if 'access_token' in session:
        test_api_url = 'https://api.sandbox.trainingpeaks.com/v1/file'
        api_call_headers = {'Authorization': 'Bearer ' + session['access_token']}

        # Save the file:
        uploadFile = request.files['uploadFile']
        fileName = secure_filename(uploadFile.filename)
        filePath = os.path.join(dirUploads, fileName)
        uploadFile.save(filePath)

        workoutDay = fileName.split('_')[1].split('T')[0]
        startTimeAux = fileName.split('_')[1].split('T')[1].split('-')[0]
        startTime = startTimeAux[0:2] + ':' + startTimeAux[2:4] + ':' + startTimeAux[4:6]

        startTime = workoutDay + 'T' + startTime
        workoutDay = workoutDay + 'T00:00:00'

        '''
        # Code to check some prints in a file:
        logFile = os.path.join(dirUploads, 'log.txt')
        original_stdout = sys.stdout # Save a reference to the original standard output
        with open(logFile, 'w') as file:
            sys.stdout = file # Change the standard output to the file we created.
            print('workoutDay:', workoutDay, ', startTime:', startTime)
            sys.stdout = original_stdout # Reset the standard output to its original value
        '''

        # Save a copy of the file
        df = pd.read_csv(filePath, header=2)
        dfCopy = pd.DataFrame(columns=['Minutes','Torq (N-m)','Km/h','Watts','Km','Cadence', 'Hrate'])
        dfCopy['Minutes'] = df['timeSeconds'] / 60
        dfCopy['Hrate'] = df['signalFrequencyBpm']
        fileNameCopy = fileName.split('.')[0] + '_copy.csv'
        filePathCopy = os.path.join(dirUploads, fileNameCopy)
        dfCopy.to_csv(filePathCopy, index=False)

        # Get the file and make the data:
        fileObject = open(filePathCopy, 'r')
        strFileContent = fileObject.read()
        fileObject.close()
        data = {
          'UploadClient': 'pontificia-universidad-catolica-de-chile',
          'Filename': fileNameCopy,
          'Data': base64.b64encode(strFileContent.encode('ascii')),
          'Type': 'other',
          'WorkoutDay': workoutDay,
          'StartTime': startTime
        }

        api_call_response = requests.post(test_api_url, headers=api_call_headers, data=data, verify=False)
        return api_call_response.text
    else:
        return 'No token in session'


@app.route('/data', methods=['GET', 'POST'])
def functionData():

    if request.method == 'GET':
        dictAfterGet = {'response': 'Hola Rodrigo'}
        return jsonify(dictAfterGet)

    elif request.method == 'POST':
        # Code to check some prints in a file:
        #logFile = os.path.join(dirUploads, 'log.txt')
        #original_stdout = sys.stdout # Save a reference to the original standard output
        #with open(logFile, 'w') as file:
        #    sys.stdout = file # Change the standard output to the file we created.
        #    print(request.form)
        #    sys.stdout = original_stdout # Reset the standard output to its original value

	# Define the bpm zones:
        maxBpmZone0, maxBpmZone1, maxBpmZone2 = 30, 45, 55
        dictResponse = request.form
        listKeys = list(dictResponse.keys())
        for key in listKeys:
            if key == 'maxBpmZone0':
                maxBpmZone0 = int(dictResponse[key])
            if key == 'maxBpmZone1':
                maxBpmZone1 = int(dictResponse[key])
            if key == 'maxBpmZone2':
                maxBpmZone2 = int(dictResponse[key])


        # Save the file:
        uploadFile = request.files['uploadFile']
        fileName = secure_filename(uploadFile.filename)
        filePath = os.path.join(dirUploads, fileName)
        uploadFile.save(filePath)
        # Read the saved .csv file and get the data frame:
        #df = pd.read_csv(filePath)
        df = pd.read_csv(filePath, header=2)
        listColumns = list(df.columns)
        # Create a json response:
        dictAfterPost = {}
        dictAfterPost['fileName'] = fileName
        for column in listColumns:
            dictAfterPost[column] = {'mean': str(int(df[column].mean())),
                                     'std': str(int(df[column].std())),
                                     'min': str(int(df[column].min())),
                                     'max': str(int(df[column].max()))}
        # For chaski basic:
        columnTime = 'timeSeconds'
        columnBpm = 'signalFrequencyBpm'
        if set([columnTime, columnBpm]).issubset(set(listColumns)):
            dictAfterPost['listTimeZones'] = []
            dictAfterPost['listPercentageZones'] = []
            timeZone1 = 0.1 * len(df[(df[columnBpm] < maxBpmZone0)])
            timeZone2 = 0.1 * len(df[(maxBpmZone0 <= df[columnBpm]) & (df[columnBpm] < maxBpmZone1)])
            timeZone3 = 0.1 * len(df[(maxBpmZone1 <= df[columnBpm]) & (df[columnBpm] < maxBpmZone2)])
            timeZone4 = 0.1 * len(df[(maxBpmZone2 <= df[columnBpm])])
            timeDuration = 0.1 * len(df[columnTime])
            percentageZone1 = 100 * timeZone1 / timeDuration
            percentageZone2 = 100 * timeZone2 / timeDuration
            percentageZone3 = 100 * timeZone3 / timeDuration
            percentageZone4 = 100 * timeZone4 / timeDuration
            dictAfterPost['listTimeZones'].append(str(int(timeZone1)))
            dictAfterPost['listTimeZones'].append(str(int(timeZone2)))
            dictAfterPost['listTimeZones'].append(str(int(timeZone3)))
            dictAfterPost['listTimeZones'].append(str(int(timeZone4)))
            dictAfterPost['listPercentageZones'].append(str(int(percentageZone1)))
            dictAfterPost['listPercentageZones'].append(str(int(percentageZone2)))
            dictAfterPost['listPercentageZones'].append(str(int(percentageZone3)))
            dictAfterPost['listPercentageZones'].append(str(int(percentageZone4)))
        # For incremental test:
        listIndexes = list(range(0, len(df), 100))
        dfUsed = df.iloc[listIndexes]
        xVar, yVar = columnTime, columnBpm
        xArray, yArray = dfUsed[xVar].to_numpy(), dfUsed[yVar].to_numpy()
        listSolverIndices, listTotalIndices = list(dfUsed.index), list(dfUsed.index)
        _, _, xVT1, yVT1, xVT2, yVT2, _, _, _, _, _ = solvePiecewiseLinearRegression(xArray, yArray, listSolverIndices, listTotalIndices)
        dictAfterPost['incrementalTest'] = {'timeVT1': "{:.1f}".format(xVT1),
                                            'bpmVT1': str(int(yVT1)),
                                            'timeVT2': "{:.2f}".format(xVT2),
                                            'bpmVT2': str(int(yVT2))}
        # For returning some data points:
        numRows, numPoints = len(df.index), 100
        modulusBase = int(math.ceil(numRows / numPoints))
        listIlocs = range(0, numRows, modulusBase)
        dictAfterPost['listDataPoints'] = [{'timeMinutes': str(df[columnTime].iloc[i]), 'valueRespiratoryRate': str(df[columnBpm].iloc[i])} for i in listIlocs]
        return jsonify(dictAfterPost)
