var debug_taps = 0;
var debug_logs = [];
var track;
var frame_width;
var frame_height;
var scanguide_anime;
var busy = false;
const max_inflight = 5;
var should_check_auto_torch = true;
var done_checking_auto_torch = false;
var camera_sensitivity = 1.0;
var camera_capabilities = null;

var Module = {
  onRuntimeInitialized: start,
};

function get_query(key) {
  const qs = window.location.search;
  const params = new URLSearchParams(qs);
  return params.get(key) || "";
}

function show_debug() {
  const debug_div = document.getElementById("debug_div");
  debug_div.classList.remove("hidden");
}
function hide_debug() {
  const debug_div = document.getElementById("debug_div");
  debug_div.classList.add("hidden");
}
function debug_countdown() {
  debug_taps += 1;
  if (debug_taps > 10) {
    show_debug();
  }
}

function hide_loading() {
  const loading = document.getElementById("loading");
  loading.classList.add("hidden");
}

function handle_stream(stream) {
  const video = document.querySelector('video');
  const videoTracks = stream.getVideoTracks();
  debug_log(`total video tracks: ${videoTracks.length} Using video device: ${videoTracks[0].label}`);
  window.stream = stream; // make variable available to browser console
  video.srcObject = stream;
  video.play();
  track = videoTracks[0];
  camera_capabilities = track.getCapabilities();
  const caps = document.getElementById("caps");
  caps.innerHTML = JSON.stringify(camera_capabilities);
  const settings = track.getSettings();
  console.log(settings);
  const canvas = document.getElementById("original");
  frame_width = settings.width;
  frame_height = settings.height;
  video.width = frame_width;
  video.height = frame_height;
  canvas.width = frame_width;
  canvas.height = frame_height;
}

function handleError(error) {
  debug_log(error);
  console.log(error);
}

function debug_log(msg) {
  debug_logs.push(Date.now() / 1000 + ": " + msg);
  const nentries = 20;
  while (debug_logs.length > nentries) {
    debug_logs.shift();
  }
  var output = "";
  for (var l of debug_logs) {
    output = `<div>${l}</div>` + output;
  }
  console.log(msg);
  const di = document.getElementById("logs");
  di.innerHTML = output;
}

async function start_camera(e) {
  try {
    const constraints = window.constraints = {
      audio: false,
      video: {
        facingMode: "environment",
        focusDistance: 0.12,
        focusMode: "manual",
        width: { ideal: 2000 },
      },
    };
    const supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
    const cons = document.getElementById("cons");
    cons.innerHTML = JSON.stringify(supportedConstraints);
    debug_log(navigator.userAgent);

    const stream = await navigator.mediaDevices.getUserMedia(constraints);
    handle_stream(stream);
    const zoom = get_query("zoom");
    if (zoom) {
      set_zoom(zoom);
    }

  } catch (e) {
    handleError(e);
  }
}

function is_emblem_qr_pattern(p)
{
  if (p.search(/code=[0-9a-zA-Z]+/) >= 0) return true;
  if (p.search(/c=[0-9a-zA-Z]+/) >= 0) return true;
  if (p.search(/https:\/\/xy.ltd\/v\/[0-9a-zA-Z]+/) >= 0) return true;
  return false;
}

function make_hint_text(r) {
  var qr_is_valid = false;
  if (r.qrcode && r.qrcode.length > 0) {
    qr_is_valid = is_emblem_qr_pattern(r.qrcode);
    if (!qr_is_valid) {
      return "无效编码";
    }
  }
  if (qr_is_valid) {
    var err = r.err || "";
    if (err.includes("margin too small")) {
      return "对齐定位点";
    } else if (err.includes("energy check failed") || err.includes("cannot detect angle")) {
      return "移近一点";
    }
  }
  return "对齐定位点";
}

function handle_frame() {
  try {
    do_handle_frame();
  } catch (e) {
    debug_log("handle frame exception: " + e);
  }
  setTimeout(handle_frame, 20);
}

image_data_urls = [];

