#include "system.h"

#ifdef SYSTEM_MAIN
#include <conio.h>
#endif

namespace sys {

HANDLE other_input = NULL;
HANDLE other_output = NULL;
HANDLE writeShell0 = NULL;
bool done = true;

bool file_exists(const string & ff) {
   File file(ff);
   return file.is_file();
}

LPVOID ReadSystem(LPVOID arg)
{
   char ch = 'A';
   DWORD rd = 0;
   HANDLE input = (HANDLE)arg;

   while(ch != EOF) {
      if(ReadFile(input, &ch, 1, &rd, NULL)) {
         if(rd == 1 && ch != EOF) {
            cout << ch << flush;
            //basic::print_text(ch+string());
         }
      }
      else {
         break;
      }
      rd = 0;
   }

   return NULL;
}

LPVOID WriteSystem(LPVOID arg)
{
   DWORD wr = 0;
   HANDLE output = (HANDLE)arg;
   char ch = 'A';
   return 0;

   while(ch != EOF) {
      ch = cin.get();
      if(ch != EOF) {
         if(WriteFile(output, &ch, 1, &wr, NULL)) {

         }
         else {
            break;
         }
      }
      wr = 0;
   }

   char eof = EOF;
   DWORD n = 0;

   WriteFile(other_output, &eof, 1, &n, NULL);

   return NULL;
}

HANDLE hProcess = NULL;
HANDLE hEndOfInput = NULL;
char program_name[MAX_PATH];


bool in_path(const string & file_name, string & path) {
   bool ret = false;
   string current_path;
   path = "";
   const char * real_path0 = getenv("PATH");
   if(real_path0) {
      string temp = real_path0;
      char current_dir[MAX_PATH];
      memset(current_dir, 0, MAX_PATH);
      _getcwd(current_dir, MAX_PATH-1);
      temp = string(current_dir) + ";" + temp; 
	   char* real_path = Strdup(temp.c_str());
	   string_tokenizer st(real_path, ";");
      while(st.has_next()) {
          current_path = st.get_next();
          if(!ends_with(current_path, "\\")) {
             current_path += "\\";
          }
          if(file_exists(current_path + file_name)) {
            path = current_path;
            ret = true;
            break;
          }
      }
	   delete [] real_path;
   }
   return ret;
}

extern int write_char(char ch);
extern bool in_system();

bool in_system()
{
   return writeShell0 != NULL;
}

int write_char(char ch)
{
   int rc = 0;
   if(writeShell0 != NULL) {
      DWORD wr = 0;
      if(ch == 13) ch = 10;
      rc = WriteFile(writeShell0, &ch, 1, &wr, NULL);
   }
   return rc;
}

int System(const string & cmd)
{
   STARTUPINFO si;
   SECURITY_ATTRIBUTES sa;
   PROCESS_INFORMATION pi;
   HANDLE newstdin, newstdout, write_stdin, read_stdout;
   HANDLE hProcess;
   DWORD read_id=0, write_id=0;
   DWORD rd = 0;
   int rc = 0;

   memset((void*)&si, 0, sizeof(si));
   memset((void*)&sa, 0, sizeof(sa));
   memset((void*)&pi, 0, sizeof(pi));

   sa.lpSecurityDescriptor = NULL;
   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
   sa.bInheritHandle = true;
   
   CreatePipe(&newstdin,&write_stdin,&sa,1024*10);
   CreatePipe(&read_stdout,&newstdout,&sa,1024*10);

   GetStartupInfo(&si);
   si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_HIDE;
   si.hStdOutput = newstdout;
   si.hStdError = newstdout;
   si.hStdInput = newstdin;

   string program = trim(cmd), args;
   string::size_type ptr = program.find(" ");
   
   if(ptr != string::npos) {
      args = trim(program.substr(ptr+1));
      program = trim(program.substr(0,ptr));
   }

   string path;

   if(!ends_with(program, ".exe") && !ends_with(program, ".bat")) {
      program += ".exe";
   }

   in_path(program, path);

   if(!ends_with(path, "\\") && path.size()) {
      path += "\\";
   }

   path = trim(path);
   program = trim(program);

   memset(program_name, 0, MAX_PATH);

   strcpy(program_name, program.c_str());

   if(args.size()) {
      program = path + program + " " + args;
   }
   else {
      program = path + program;
   }

   other_input = newstdin;
   other_output = newstdout;

   writeShell0 = write_stdin;

   HANDLE hReadThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadSystem, read_stdout, 0, &read_id);
   HANDLE hWriteThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WriteSystem, write_stdin, 0, &write_id);

   rc = (int)CreateProcess(NULL,
                 (LPSTR)program.c_str(),
                 NULL,
                 NULL,
                 TRUE,
                 0,//CREATE_NEW_CONSOLE,
                 NULL,
                 NULL,
                 &si,
                 &pi);
   
   if(rc == 0) {
      return 1;
   }

   hProcess = pi.hProcess;

   DWORD exit_code;

   WaitForSingleObject(pi.hProcess, INFINITE);
   WaitForSingleObject(pi.hThread, INFINITE);

   GetExitCodeProcess(pi.hProcess, &exit_code);

   if(exit_code == STILL_ACTIVE) {
      return 1;
   }

   CloseHandle(pi.hProcess);
   CloseHandle(pi.hThread);
   
   if(exit_code == 0)
      rc = 0;
   else
      rc = 1;

   char ch = EOF;
   DWORD wr;
   
   wr = 0;
   WriteFile(newstdout, &ch, 1, &wr, NULL);

   WaitForSingleObject(hReadThread, INFINITE);
   //WaitForSingleObject(hWriteThread, INFINITE);

   CloseHandle(hWriteThread);
   CloseHandle(hReadThread);

   CloseHandle(write_stdin);
   CloseHandle(read_stdout);
   CloseHandle(newstdin);
   CloseHandle(newstdout);

   writeShell0 = NULL;

   return rc;
}

