#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <unistd.h>
#include <assert.h>
#include <pthread.h>

#include <sys/wait.h>
#include <sys/stat.h>

#include <GLES3/gl3.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>

#include "common/util.h"
#include "common/mat.h"
#include "common/glutil.h"
#include "common/framebuffer.h"
#include "common/spinner.h"

#ifndef BRAND
#define BRAND openpilot
#endif

#define STR(X) #X
#define STR2(X) STR(X)
#define PASTE(A, B) A ## B
#define PASTE2(A, B) PASTE(A, B)
#define BRAND_S STR2(BRAND)
#define BRANCH_S STR2(BRANCH)

#define PRE_CHECKOUT_FOLDER "/system/comma/openpilot"
#define GIT_CLONE_COMMAND "git clone https://github.com/commaai/openpilot.git "


extern const uint8_t str_continue[] asm("_binary_continue_" BRAND_S "_sh_start");
extern const uint8_t str_continue_end[] asm("_binary_continue_" BRAND_S "_sh_end");

static bool time_valid() {
  time_t rawtime;
  time(&rawtime);

  struct tm * sys_time = gmtime(&rawtime);
  return (1900 + sys_time->tm_year) >= 2019;
}

static int use_pre_checkout() {
  int err;

  // Cleanup
  err = system("rm -rf /tmp/openpilot");
  if(err) return 1;
  err = system("rm -rf /data/openpilot");
  if(err) return 1;

  // Copy pre checkout into tmp so we can work on it
  err = system("cp -rp " PRE_CHECKOUT_FOLDER " /tmp");
  if(err) return 1;

  err = chdir("/tmp/openpilot");
  if(err) return 1;

  // Checkout correct branch
  err = system("git remote set-branches --add origin " BRANCH_S);
  if(err) return 1;
  err = system("git fetch origin " BRANCH_S);
  if(err) return 1;
  err = system("git checkout " BRANCH_S);
  if(err) return 1;
  err = system("git reset --hard origin/" BRANCH_S);
  if(err) return 1;

  // Move to final location
  err = system("mv /tmp/openpilot /data");
  if(err) return 1;

  return 0;
}

static int fresh_clone() {
  int err;

  // Cleanup
  err = chdir("/tmp");
  if(err) return 1;
  err = system("rm -rf /tmp/openpilot");
  if(err) return 1;

  err = system(GIT_CLONE_COMMAND " -b " BRANCH_S " --depth=1 openpilot");
  if(err) return 1;

  // Cleanup old folder in /data
  err = system("rm -rf /data/openpilot");
  if(err) return 1;

  // this won't move if /data/openpilot exists
  err = system("mv /tmp/openpilot /data");
  if(err) return 1;

  return 0;
}

static int do_install() {
  int err;


  // Wait for valid time
  while (!time_valid()){
    usleep(500 * 1000);
    printf("Waiting for valid time\n");
  }

  struct stat sb;
  if (stat(PRE_CHECKOUT_FOLDER, &sb) == 0 && S_ISDIR(sb.st_mode)) {
    printf("Pre-checkout found\n");
    err = use_pre_checkout();
  } else {
    printf("Doing fresh clone\n");
    err = fresh_clone();
  }
  if(err) return 1;


  // Write continue.sh
  FILE *of = fopen("/data/data/com.termux/files/continue.sh.new", "wb");
  if(of == NULL) return 1;

  size_t num = str_continue_end - str_continue;
  size_t num_written = fwrite(str_continue, 1, num, of);
  if (num != num_written) return 1;

  fclose(of);

  err = system("chmod +x /data/data/com.termux/files/continue.sh.new");
  if(err) return 1;

  err = rename("/data/data/com.termux/files/continue.sh.new", "/data/data/com.termux/files/continue.sh");
  if(err == -1) return 1;

  // Disable SSH
  err = system("setprop persist.neos.ssh 0");
  if(err) return 1;

  return 0;
}


void * run_spinner(void * args) {
  char *loading_msg = "Installing " BRAND_S;
  char *argv[2] = {NULL, loading_msg};
  spin(2, argv);
  return NULL;
}


int main() {
  pthread_t spinner_thread;
  int err = pthread_create(&spinner_thread, NULL, run_spinner, NULL);
  assert(err == 0);

  int status = do_install();

  return status;
}