function do_handle_frame() {
  if (busy) return;
  const canvas = document.getElementById("original");
  const video = document.getElementById("video");

  canvas.width = video.width;
  canvas.height = video.height;

  canvas.style.width = video.width / 4 + "px";
  canvas.style.height = video.height / 4 + "px";

  const ctx = canvas.getContext("2d");
  ctx.drawImage(video, 0, 0);
  const id = ctx.getImageData(0, 0, canvas.width, canvas.height);
  console.log(Module);
  var buf = Module._malloc(id.data.length * id.data.BYTES_PER_ELEMENT);
  Module.HEAPU8.set(id.data, buf);
  var r = Module.ccall('qrtool_angle', 'string', ['number', 'number', 'number', 'number', 'number'], [buf, id.width, id.height, 0, camera_sensitivity]);
  Module._free(buf);
  debug_log(r);
  const res = JSON.parse(r);
  const is_valid_pattern = res.qrcode && res.qrcode.length && is_emblem_qr_pattern(res.qrcode);
  if (res.qrcode && should_check_auto_torch) {
    start_check_auto_torch(res.qrcode);
  }
  if (done_checking_auto_torch && is_valid_pattern && res.ok) {
    var data_url = canvas.toDataURL("image/jpeg", 1.0);
    image_data_urls.push(data_url);
    if (image_data_urls.length >= 3) {
      submit_image(res.qrcode, res.angle, image_data_urls);
      image_data_urls = [];
    } else {
      pending_hint = make_hint_text(res);
    }
  } else {
    pending_hint = make_hint_text(res);
  }
}

async function start_check_auto_torch(qrcode)
{
  should_check_auto_torch = false;
  var r = await fetch("https://themblem.com/api/v1/check-auto-torch/?qrcode=" + encodeURIComponent(qrcode), {
    method: "GET",
  });
  var d = await r.json();
  debug_log(JSON.stringify(d));
  if (d.enable_auto_torch && !torch) {
    toggle_torch();
  }
  camera_sensitivity = d.camera_sensitivity || 1.0;
  setTimeout(() => {
    done_checking_auto_torch = true;
  }, 200);
}

function set_zoom(zoom) {
  if (camera_capabilities.zoom) {
    debug_log("set zoom by applying constraints");
    track.applyConstraints({advanced: [ {zoom} ]});
  } else {
    debug_log("set zoom by scaling video element");
    add_style_by_query("video.preview", "transform", `scale(${zoom})`);
  }
}

let torch = false;
function toggle_torch() {
  torch = !torch;
  track.applyConstraints({advanced: [ {torch: torch} ]});
  if (torch) {
    add_class_by_query(".bottomfixed .torch.action", "highlight");
  } else {
    remove_class_by_query(".bottomfixed .torch.action", "highlight");
  }
}

function show_spinner() {
  remove_class_by_query('.verifyspin', 'hidden');
  remove_class_by_query('.spin-image', 'spin-only');
  remove_class_by_query('.spin-image', 'spin-and-shrink');
  setTimeout(() => {
    add_class_by_query('.spin-image', 'spin-and-shrink');
    setTimeout(() => {
      remove_class_by_query('.spin-image', 'spin-and-shrink');
      add_class_by_query('.spin-image', 'spin-only');
    }, 3000);
  }, 0);
}

function hide_spinner() {
  add_class_by_query('.verifyspin', 'hidden');
}

