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, evl_nodejs_api, nodejs_code_eval

@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, '']
            elif language == 'node.js' or language == 'nodejs':
                input_output_list = [(std_in1, std_out1)]
                if std_in2 and std_out2:
                    input_output_list.append((std_in2, std_out2))
                if std_in3 and std_out3:
                    input_output_list.append((std_in3, std_out3))
                output = nodejs_code_eval(code, input_output_list)
            else:
                output = [0, '']
                message = 'Program language not supported.'
            if language == 'node.js' or language == 'nodejs':
                avg_score = output[0]
                final_output = output[1]
            else:
                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')


def eval_nodejs(request):
    code = """
const express = require('express');
const app = express();

const books = [
  {
    id: 1,
    title: "The Hitchhiker's Guide to the Galaxy",
    author: "Douglas Adams"
  },
  {
    id: 2,
    title: "To Kill a Mockingbird",
    author: "Harper Lee"
  },
  {
    id: 3,
    title: "1984",
    author: "George Orwell"
  }
];

app.get('/books', (req, res) => {
   
  res.json(books);
});

module.exports = app;"""
    # response = evl_nodejs_api()
    input = {
          "method": "GET",
          "url": "/books"
        }
    output = {
  "http-code": 200,
  "output-json": [
          {
            "id": 1,
            "title": "The Hitchhiker's Guide to the Galaxy",
            "author": "Douglas Adams"
          },
          {
            "id": 2,
            "title": "To Kill a Mockingbird",
            "author": "Harper Lee"
          },
          {
            "id": 3,
            "title": "1984",
            "author": "George Orwell"
          }
  ]
    }

    response = str(nodejs_code_eval(code, [(input, output), (input, output), (input, output)]))

    return HttpResponse(json.dumps(response), content_type='application/javascript')



