class deviceData {
  constructor(lx, ly, lz, gsx, gsy, gsz, oh, op, or){
    this.lx = lx;
    this.ly = ly;
    this.lz = lz;
    this.gsx = gsx; // (gyro_speed_x) <- angular velocity
    this.gsy = gsy;
    this.gsz = gsz;
    this.oh = oh;
    this.op = op;
    this.or = or;
    this.px = 0.0;
    this.py = 0.0;
    this.pz = 0.0;
    this.ox = 0.0;
    this.oy = 0.0;
    this.oz = 0.0;
  }

  setBaseData(lx, ly, lz, gsx, gsy, gsz, oh, op, or){
    this.lx = lx;
    this.ly = ly;
    this.lz = lz;
    this.gsx = gsx;
    this.gsy = gsy;
    this.gsz = gsz;
    this.oh = oh;
    this.op = op;
    this.or = or;
  }

  setOH(oh){ this.oh = oh; }
  setOP(op){ this.op = op; }
  setOR(or){ this.or = or; }

  setPosX(px){ this.px = px; }
  setPosY(py){ this.py = py; }
  setPosZ(pz){ this.pz = pz; }

  setOriX(ox){ this.ox = ox; }
  setOriY(oy){ this.oy = oy; }
  setOriZ(oz){ this.oz = oz; }

  reset(){
    this.px = 0;
    this.py = 0;
    this.pz = 0;
    this.ox = 0;
    this.oy = 0;
    this.oz = 0; 
  }

  getLX() { return this.lx; }
  getLY() { return this.ly; }
  getLZ() { return this.lz; }
  getGSX() { return this.gsx; }
  getGSY() { return this.gsy; }
  getGSZ() { return this.gsz; }
  getOH() { return this.oh; }
  getOP() { return this.op; }
  getOR() { return this.or; }
  getOH() { return this.oh; }
  getPX() { return this.px; }
  getPY() { return this.py; }
  getPZ() { return this.pz; }
  getOX() { return this.ox; }
  getOY() { return this.oy; }
  getOZ() { return this.oz; }

  max() {
    var m  = -1;
    if (Math.abs(this.gsx) > Math.abs(this.gsy)) m = 0;
    else m = 1;
    if (Math.abs(this.gsz) > Math.abs(this.gsx) && Math.abs(this.gsz) > Math.abs(this.gsy)) m = 2;
    return m;    
  }

  moveCheck() {
    if (this.gsx > 25 || this.gsx < -20 || this.gsy > 25 || this.gsy < -20 || this.gsz > 25 || this.gsz < -20){
      return true;
    }
    return false;
  }
}

var mode = -1;

function modeLock() { 
  if (mode < 0){
    mode = 100;
    return true;
  }
  return false
}

function modeRelese(){
  mode = -1;
}

var time = [Date.now(), Date.now()];
var time_diff = 0;

var stopTime = Date.now();
var lTime = 0;
var rTime = 0;

