import pandas as pd
import os
import logging
import math
import re
from datetime import datetime
import json
from django.http import HttpResponse
from django.views.decorators.cache import never_cache
from django.shortcuts import render_to_response, redirect

from core.pandajob.models import Jobsarchived4, Jobsarchived

from core.oauth.utils import login_customrequired
from core.views import initRequest
from core.libs.exlib import parse_datetime
from core.filebrowser.views import get_job_log_file_path
from core.settings.config import PRMON_LOGS_DIRECTIO_LOCATION

_logger = logging.getLogger('bigpandamon')
filebrowserURL = "http://bigpanda.cern.ch/filebrowser/" #This is deployment specific because memory monitoring is intended to work in ATLAS


@login_customrequired
def getPlots(request):
    valid, response = initRequest(request)
    if not valid:
        return response

    if not 'pandaid' in request.session['requestParams']:
        data = {
            'viewParams': request.session['viewParams'],
            'requestParams': request.session['requestParams'],
            "errormessage": "No pandaid provided!",
        }
        return render_to_response('errorPage.html', data, content_type='text/html')
    else:
        pandaid = request.session['requestParams']['pandaid']
        try:
            pandaid = int(pandaid)
        except:
            data = {
                'viewParams': request.session['viewParams'],
                'requestParams': request.session['requestParams'],
                "errormessage": "Illegal value {} for pandaid provided! Check the URL please!".format(pandaid),
            }
            return render_to_response('errorPage.html', data, content_type='text/html')
    # redirect to new PrMon plots page
    return redirect('prMonPlots', pandaid=pandaid)


@login_customrequired
def prMonPlots(request, pandaid=-1):
    valid, response = initRequest(request)
    if not valid:
        return response

    msg = ''
    try:
        pandaid = int(pandaid)
    except:
        pandaid = -1
        msg = 'No pandaid provided!'

    processor_type = 'cpu'
    if pandaid > 0:
        job = Jobsarchived4.objects.filter(pandaid=pandaid).values('pandaid', 'cmtconfig')
        if len(job) == 0:
            job = Jobsarchived.objects.filter(pandaid=pandaid).values('pandaid', 'cmtconfig')

        if len(job) > 0:
            job = job[0]
            if 'cmtconfig' in job and 'gpu' in job['cmtconfig']:
                processor_type = 'gpu'

    plots_list = [
        'np_nt',
        'cpu_rate',
        'memory',
        'memory_rate',
        'io',
        'io_rate',
    ]

    if processor_type == 'gpu':
        plots_list.extend(['ng', 'gpu_memory', 'gpu_pct'])

    data = {
        'request': request,
        'viewParams': request.session['viewParams'],
        'requestParams': request.session['requestParams'],
        'pandaid': pandaid,
        'processor_type': processor_type,
        'built': datetime.now().strftime("%H:%M:%S"),
        'plotsList': plots_list,
        "error": msg,
    }
    return render_to_response('jobMemoryMonitor.html', data, content_type='text/html')


def get_seconds(line):
    """gets the seconds from timestamp in athena output (location of timestamp is hard-coded)"""
    matches = re.findall(r'\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2},\d{3}', line)
    t = parse_datetime(matches[0])
    seconds = (t-datetime(1970, 1, 1)).total_seconds()  # get seconds
    # seconds = seconds + float("0."+line.split()[2].split(":")[2].split(',')[1])  # add fraction of the seconds
    return seconds


def get_payload_steps(payload_stdout_path):
    """
    Read payload.stdout log to find name and start time in min of each step
    :param pandaid: int
    :return: payload_steps: list
    """
    payload_steps = []

    init_time = True
    with open(payload_stdout_path) as f:
        for line in f:
            if "INFO" in line and init_time:
                init_time = False
                firsttime = get_seconds(line)

            if "Starting execution" in line:
                try:
                    payload_steps.append([math.floor((get_seconds(line) - firsttime)/60), str(line.split()[7])])
                except Exception as e:
                    _logger.debug('Failed to get timestamp from log line: {}\n{}'.format(line, e))
            if "INFO Validating output files" in line:
                try:
                    payload_steps.append([math.floor((get_seconds(line) - firsttime) / 60), "VALIDATION"])
                except Exception as e:
                    _logger.debug('Failed to get timestamp from log line: {}\n{}'.format(line, e))

    # remove following steps if they can overlap
    distances = [i_step[0] - payload_steps[i-1][0] for i, i_step in enumerate(payload_steps) if i > 0]
    if len([i for i, d in enumerate(distances) if d < 1]) > 0:
        for j in sorted([i for i, d in enumerate(distances) if d < 1], reverse=True):
            try:
                del payload_steps[j+1]
                payload_steps[j][1] += '[+]'
            except:
                pass
    return payload_steps


@never_cache
def getPrMonPlotsData(request, pandaid=-1):
    """
    Getting and prepare data for plots based on PRMON output data stored in job logs
    :param request:
    :param pandaid:
    :return: list of dicts containing data for plots
    """
    valid, response = initRequest(request)
    if not valid:
        return response
    try:
        pandaid = int(pandaid)
    except:
        pandaid = -1

    # Definition of prmon labels/units for beautification from:
    # https://github.com/HSF/prmon/blob/master/package/scripts/prmon_plot.py
    axisunits = {'vmem': 'kb',
                 'pss': 'kb',
                 'rss': 'kb',
                 'swap': 'kb',
                 'utime': 'sec', 'stime': 'sec', 'wtime': 'sec',
                 'rchar': 'b', 'wchar': 'b',
                 'read_bytes': 'b', 'write_bytes': 'b',
                 'rx_packets': '1', 'tx_packets': '1',
                 'rx_bytes': 'b', 'tx_bytes': 'b',
                 'nprocs': '1', 'nthreads': '1',
                 'gpufbmem': 'kb',
                 'gpumempct': '%',
                 'gpusmpct': '%',
                 'ngpus': '1'
                 }

    axisnames = {'vmem': 'Memory',
                 'pss': 'Memory',
                 'rss': 'Memory',
                 'swap': 'Memory',
                 'utime': 'CPU-time',
                 'stime': 'CPU-time',
                 'wtime': 'Wall-time',
                 'rchar': 'I/O',
                 'wchar': 'I/O',
                 'read_bytes': 'I/O',
                 'write_bytes': 'I/O',
                 'rx_packets': 'Network',
                 'tx_packets': 'Network',
                 'rx_bytes': 'Network',
                 'tx_bytes': 'Network',
                 'nprocs': 'Count',
                 'nthreads': 'Count',
                 'gpufbmem': 'Memory',
                 'gpumempct': 'Memory',
                 'gpusmpct': 'Streaming Multiprocessors',
                 'ngpus': 'Count'