const fs = require('fs');
const childProcess = require("child_process");
const process = require("process");
const express = require('express');
let app = express();
app.use(express.static(__dirname + '/public')); 
app.use(express.json()); 
app.use(express.urlencoded({ extended: false }));
let req_id=0;


function check_request(req) {
  if(!req.input) req.input="";
  if(req.input.length>1000000) return false;
  if(!req.time_limit) req.time_limit=0.1;
  if(req.time_limit<=0 || req.time_limit>1) return false;
  if(!req.memory_limit) req.memory_limit=256;
  if(req.memory_limit<=0 || req.memory_limit>1024) return false;
  if(!req.output_limit) req.output_limit=10000;
  if(req.output_limit<=0 || req.output_limit>1000000) return false;

  if(!req.code) return false;
  if(req.code.length>65535) return false;
  if(!req.prefix) req.prefix="";
  if(req.prefix.length>32767) return false;
  if(!req.suffix) req.suffix="";
  if(req.suffix.length>32767) return false;
  if(!req.language) return false;
  if(!(req.language=="cpp" || req.language=="py")) return false;
  return true;
}

function execute1(command) {
  console.log(command);
  return new Promise(function(resolve, reject) {
    childProcess.exec(command, function(error, standardOutput, standardError) {
      resolve(standardOutput);
    });
  });
}
function make_program(req) {
  try {
    fs.mkdirSync(req.path);
  } catch(error) {
  }
  fs.writeFileSync(req.path+'\\program.'+req.language, req.prefix+req.code+req.suffix);
}
async function do_compile(req,res) {
  if(req.language=="cpp")  { 
    try {
      let out=await execute1(__dirname+`\\run.exe -Xacp -t 1s -m 1024M -d ${req.path} -o ${req.path}\\compile1 -e ${req.path}\\compile2 g++ -o ${req.path}\\program ${req.path}\\program.cpp`);
      console.log(out);
      if(out.search("  exit code:     0")==-1)
      {
        let out1=fs.readFileSync(req.path+"\\compile1",{ encoding: 'utf8' });
        let out2=fs.readFileSync(req.path+"\\compile2",{ encoding: 'utf8' });
        res.send({error:"compile_error",compile_output:out1+out2});
        return false;
      }
    } catch (error) {
      res.send({error:"compile_error",compile_output:error});
      return false;
    }
  }
  return true;
}
async function do_test(req, ro, path,cmd) {
  fs.writeFileSync(path+'\\input.txt', req.input);
  let out="";
  let error="exec_error";
  try {
    out=await execute1(__dirname+`\\run.exe -t ${Math.trunc(req.time_limit*1000)}ms -m ${Math.trunc(req.memory_limit)}M -d ${path} -i ${path}\\input.txt -o ${path}\\output.txt -e ${path}\\error.txt ${cmd}`);
  } catch (error) {
  } 
  console.log(out);
  let out1=fs.readFileSync(path+"\\output.txt",{ encoding: 'utf8' });
  ro.output=out1;
  let m=out.match(/time consumed: +([^ ]+)/);
  if(m) {
    ro.time=parseFloat(m[1]);
  }
  m=out.match(/peak memory: +([^ ]+)/);
  if(m) {
    ro.memory=parseInt(m[1])/(1024*1024);
  }
  if(out.search("Program successfully terminated")!=-1) {
    if(out.search("  exit code:     0")!=-1) {
      error="";
      if(req.answer && req.answer.trim()!=out1.trim()) {
        error="wrong_answer";
      }
    }
  }
  else if(out.search("Time limit exceeded")!=-1) {
    error="time_limit";
  }
  else if(out.search("Memory limit exceeded")!=-1) {
    error="memory_limit";
  }
  ro.error=error;
}
async function do_run(req,res) {
  let path=req.path;
  let cmd=path+"\\program.exe";
  
  if(req.language=="py") {
    cmd="python "+path+"\\program.py"
  }
  let ro={error:""};
  if(req.tests) { 
    ro.tests=[];
    for(let t of req.tests) {
       ro.tests.push({id:t.id});
       t.time_limit=req.time_limit;
       t.memory_limit=req.memory_limit;
       t.output_limit=req.output_limit;
       await do_test(t,ro.tests[ro.tests.length-1],req.path,cmd);
    }
  }
  else
    await do_test(req,ro,req.path,cmd);
  res.send(ro);
}


// обработка запросов
app.post('/run', async function(req, res) {
  req.body.id= ++req_id;
  req.body.path=__dirname+'\\check'+req.body.id;
  if(check_request(req.body)) { 
    make_program(req.body);
    let r=await do_compile(req.body,res);
    if(r) { 
      await do_run(req.body,res);
    }
  }
  else
    res.send({error:"request_error",output:""});
});
app.listen(3000, function () {
  
  console.log('Listening on port 3000!');

});