var dL = new deviceData(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
var dR = new deviceData(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
var dD = new deviceData(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);

var speedL = 0.0;
var positionL = 0.0;
var speedR = 0.0;
var positionR = 0.0;

var orienL = 0.0;
var orienR = 0.0;

function initLROri() {
  orienL = 0;
  orienR = 0;
}

var angleX = 0;
var angleY = 0;
var angleZ = 0;

var preOXL = 0;
var preOYL = 0;
var preOZL = 0;
var preOXR = 0;
var preOYR = 0;
var preOZR = 0;


var transV = [0.0, 0.0, 0.0];

function reset() {
  dL.reset(); 
  dR.reset(); 
  dD.reset(); 
  orienL = 0;
  orienR = 0;
  angleX = 0;
  angleY = 0;
  angleZ = 0;
  preOXL = 0;
  preOYL = 0;
  preOZL = 0;
  preOXR = 0;
  preOYR = 0;
  preOZR = 0;
}

/*------------------------------input data----------------------------*/
var firstDevice;
var secondDevice; 
// var serviceS;
// var myCharacteristic;

var started = false;
function leftScanning(){
  console.log("starting left scan")
  navigator.bluetooth.requestDevice({ filters: [{ services: ['0000c001-0000-1000-8000-00805f9b34fb'] }]
})
  .then(device => {
    firstDevice = device;
    console.log(device)
    return device.gatt.connect()
  })
  .then(server=>{
    console.log("passing service uuid")
    return server.getPrimaryService("0000c001-0000-1000-8000-00805f9b34fb");
  })
  .then(service=>{
    console.log("passing service characteristics uuid")
    return service.getCharacteristic("d7b83c07-eae9-469c-ba5c-12db92c7cfa2");
  })
  .then(characteristic=>{
    console.log("subscribing");
    characteristic.startNotifications().then(_ => {
      console.log('> Notifications started');
      characteristic.addEventListener('characteristicvaluechanged',
        handleLeftNotifications);
      started = true;
    });
  })
  .catch(error => { console.error(error.message); });
};

function rightScanning(){
    console.log("starting right scan")
    navigator.bluetooth.requestDevice({ filters: [{ services: ['0000c001-0000-1000-8000-00805f9b34fb'] }]
  })
    .then(device => {
      secondDevice = device;
      console.log(device)
      return device.gatt.connect()
    })
    .then(server=>{
      console.log("passing service uuid")
      return server.getPrimaryService("0000c001-0000-1000-8000-00805f9b34fb");
    })
    .then(service=>{
      console.log("passing service characteristics uuid")
      return service.getCharacteristic("d7b83c07-eae9-469c-ba5c-12db92c7cfa2");
    })
    .then(characteristic=>{
      console.log("subscribing");
      characteristic.startNotifications().then(_ => {
        console.log('> Notifications started');
        characteristic.addEventListener('characteristicvaluechanged',
          handleRightNotifications);
        started = true;
      });
    })
    .catch(error => { console.error(error.message); });
  };

function fromTwosComplement(twosComplement, numberBytes) 
{   
    var numberBits = (numberBytes || 1) * 8;
    
    if (twosComplement < 0 || twosComplement > (1 << numberBits) - 1)
        throw "Two's complement out of range given " + numberBytes + " byte(s) to represent.";
    
    // If less than the maximum positive: 2^(n-1)-1, the number stays positive
    if (twosComplement <= Math.pow(2, numberBits - 1) - 1)
        return twosComplement;
    
    // Else convert to it's negative representation
    return -(((~twosComplement) & ((1 << numberBits) - 1)) + 1);
}

function handleLeftNotifications(e) {
  //handle data here
  let dataView = new Uint8Array(e.target.value.buffer)

  var pHeading = (dataView[12]<<8) + dataView[13]
  var pPitch = (dataView[14]<<8) + dataView[15]
  var pRoll = (dataView[16]<<8) + dataView[17]
  //pPitch = 65200;
  if(pPitch != null && pRoll != null){
    pPitch = fromTwosComplement(pPitch,2);
    pRoll = fromTwosComplement(pRoll,2);
    // heading = pPitch > 16383 ? pHeading * 0.011 : 360-(pHeading * 0.011)
  }
  else {
    pHeading = 0;
    pPitch = 0;
    pRoll = 0;
  }

  dL.setBaseData(
            fromTwosComplement((dataView[2]<<8) + dataView[3],2)*0.000244,
            fromTwosComplement((dataView[0]<<8) + dataView[1],2)*0.000244,
            fromTwosComplement((dataView[4]<<8) + dataView[5],2)*0.000244,
            fromTwosComplement((dataView[10]<<8) + dataView[11],2)*0.061037,
            fromTwosComplement((dataView[6]<<8) + dataView[7],2)*0.061037,
            fromTwosComplement((dataView[8]<<8) + dataView[9],2)*0.061037,
            pHeading * 0.011,
            pPitch * 0.011,
            pRoll * 0.011);
}

function handleRightNotifications(e) {
   //handle data here
   let dataView = new Uint8Array(e.target.value.buffer)

   var pHeading = (dataView[12]<<8) + dataView[13]
   var pPitch = (dataView[14]<<8) + dataView[15]
   var pRoll = (dataView[16]<<8) + dataView[17]
   //pPitch = 65200;
   if(pPitch != null && pRoll != null){
     pPitch = fromTwosComplement(pPitch,2);
     pRoll = fromTwosComplement(pRoll,2);
     // heading = pPitch > 16383 ? pHeading * 0.011 : 360-(pHeading * 0.011)
   }
   else {
     pHeading = 0;
     pPitch = 0;
     pRoll = 0;
   }
 
   dR.setBaseData(
             fromTwosComplement((dataView[2]<<8) + dataView[3],2)*0.000244148075808,
             fromTwosComplement((dataView[0]<<8) + dataView[1],2)*0.000244148075808,
             fromTwosComplement((dataView[4]<<8) + dataView[5],2)*0.000244148075808,
             fromTwosComplement((dataView[10]<<8) + dataView[11],2)*0.061037018951994,
             fromTwosComplement((dataView[6]<<8) + dataView[7],2)*0.061037018951994,
             fromTwosComplement((dataView[8]<<8) + dataView[9],2)*0.061037018951994,
             pHeading * 0.011,
             pPitch * 0.011,
             pRoll * 0.011);
}


  /*------------------------------data control function--------------------*/


function sleep(ms) {
  const wakeUpTime = Date.now() + ms;
  while (Date.now() < wakeUpTime) {
      if (Date.now() > wakeUpTime) break;
  }
}

function modeOneHandCheck(){
  if (dL.moveCheck() && !dR.moveCheck()){
    if (lTime == 0){
      lTime = Date.now();
    }
    else {
      var timeNow = Date.now();
      var ldiff = timeNow - stopTime;
      if (ldiff > 200) {
        var LM = dL.max();
        if (LM == 0)
          mode = modeLock() ? 3 : -1;
        else if (LM == 1) 
          mode = modeLock() ? 4 : -1;
        else
          mode = modeLock() ? 5 : -1;
  
        lTime = 0;
      }
    }
  }
  else if (!dL.moveCheck() && dR.moveCheck()){
    if (rTime == 0){
      rTime = Date.now();
    }
    else {
      var timeNow = Date.now();
      var rdiff = timeNow - rTime;
      if (rdiff > 200) {
        var RM = dR.max();
        if (RM == 0)
          mode = modeLock() ? 3 : -1;
        else if (RM == 1) 
          mode = modeLock() ? 4 : -1;
        else
          mode = modeLock() ? 5 : -1;

        rTime = 0;
      }
    }
  }
}

function modeReleseCheck(){
  if(!dL.moveCheck() && !dR.moveCheck()) {
    if (stopTime == 0){
      stopTime = Date.now();
    }
    else {
      var timeNow = Date.now();
      var diff = timeNow - stopTime;
      if (diff > 2000) {
        modeRelese();
        console.log(mode);
        stopTime = 0;
      }
    }
  }
}

function modeCheck() {
  if (mode == -1) {
    if (dL.moveCheck() && dR.moveCheck()) {
      var LM = dL.max();
      var RM = dR.max();

      if (LM == RM){
        if (LM == 0){
          if ((dL.getGSX() > 20 && dR.getGSX() > 20) || (dL.getGSX() < -10 && dR.getGSX() < -10)) 
            mode = modeLock() ? 0 : -1;
          else if ((dL.getGSX() > 20 && dR.getGSX() < -10) || (dL.getGSX() < -10 && dR.getGSX() > 20)) 
            mode = modeLock() ? 6 : -1;
        }
        else if (LM == 1){
          if ((dL.getGSY() > 20 && dR.getGSY() > 20) || (dL.getGSY() < -10 && dR.getGSY() < -10)) 
              mode = modeLock() ? 1 : -1;
          else if ((dL.getGSY() > 20 && dR.getGSY() < -10) || (dL.getGSY() < -10 && dR.getGSY() > 20)) 
            mode = modeLock() ? 7 : -1;
        }
        else{
          if ((dL.getGSZ() > 20 && dR.getGSZ() > 20) || (dL.getGSZ() < -10 && dR.getGSZ() < -10)) 
            mode = modeLock() ? 8 : -1;
          else if ((dL.getGSZ() > 20 && dR.getGSZ() < -10) || (dL.getGSZ() < -10 && dR.getGSZ() > 20)) 
            mode = modeLock() ? 2 : -1;
        }
      }
    }
    else {
      modeOneHandCheck();
    }   
    if (mode > -1) console.log(mode);
  }
  else {
    modeReleseCheck();
  }
}



function recodeSpX() { 
  if ((dL.getGSX() > 15 || dL.getGSX() < -10) && (dR.getGSX() > 15 || dR.getGSX() < -10)) {
    orienL = dL.getGSX()*time_diff*0.1;
    orienR = dR.getGSX()*time_diff*0.1;

    if (mode == 0) { 
      // x trans
      dD.setPosX( dD.getPX() + (orienL + orienR) / 2 );
      dD.setPosX( dD.getPX() > 5 ? 5 : dD.getPX() );
      dD.setPosX( dD.getPX() < -5 ? -5 : dD.getPX() );
    }
    else {
      // x scale
      if (orienL > 0 && orienR < 0) {
        dL.setPosX( dL.getPX() + orienL );
        dR.setPosX( dR.getPX() + orienR );
      }
      else if (dL.getPX() - dR.getPX() > 0) {
        dL.setPosX( dL.getPX() + orienL );
        dR.setPosX( dR.getPX() + orienR );
      }
      dL.setPosX( dL.getPX() > 5 ? 5 : dL.getPX() );
      dL.setPosX( dL.getPX() < -5 ? -5 : dL.getPX() );
      dR.setPosX( dR.getPX() > 5 ? 5 : dR.getPX() );
      dR.setPosX( dR.getPX() < -5 ? -5 : dR.getPX() );
    }
  }
}

function recodeSpY() { 
  if ((dL.getGSY() > 15 || dL.getGSY() < -10) && (dR.getGSY() > 15 || dR.getGSY() < -10)) {
      orienL = dL.getGSY()*time_diff*0.1;
      orienR = dR.getGSY()*time_diff*0.1;
  
      if (mode == 1) { 
        // y trans
        dD.setPosY( dD.getPY() + (orienL + orienR) / 2 );
        dD.setPosY( dD.getPY() > 2 ? 2 : dD.getPY() );
        dD.setPosY( dD.getPY() < -2 ? -2 : dD.getPY() );
      }
      else {
        // y scale
        if (orienL < 0 && orienR > 0) {
          dL.setPosY( dL.getPY() + orienL );
          dR.setPosY( dR.getPY() + orienR );
        }
        else if (-dL.getPY() + dR.getPY() > 0) {
          dL.setPosY( dL.getPY() + orienL );
          dR.setPosY( dR.getPY() + orienR );
        }
        dL.setPosY( dL.getPY() > 2 ? 2 : dL.getPY() );
        dL.setPosY( dL.getPY() < -2 ? -2 : dL.getPY() );
        dR.setPosY( dR.getPY() > 2 ? 2 : dR.getPY() );
        dR.setPosY( dR.getPY() < -2 ? -2 : dR.getPY() );
      }
  }
}

function recodeSpZ() {
  if ((dL.getGSZ() > 15 || dL.getGSZ() < -10) && (dR.getGSZ() > 15 || dR.getGSZ() < -10)) {
      orienL = dL.getGSZ()*time_diff*0.1;
      orienR = dR.getGSZ()*time_diff*0.1;
      
      if (mode == 2) { 
        // Z trans
        dD.setPosZ( dD.getPZ() + (-orienL + orienR) / 2 );
        dD.setPosZ( dD.getPZ() > 7 ? 7 : dD.getPZ() );
        dD.setPosZ( dD.getPZ() < -2 ? -2 : dD.getPZ() );
      }
      else {
        // Z scale
        if (orienL < 0 && orienR < 0) {
          dL.setPosZ( dL.getPZ() + orienL );
          dR.setPosZ( dR.getPZ() + orienR );
        }
        else if (-dL.getPZ() - dR.getPZ() > 0) {
          dL.setPosZ( dL.getPZ() + orienL );
          dR.setPosZ( dR.getPZ() + orienR );
        }
        dL.setPosZ( dL.getPZ() > 5 ? 5 : dL.getPZ() );
        dL.setPosZ( dL.getPZ() < -5 ? -5 : dL.getPZ() );
        dR.setPosZ( dR.getPZ() > 5 ? 5 : dR.getPZ() );
        dR.setPosZ( dR.getPZ() < -5 ? -5 : dR.getPZ() );
      }
  }
}

function recodeOX() { 
  if ((dL.getGSX() > 15 || dL.getGSX() < -10) && (dR.getGSX() > 15 || dR.getGSX() < -10)) {
    var ori_diff_l = dL.getOX() - preOXL;
    var ori_diff_r = dR.getOX() - preOXR;

    ori_diff_l = ori_diff_l > 100 ? 0 : ori_diff_l;
    ori_diff_l = ori_diff_l < -100 ? 0 : ori_diff_l;
    ori_diff_r = ori_diff_r > 100 ? 0 : ori_diff_r;
    ori_diff_r = ori_diff_r < -100 ? 0 : ori_diff_r;
    
    orienL = (dL.getGSX()*time_diff - ori_diff_l) / 2;
    orienR = (dR.getGSX()*time_diff - ori_diff_r) / 2;
    angleX = angleX + (orienL + orienR) / 2;
    dD.setOriX(angleX%360);
  }
}
function recodeOY() { 
  if ((dL.getGSY() > 15 || dL.getGSY() < -10) && (dR.getGSY() > 15 || dR.getGSY() < -10)) {

    var ori_diff_l = dL.getOY() - preOYL;
    var ori_diff_r = dR.getOY() - preOYR;

    ori_diff_l = ori_diff_l > 100 ? 0 : ori_diff_l;
    ori_diff_l = ori_diff_l < -100 ? 0 : ori_diff_l;
    ori_diff_r = ori_diff_r > 100 ? 0 : ori_diff_r;
    ori_diff_r = ori_diff_r < -100 ? 0 : ori_diff_r;

    orienL = (dL.getGSY()*time_diff - ori_diff_l) / 2;
    orienR = (dR.getGSY()*time_diff - ori_diff_r) / 2;

    angleY = angleY + (-orienL+orienR)/2;
    dD.setOriY(angleY%360);
  }
}
function recodeOZ() {
  if ((dL.getGSZ() > 15 || dL.getGSZ() < -10) && (dR.getGSZ() > 15 || dR.getGSZ() < -10)) {

    var ori_diff_l = dL.getOZ() - preOZL;
    var ori_diff_r = dR.getOZ() - preOZR;

    ori_diff_l = ori_diff_l > 100 ? 0 : ori_diff_l;
    ori_diff_l = ori_diff_l < -100 ? 0 : ori_diff_l;
    ori_diff_r = ori_diff_r > 100 ? 0 : ori_diff_r;
    ori_diff_r = ori_diff_r < -100 ? 0 : ori_diff_r;
    
    orienL = (dL.getGSZ()*time_diff - ori_diff_l) / 2;
    orienR = (dR.getGSZ()*time_diff - ori_diff_r) / 2;
    
    angleZ = angleZ + (-orienL+orienR)/2;
    dD.setOriZ(angleZ%360);
  }
}


function preOrien() {
  preOXL = dL.getOX();
  preOYL = dL.getOY();
  preOZL = dL.getOZ();
  preOXR = dR.getOX();
  preOYR = dR.getOY();
  preOZR = dR.getOZ();
}

function controlLOH() {
  if (dL.getOP() < 130 && dL.getOP() > -130)
    dL.setOriX(dL.getOH());
}

function controlLOP() {
  dL.setOriY(dL.getOP());
}

function controlLOR() {
  if (dL.getOP() < 135 && dL.getOP() > -135){
    dL.setOriZ(dL.getOR());
  }
}


function controlROH() {
  if (dR.getOP() < 130 && dR.getOP() > -130)
    dR.setOriX(dR.getOH());
}

function controlROP() {
  dR.setOriY(dR.getOP());
}

function controlROR() {
  if (dR.getOP() < 135 && dR.getOP() > -135){
    dR.setOriZ(dR.getOR());
  }
}

/*--------------------------------translation Matrix-----------------------*/ 

function scaleLinear(M){
  scaleV=[1+Math.abs(dL.getPX() - dR.getPX()), 1+Math.abs(-dL.getPY() + dR.getPY()), 1+Math.abs(dL.getPZ() + dR.getPZ())];
  glMatrix.mat4.scale(M, M, scaleV);

  return M;
}

function rotateOri(M) {
  var xaxis = [1.0, 0.0, 0.0];
  // var yaxis = [0.0, 1.0, 0.0];
  var zaxis = [0.0, 0.0, 1.0];

  glMatrix.mat4.rotateY(M, M, dD.getOX()*0.0174444); //heading //3.14/180
  glMatrix.vec3.transformMat4(xaxis, xaxis, M);
  glMatrix.mat4.rotate(M, M, dD.getOZ()*0.0174444, xaxis);//pitch
  glMatrix.vec3.transformMat4(zaxis, zaxis, M);
  glMatrix.mat4.rotate(M, M, dD.getOY()*0.0174444, zaxis);//roll
  
  return M;
}

function moveLinear(M){
  transV = [dD.getPX(), dD.getPY(), dD.getPZ()]
  glMatrix.mat4.translate(M, M, transV);
  return M;
}

function matrixByMode(){
  var M = [1.0, 0.0, 0.0, 0.0,
          0.0, 1.0, 0.0, 0.0,
          0.0, 0.0, 1.0, 0.0,
          0.0, 0.0, 0.0, 1.0];

  M = moveLinear(M);
  M = rotateOri(M);
  M = scaleLinear(M);

  return M;
}

/*-----------------------------------Web gl code---------------------------*/


//code for cube
var gl;
//var texture;
//var videoE;
var image;
var viewMatrixData = new Array(1400);

var minutes = 1000 * 60;
var hours = minutes * 60;
var days = hours * 24;
var years = days * 365;
var d = new Date();
var t = d.getTime(); //밀리초

var yt = Math.round(t / years);

function testGLError(functionLastCalled) {
    var lastError = gl.getError();

    if (lastError != gl.NO_ERROR) {
        alert(functionLastCalled + " failed (" + lastError + ")");
        return false;
    }
    return true;
}

function initialiseGL(canvas) {
    try {
        // Try to grab the standard context. If it fails, fallback to experimental
        gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
        gl.viewport(0, 0, canvas.width, canvas.height);
    }
    catch (e) {
    }

    if (!gl) {
        alert("Unable to initialise WebGL. Your browser may not support it");
        return false;
    }
    return true;
}

var vertexData = [
    // Backface (RED/WHITE) -> z = 0.5
    -0.5, -0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
     0.5,  0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
     0.5, -0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
    -0.5, -0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
    -0.5,  0.5, -0.5,  1.0, 0.0, 0.0, 1.0,
     0.5,  0.5, -0.5,  1.0, 1.0, 1.0, 1.0,
    // Front (BLUE/WHITE) -> z = 0.5
    -0.5, -0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
     0.5,  0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
     0.5, -0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
    -0.5, -0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
    -0.5,  0.5,  0.5,  0.0, 0.0, 1.0, 1.0,
     0.5,  0.5,  0.5,  1.0, 1.0, 1.0, 1.0,
    // LEFT (GREEN/WHITE) -> z = 0.5
    -0.5, -0.5, -0.5,  0.0, 1.0, 0.0, 1.0,
    -0.5,  0.5,  0.5,  0.0, 1.0, 0.0, 1.0,
    -0.5,  0.5, -0.5,  0.0, 1.0, 0.0, 1.0,
    -0.5, -0.5, -0.5,  0.0, 1.0, 0.0, 1.0,
    -0.5, -0.5,  0.5,  0.0, 1.0, 0.0, 1.0,
    -0.5,  0.5,  0.5,  0.0, 1.0, 1.0, 1.0,
    // RIGHT (YELLOE/WHITE) -> z = 0.5
     0.5, -0.5, -0.5,  1.0, 1.0, 0.0, 1.0,
     0.5,  0.5,  0.5,  1.0, 1.0, 0.0, 1.0,
     0.5,  0.5, -0.5,  1.0, 1.0, 0.0, 1.0,
     0.5, -0.5, -0.5,  1.0, 1.0, 0.0, 1.0,
     0.5, -0.5,  0.5,  1.0, 1.0, 0.0, 1.0,
     0.5,  0.5,  0.5,  1.0, 1.0, 1.0, 1.0,
    // BOTTON (MAGENTA/WHITE) -> z = 0.5
    -0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,
     0.5, -0.5,  0.5,  1.0, 0.0, 1.0, 1.0,
     0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,
    -0.5, -0.5, -0.5,  1.0, 0.0, 1.0, 1.0,
    -0.5, -0.5,  0.5,  1.0, 0.0, 1.0, 1.0,
     0.5, -0.5,  0.5,  1.0, 1.0, 1.0, 1.0,
    // TOP (CYAN/WHITE) -> z = 0.5
    -0.5,  0.5, -0.5,  0.0, 1.0, 1.0, 1.0,
     0.5,  0.5,  0.5,  0.0, 1.0, 1.0, 1.0,
     0.5,  0.5, -0.5,  0.0, 1.0, 1.0, 1.0,
    -0.5,  0.5, -0.5,  0.0, 1.0, 1.0, 1.0,
    -0.5,  0.5,  0.5,  0.0, 1.0, 1.0, 1.0,
     0.5,  0.5,  0.5,  1.0, 1.0, 1.0, 1.0
];

var screenData = [
    //screen data, texture plane
    -1.0, -1.0, -1.0,  
    -1.0,  1.0, -1.0,  
     1.0,  1.0, -1.0,  
    -1.0, -1.0, -1.0,  
     1.0, -1.0, -1.0,  
     1.0,  1.0, -1.0  
];

const textureCoordinates = [
    // Front
    0.0,  0.0,
    0.0,  1.0,
    1.0,  1.0,
    0.0,  0.0,
    1.0,  0.0,
    1.0,  1.0
  ];

function initialiseBuffer() {

    gl.vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);

    gl.screenBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.screenBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(screenData), gl.STATIC_DRAW);

    gl.textureBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.textureBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), gl.STATIC_DRAW);

    return testGLError("initialiseBuffers");
}