async function submit_image(qrcode, angle, image_data_urls) {
  busy = true;
  var begin = Date.now();
  show_spinner();
  debug_log(`submit: qrcode: ${qrcode} angle: ${angle}`);
  try {
    // TODO: pass these parameters from query string
    var emblem_id = get_query("emblem_id");
    var nick_name = get_query("nick_name");
    var realip = get_query("realip");
    var phonemodel = get_query("phonemodel");
    var data = {
        emblem_id,
        nick_name,
        realip,
        qrcode,
        angle,
        phonemodel,
        image_data_urls,
        log: debug_logs.join("\n"),
    };
    var r = await fetch("https://themblem.com/api/v1/qr-verify/", {
      method: "POST",
      body: JSON.stringify(data),
      headers: {
      'Content-Type': 'application/json'
      },
    });
    var d = await r.json();
    debug_log(JSON.stringify(d));
    if (d.serial_code) {
      if (wx) {
        var delay = 3000 - (Date.now() - begin);
        setTimeout(() => {
          if (get_query("ai_chat_mode")) {
            wx.miniProgram.redirectTo({
              url: '/pages/chat/chat?serial_code=' + d.serial_code,
            });
          } else {
            wx.miniProgram.redirectTo({
              url: '/pages/productinfo/productinfo?serial_code=' + d.serial_code,
            });
          }
        }, delay > 0 ? delay : 0);
      }
    } else {
      this.show_modal('verifyfailed');
    }
  } catch (e) {
    debug_log(`submission error: ${e}`);
    this.show_modal('verifyfailed');
  }
}

var pending_hint = null;

function update_hint() {
  var now = Date.now();
  if (pending_hint) {
    const hint_dev = document.getElementById("hint");
    hint_dev.innerHTML = pending_hint;
    pending_hint = null;
  }
}

function start() {
  console.log("start");
  setTimeout(handle_frame, 100);
  hide_loading();
  if (get_query("debug")) {
    show_debug();
  }
  setInterval(update_hint, 1000);
  setTimeout(() => {
    show_tooltip();
  }, 15000);
}

function show_tooltip() {
  remove_class_by_query(".tooltip", "hidden");
  add_class_by_query(".bottomfixed .action", "highlight");
}

function hide_tooltip() {
  add_class_by_query(".tooltip", "hidden");
  remove_class_by_query(".bottomfixed .action", "highlight");
}

async function init_scanguide() {
  var r = await fetch('https://emblem-resources.oss-cn-guangzhou.aliyuncs.com/scan-guide-1080x1920-3.json');
  var d = await r.json();
  console.log("start scanguide", d);
  const elem = document.getElementById("scanguide");
  scanguide_anime = bodymovin.loadAnimation({
    container: elem,
    animationData: d,
    // path: 'data.json',
    renderer: 'svg',
    loop: true,
    autoplay: true,
    name: "Scan guide",
  })
}

function add_style_by_query(query, property, style) {
  var list = document.querySelectorAll(query);
  for (var i = 0; i < list.length; ++i) {
    list[i].style[property] = style;

  }
}

function add_class_by_query(query, to_add) {
  var list = document.querySelectorAll(query);
  for (var i = 0; i < list.length; ++i) {
    list[i].classList.add(to_add);
  }
}

function remove_class_by_query(query, to_remove) {
  var list = document.querySelectorAll(query);
  for (var i = 0; i < list.length; ++i) {
    list[i].classList.remove(to_remove);
  }
}

var modals = ['serviceqr', 'scanguide', 'verifyfailed', 'verifyspin'];

function hide_modal() {
  busy = false;
  scanguide_anime.stop();
  add_class_by_query(".modal", "hidden");
  hide_tooltip();
}

function show_modal(which) {
  busy = true;
  hide_tooltip();
  hide_spinner();
  add_class_by_query(".verifyspin", "hidden");
  remove_class_by_query(".modal", "hidden");
  remove_class_by_query('.actions', 'hidden');
  for (var m of modals) {
    add_class_by_query('.' + m, 'hidden');
  }
  if (which == 'scanguide') {
    scanguide_anime.goToAndPlay(0);
    remove_class_by_query('.scanguide', 'hidden');
  }
  if (which == 'serviceqr') {
    remove_class_by_query('.serviceqr', 'hidden');
    add_class_by_query('.actions', 'hidden');
  }
  if (which == 'verifyfailed') {
    remove_class_by_query('.verifyfailed', 'hidden');
  }
}

function set_service_qr_img() {
  var tid = get_query('tenant');
  var url = '/api/v1/service-qr/?tenant=' + tid;
  const elem = document.getElementById("service_img");
  elem.src = url;
}

(function() {
  start_camera();
  init_scanguide();
  set_service_qr_img();
})();