extern void* WaitFor(void* arg);
extern Task* popen(const string & cmd);
Task* popen(const string & cmd)
{
   STARTUPINFO si;
   SECURITY_ATTRIBUTES sa;
   PROCESS_INFORMATION pi;
   HANDLE newstdin, newstdout, write_stdin, read_stdout;
   HANDLE hProcess;
   DWORD read_id=0, write_id=0;
   DWORD rd = 0;
   int rc = 0;

   memset((void*)&si, 0, sizeof(si));
   memset((void*)&sa, 0, sizeof(sa));
   memset((void*)&pi, 0, sizeof(pi));

   sa.lpSecurityDescriptor = NULL;
   sa.nLength = sizeof(SECURITY_ATTRIBUTES);
   sa.bInheritHandle = true;
   
   CreatePipe(&newstdin,&write_stdin,&sa,1024*10);
   CreatePipe(&read_stdout,&newstdout,&sa,1024*10);

   GetStartupInfo(&si);
   si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
   si.wShowWindow = SW_HIDE;
   si.hStdOutput = newstdout;
   si.hStdError = newstdout;
   si.hStdInput = newstdin;

   string program = trim(cmd), args;
   string::size_type ptr = program.find(" ");
   
   if(ptr != string::npos) {
      args = trim(program.substr(ptr+1));
      program = trim(program.substr(0,ptr));
   }

   string path;

   if(!ends_with(program, ".exe") && !ends_with(program, ".bat")) {
      program += ".exe";
   }

   in_path(program, path);

   if(!ends_with(path, "\\") && path.size()) {
      path += "\\";
   }

   path = trim(path);
   program = trim(program);

   memset(program_name, 0, MAX_PATH);

   strcpy(program_name, program.c_str());

   if(args.size()) {
      program = path + program + " " + args;
   }
   else {
      program = path + program;
   }

   other_input = newstdin;
   other_output = newstdout;

   writeShell0 = write_stdin;

   rc = (int)CreateProcess(NULL,
                 (LPSTR)program.c_str(),
                 NULL,
                 NULL,
                 TRUE,
                 0,
                 NULL,
                 NULL,
                 &si,
                 &pi);
   
   if(rc == 0) {
      return NULL;
   }

   hProcess = pi.hProcess;

   hEndOfInput = newstdout;

   DWORD wait_id = 0;
   PROCESS_INFORMATION* pi_ptr = new PROCESS_INFORMATION();
   *pi_ptr = pi;

   HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WaitFor, (LPVOID)pi_ptr, 0, &wait_id);

   Task* task = new Task(read_stdout, write_stdin, newstdin, newstdout);

   done = false;

   return task;
}

int pgetc(HANDLE input) 
{
   char ch;
   DWORD rd = 0;
   if(ReadFile(input, &ch, 1, &rd, NULL) != 1) {
      ch = -1;
   }
   return ch;
}

int pputc(HANDLE hWrite, int ch)
{
   int rc = 0;
   if(hWrite != NULL) {
      DWORD wr = 0;
      char ch0 = ch;
      if(ch == 13)
         ch0 = 10;
      rc = WriteFile(hWrite, &ch0, 1, &wr, NULL);
      if(rc != 1)
         rc = -1;
   }
   else {
      rc = -1;
   }
   return rc;
}

void pclose(Task* pt) 
{
    delete pt;
}

}

extern bool program_done()
{
   return done;
}

void*
WaitFor(void* arg)
{
   DWORD exit_code;
   PROCESS_INFORMATION* pi = (PROCESS_INFORMATION*)arg;

   done = false;

   WaitForSingleObject(pi->hProcess, INFINITE);
   WaitForSingleObject(pi->hThread, INFINITE);

   GetExitCodeProcess(pi->hProcess, &exit_code);

   DWORD wr = 0;
   char ch = -1;
   WriteFile(hEndOfInput, &ch, 1, &wr, NULL);

   CloseHandle(pi->hProcess);
   CloseHandle(pi->hThread);

   delete pi;

   writeShell0 = NULL;
   hEndOfInput = NULL;

   done = true;

   return NULL;
}
}