function initTexture() {
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);

    var pixel = new Uint8Array([0, 0, 120, 255]);  // opaque blue
    //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    return texture;
}

function updateTexture(texture) {
    const level = 0;
    
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE);
}

copyVideo = false;

function initialiseShaders() {

    var fragmentShaderSource1 = '\
			varying highp vec4 color; \
			void main(void) \
			{ \
				gl_FragColor = color;\
			}';

    gl.fragShader1 = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(gl.fragShader1, fragmentShaderSource1);
    gl.compileShader(gl.fragShader1);
    // Check if compilation succeeded
    if (!gl.getShaderParameter(gl.fragShader1, gl.COMPILE_STATUS)) {
        alert("Failed to compile the fragment shader1.\n" + gl.getShaderInfoLog(gl.fragShader1));
        return false;
    }

    // Vertex shader code1
    var vertexShaderSource1 = '\
			attribute highp vec4 myVertex; \
			attribute highp vec4 myColor; \
			uniform mediump mat4 mMat; \
			uniform mediump mat4 vMat; \
			uniform mediump mat4 pMat; \
			varying  highp vec4 color;\
			void main(void)  \
			{ \
				gl_Position = pMat * vMat * mMat * myVertex; \
				gl_PointSize = 8.0; \
				color = myColor; \
			}';

    gl.vertexShader1 = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(gl.vertexShader1, vertexShaderSource1);
    gl.compileShader(gl.vertexShader1);
    // Check if compilation succeeded
    if (!gl.getShaderParameter(gl.vertexShader1, gl.COMPILE_STATUS)) {
        alert("Failed to compile the vertex shader1.\n" + gl.getShaderInfoLog(gl.vertexShader1));
        return false;
    }

    var fragmentShaderSource2 = '\
            precision mediump float; \
            varying vec2 vTexture; \
            uniform sampler2D uSampler; \
			void main(void) \
			{ \
				gl_FragColor = texture2D(uSampler, vTexture); \
			}';

    gl.fragShader2 = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(gl.fragShader2, fragmentShaderSource2);
    gl.compileShader(gl.fragShader2);
    // Check if compilation succeeded
    if (!gl.getShaderParameter(gl.fragShader2, gl.COMPILE_STATUS)) {
        alert("Failed to compile the fragment shader2.\n" + gl.getShaderInfoLog(gl.fragShader2));
        return false;
    }

    // Vertex shader code2
    var vertexShaderSource2 = '\
			attribute vec3 myVertex; \
			attribute vec2 myTexture; \
			uniform mediump mat4 mMat; \
			uniform mediump mat4 vMat; \
			uniform mediump mat4 pMat; \
			varying vec2 vTexture; \
			void main(void)  \
			{ \
				gl_Position = pMat * vMat * mMat * vec4(myVertex, 1.0); \
				vTexture = myTexture; \
			}';

    gl.vertexShader2 = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(gl.vertexShader2, vertexShaderSource2);
    gl.compileShader(gl.vertexShader2);
    // Check if compilation succeeded
    if (!gl.getShaderParameter(gl.vertexShader2, gl.COMPILE_STATUS)) {
        alert("Failed to compile the vertex shader2.\n" + gl.getShaderInfoLog(gl.vertexShader2));
        return false;
    }

    // Create the shader program
    gl.programObject1 = gl.createProgram();
    // Attach the fragment and vertex shaders to it
    gl.attachShader(gl.programObject1, gl.fragShader1);
    gl.attachShader(gl.programObject1, gl.vertexShader1);
    // Bind the custom vertex attribute "myVertex" to location 0
    gl.bindAttribLocation(gl.programObject1, 0, "myVertex");
    gl.bindAttribLocation(gl.programObject1, 1, "myColor");

    gl.programObject2 = gl.createProgram();
    // Attach the fragment and vertex shaders to it
    gl.attachShader(gl.programObject2, gl.fragShader2);
    gl.attachShader(gl.programObject2, gl.vertexShader2);
    gl.bindAttribLocation(gl.programObject2, 2, "myVertex");
    gl.bindAttribLocation(gl.programObject2, 3, "myTexture");
    //gl.bindAttribLocation(gl.programObject, 2, "aTexture")
    // Link the program
    gl.linkProgram(gl.programObject1);
    // Check if linking succeeded in a similar way we checked for compilation errors
    if (!gl.getProgramParameter(gl.programObject1, gl.LINK_STATUS)) {
        alert("Failed to link the program1.\n" + gl.getProgramInfoLog(gl.programObject1));
        return false;
    }

    gl.linkProgram(gl.programObject2);
    // Check if linking succeeded in a similar way we checked for compilation errors
    if (!gl.getProgramParameter(gl.programObject2, gl.LINK_STATUS)) {
        alert("Failed to link the program2.\n" + gl.getProgramInfoLog(gl.programObject2));
        return false;
    }

    gl.useProgram(gl.programObject1);
    gl.useProgram(gl.programObject2);

    return testGLError("initialiseShaders");
}


