import os
import random
import re
import string
import subprocess
import time
import json

from django.conf import settings
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

from program_app.helpers import generate_filename, python_output_with_input, c_output_with_input, cpp_output_with_input, \
    java_output_with_input, javascript_output_with_input

@csrf_exempt
def execute_programs(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        try:
            language = data.get("language")
            code = data.get('code')
            if language == 'python':
                output = python_output(code)
            elif language == 'c':
                output = c_output(code)
            elif language == 'c++':
                output = cpp_output(code)
            elif language == 'java':
                output = java_output(code)
            else:
                output = 'Program language not supported.'
            response = {'status': 'Success', 'status_code': 200, 'message': output}
        except Exception as ex:
            response = {'status': 'Failure', 'status_code': 401,
                        'message': 'Data issue, Please try again.'}
    else:
        response = {'status': 'Failure', 'status_code': 405, 'message': 'Method Not Allowed.'}
    return HttpResponse(json.dumps(response), content_type='application/javascript')


def python_output(code):
    file_name = generate_filename() + '.py'
    try:
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name, 'w')
        f.write(code)
        f.close()
        proc = subprocess.Popen(['python3', file_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
        n = 0
        while 1:
            if proc.poll() is not None:
                output = proc.stdout.read()
                break
            time.sleep(1)
            n = n + 1
            if n >= 5:
                proc.kill()
        try:
            os.remove(file_name)
        except:
            pass
    except:
        output = ''
    return str(output)
    # return str(output, encoding='UTF-8')


def java_output(code):
    output = ''
    file_name = generate_filename() + '.java'
    try:
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name, 'w')
        f.write(code)
        f.close()
        proc = subprocess.Popen(['java', file_name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False)
        n = 0
        while 1:
            if proc.poll() is not None:
                output = proc.stdout.read()
                break
            time.sleep(1)
            n = n + 1
            if n >= 5:
                proc.kill()
    except:
        output = ''
    finally:
        try:
            os.remove(file_name)
        except Exception as ex:
            pass

    return str(output)


def c_output(code):
    try:
        file_name = generate_filename()
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name + ".c", 'w')
        f.write(code)
        f.close()
        compl = subprocess.Popen(['gcc', '-o', file_name, file_name + '.c'], stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
        compl_output = compl.communicate()[0]
        if b'warning' in compl_output.lower():
            output = compl_output + b'\n'
            try:
                out = subprocess.Popen(file_name, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                n = 0
                while 1:
                    if out.poll() is not None:
                        output += out.stdout.read()
                        break
                    time.sleep(1)
                    n += 1
                    if n >= 5:
                        out.kill()
            except:
                output = compl_output

        else:
            if compl_output:
                output = compl_output
            else:
                out = subprocess.Popen(file_name, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                n1 = 0
                while 1:
                    if out.poll() is not None:
                        output = out.stdout.read()
                        break
                    time.sleep(1)
                    n1 += 1
                    if n1 >= 5:
                        out.kill()
        try:
            os.remove(file_name + '.c')
            os.remove(file_name)
            os.remove(file_name + '.exe')
        except:
            pass
    except:
        output = ''
    return str(output)


def cpp_output(code):
    try:
        file_name = generate_filename()
        file_name = settings.FILE_EXECUTING_PATH + file_name
        f = open(file_name + ".cpp", 'w')
        f.write(code)
        f.close()
        compl = subprocess.Popen(['g++', '-o', file_name, file_name + '.cpp'], stdout=subprocess.PIPE,
                                 stderr=subprocess.STDOUT)
        compl_output = compl.communicate()[0]
        if b'warning' in compl_output.lower():
            output = compl_output + b'\n'
            try:
                out = subprocess.Popen(file_name, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                n = 0
                while 1:
                    if out.poll() is not None:
                        output += out.stdout.read()
                        break
                    time.sleep(1)
                    n += 1
                    if n >= 5:
                        out.kill()
            except:
                output = compl_output
        else:
            if compl_output:
                output = compl_output
            else:
                out = subprocess.Popen(file_name, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
                n1 = 0
                while 1:
                    if out.poll() is not None:
                        output = out.stdout.read()
                        break
                    time.sleep(1)
                    n1 += 1
                    if n1 >= 5:
                        out.kill()
        try:
            os.remove(file_name + '.cpp')
            os.remove(file_name)
            os.remove(file_name + '.exe')
        except:
            pass
    except:
        output = ''
    return str(output)



@csrf_exempt
def evaluate_code(request):
    if request.method == 'POST':
        data = json.loads(request.body)
        try:
            language = data.get("language")
            code = data.get('code')
            std_in1 = data.get('std_in1')
            std_out1 = data.get('std_out1')
            std_in2 = data.get('std_in2')
            std_out2 = data.get('std_out2')
            std_in3 = data.get('std_in3')
            std_out3 = data.get('std_out3')
            output2 = [0, '']
            output3 = [0, '']
            message = ''
            if language == 'python':
                output = python_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = python_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = python_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']

            elif language == 'c':
                output = c_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = c_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = c_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
            elif language == 'c++':
                output = cpp_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = cpp_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = cpp_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']

            elif language == 'java':
                output = java_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = java_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = java_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
            elif language == 'javascript':
                output = javascript_output_with_input(code, std_in1, std_out1)
                if std_in2 and std_out2:
                    output2 = javascript_output_with_input(code, std_in2, std_out2)
                else:
                    output2 = [0, '']
                if std_in3 and std_out3:
                    output3 = javascript_output_with_input(code, std_in3, std_out3)
                else:
                    output3 = [0, '']
            else:
                output = [0, '']
                message = 'Program language not supported.'
            final_score_list = []
            final_output = "Test Case 1: \n Input: " + str(std_in1).replace('\n', '&') + "\n Output: " + str(output[1]) + "\n Expected Output: " + str(std_out1) + "\n"
            if output[0]:
                final_output += "Test Case Status: Passed\n\n"
            else:
                final_output += "Test Case Status: Failed\n\n"
            final_score_list.append(output[0])
            if std_in2 and std_out2:
                final_output += "Test Case 2: \n Input: " + str(std_in2).replace('\n', '&') + "\n Output: " + str(output2[1]) + "\n Expected Output: " + str(std_out2) + "\n"
                if output2[0]:
                    final_output += "Test Case Status: Passed\n\n"
                else:
                    final_output += "Test Case Status: Failed\n\n"
                final_score_list.append(output2[0])
            if std_in3 and std_out3:
                final_output += "Test Case 3: \n Input: " + str(std_in3).replace('\n', ' & ') + "\n Output: " + str(output3[1]) + "\n Expected Output: " + str(std_out3) + "\n"
                if output3[0]:
                    final_output += "Test Case Status: Passed\n\n"
                else:
                    final_output += "Test Case Status: Failed\n\n"
                final_score_list.append(output3[0])

            if final_score_list:
                if all(final_score_list):
                    try:
                        avg_score = sum(final_score_list)/len(final_score_list)
                    except:
                        avg_score = 0
                else:
                    avg_score = 0
            else:
                avg_score = 0



            final_output = re.sub(r'File ".+.py",', 'File "file.py",', final_output)

            if message:
                response = {'status': 'Success', 'status_code': 200, 'message': message}
            else:
                response = {'status': 'Success', 'status_code': 200, 'score': avg_score, 'message': str(final_output)}
        except Exception as ex:
            response = {'status': 'Failure', 'status_code': 401,
                        'message': 'Data issue, Please try again.'}

    else:
        response = {'status': 'Failure', 'status_code': 405, 'message': 'Method Not Allowed.'}


    # data = json.loads(request.body)
    # code = data.get('code')
    # std_in = data.get('std_in')
    # std_out = data.get('std_out')
    # r = python_output_with_input(code, std_in, std_out)
    # res = {'message': r}
    return HttpResponse(json.dumps(response), content_type='application/javascript')