var tMs = [1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    0.0, 0.0, 0.0, 1.0
];

function drawTexture(texture){
    //texture screen 그리기 
    gl.useProgram(gl.programObject2);

    var worldMatrix = new Float32Array(16);
    var viewMatrix = new Float32Array(16);
    var projMatrix = new Float32Array(16);

    glMatrix.mat4.identity(worldMatrix);
    glMatrix.mat4.identity(viewMatrix);

    var matWorldUniformLocation2 = gl.getUniformLocation(gl.programObject2, 'mMat');
	var matViewUniformLocation2 = gl.getUniformLocation(gl.programObject2, 'vMat');
    var matProjUniformLocation2 = gl.getUniformLocation(gl.programObject2, 'pMat');

    
    //glMatrix.mat4.lookAt(viewMatrix, [0.0, 0.0, 5.0], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]); //viewMatrix가 카메라 
    glMatrix.mat4.ortho(projMatrix, -1, 1, -1, 1, 0, 1);

    gl.uniformMatrix4fv(matWorldUniformLocation2, gl.FALSE, worldMatrix);
    gl.uniformMatrix4fv(matViewUniformLocation2, gl.FALSE, viewMatrix);
    gl.uniformMatrix4fv(matProjUniformLocation2, gl.FALSE, projMatrix);

    if (!testGLError("gl.uniformMatrix4fv")) {
        return false;
    }

    gl.bindBuffer(gl.ARRAY_BUFFER, gl.screenBuffer);
    gl.enableVertexAttribArray(2);
    gl.vertexAttribPointer(2, 3, gl.FLOAT, gl.FALSE, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.textureBuffer);
    gl.enableVertexAttribArray(3);
    gl.vertexAttribPointer(3, 2, gl.FLOAT, gl.FALSE, 0, 0);

    //텍스쳐 적용
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.uniform1i(gl.getUniformLocation(gl.programObject2, "uSampler"), 0);

    if (!testGLError("gl.vertexAttribPointer")) {
        return false;
    }

    //텍스쳐 스크린 그리는 드로 근데 바인드가 안됨 왜???
    gl.drawArrays(gl.TRIANGLES, 0, 6); 

    if (!testGLError("gl.drawArrays")) {
        return false;
    }
}

var lookDistance = 5.0;
function incDistance(){
    lookDistance += 0.1;
}
function decDistance(){
    lookDistance -= 0.1;
}

var frames = 0;

function drawCube(){
    gl.useProgram(gl.programObject1);

    var matWorldUniformLocation1 = gl.getUniformLocation(gl.programObject1, 'mMat');
	  var matViewUniformLocation1 = gl.getUniformLocation(gl.programObject1, 'vMat');
    var matProjUniformLocation1 = gl.getUniformLocation(gl.programObject1, 'pMat');

    var tMatrix = new Float32Array(16);
    glMatrix.mat4.identity(tMatrix);
    if (started) tMatrix = matrixByMode();

    var worldMatrix = new Float32Array(16);
    var viewMatrix = new Float32Array(16);
    var projMatrix = new Float32Array(16);


    glMatrix.mat4.identity(worldMatrix);
    glMatrix.mat4.identity(projMatrix);
    glMatrix.mat4.lookAt(viewMatrix, [0.0, 0.0, -lookDistance], [0.0, 0.0, 0.0], [0.0, 1.0, 0.0]); //viewMatrix가 카메라 
    glMatrix.mat4.perspective(projMatrix, glMatrix.glMatrix.toRadian(55.2), 1600.0 / 900.0, 0.1, 15.0); // 0.1 가장 가까운것 1000.0가장 먼것 , projMatrix

    //시간
    t = new Date().getTime();
    t = (t - start)/10;
    t = Math.floor(t);
    //console.log(t);
    if(started){
      //console.clear()
      //console.log(roll*57.32) 
    }
    glMatrix.mat4.mul(worldMatrix, tMatrix, worldMatrix);
    //glMatrix.mat4.mul(viewMatrix, iMs, viewMatrix); //vM lookAT이후 
    gl.uniformMatrix4fv(matWorldUniformLocation1, gl.FALSE, worldMatrix);
    gl.uniformMatrix4fv(matViewUniformLocation1, gl.FALSE, viewMatrix);
    gl.uniformMatrix4fv(matProjUniformLocation1, gl.FALSE, projMatrix);

    if (!testGLError("gl.uniformMatrix4fv")) {
        return false;
    }
    
    // */
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.vertexBuffer);
    gl.enableVertexAttribArray(0);
    gl.vertexAttribPointer(
        0, // Attribute location
        3, // Number of elements per attribute
        gl.FLOAT, // Type of elements
        gl.FALSE,
        28,
        0 // Offset from the beginning of a single vertex to this attribute
    );
    gl.enableVertexAttribArray(1);
    gl.vertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, 28, 12);
    
    if (!testGLError("gl.vertexAttribPointer")) {
        return false;
    }

    // 큐브 그리기
    gl.drawArrays(gl.TRIANGLES, 0, 36); 

    if (!testGLError("gl.drawArrays")) {
        return false;
    }
}

function renderScene(texture) {

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clearDepth(1.0);

  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.disable(gl.DEPTH_TEST);

  drawTexture(texture);
  gl.enable(gl.DEPTH_TEST);


  time[0] = time[1];
  time[1] = Date.now();
  time_diff = (time[1]-time[0])*0.001; //second

  modeCheck();

  if (mode == -1){
    initLROri();
    i = 0;
  } 
  else if (mode == 0) 
    recodeSpX();
  else if (mode == 1) 
    recodeSpY();
  else if (mode == 2) 
    recodeSpZ();
  else if (mode == 3) {
    controlLOH();
    controlROH();
    recodeOX();
  }
  else if (mode == 4){
    controlLOP();
    controlROP();
    recodeOY();
  }
  else if (mode == 5) {
    controlLOR();
    controlROR();
    recodeOZ();
  }
  else if (mode == 6)
    recodeSpX();
  else if (mode == 7)
    recodeSpY();
  else if (mode == 8) 
    recodeSpZ();    

  drawCube();
  preOrien();

  return true;
}


pA = [0, 1, 0];
tA = [1, 0, 0];
rA = [0, 0, 1];

function degreeToRadian(deg){
    var crad = glMatrix.glMatrix.toRadian(deg * 360 / 32767);
    return crad;
}


/* ----------------------mouse event function-------------------*/


var dragging = false;
var rightclick_count = 0;
var dblclick_count = 0;


function doDoubleClick(evt){
  if (modeLock()){
    modeRelese();
    reset();
  }
}

/*------------------------------main function------------------------*/

var start;

function main() {

    const canvas = document.querySelector('#helloapicanvas');
    this.addEventListener("dblclick", doDoubleClick, false);

    if (!initialiseGL(canvas)) {
        return;
    }


    if (!initialiseBuffer()) {
        return;
    }

    //attriblocation

    if (!initialiseShaders()) {
        return;
    }

    const texture = initTexture();

    // Render loop
    requestAnimFrame = (function () {
        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
      function (callback) {
          window.setTimeout(callback, 100);
      };
    })();

    t = 0;
    var p = 0;
    (function renderLoop() {
      if(!p){
          t = new Date().getTime();
          start = t;
          p = 1;
      }
      if (renderScene(texture)) {
          // Everything was successful, request that we redraw our scene again in the future
          requestAnimFrame(renderLoop);
      }
    })();

  }