

var juxtaposition = d3.select("#juxtaposition").append("svg")
  .attr("width", "100%");



let juxtacbs = {
  acidMouseleave: [],
  acidMouseOver: [],
  acidClick: [],
}

let juxtapreviewing;

function fireCallbacks(callbacks, protein, acid) {
  for (let cb of callbacks) {
    cb(protein, acid);
  }
}


function appendAcidPolygon(group, col, quadPoints, acid_index, protein_n, highlight, preview) {
  
  //var colors = colorMolecule[n];
  if (highlight && highlighted[protein_n] != null) {
    highlighted[protein_n].forEach(function callback(loopI) {
      //change colour if it is part of highlighted loop
      if ((acid_index >= loopI.start && acid_index <= loopI.end) ){ // does end count?
        col = "#66FF00";
      }
    });
  }


  if(preview && juxtapreviewing && juxtapreviewing.contains(protein_n, acid_index)){
    col = d3.hsl(col).brighter(1.5);
  }

  return group.append("polygon")
    .attr("fill", col)
    .attr("points", quadPoints)
    .attr("data-acid", acid_index)
    .on("mouseover", () => fireCallbacks(juxtacbs.acidMouseOver, protein_n, acid_index))
    .on("mouseleave", () => fireCallbacks(juxtacbs.acidMouseleave, protein_n, acid_index))
    .on("click", () => fireCallbacks(juxtacbs.acidClick, protein_n, acid_index));
}



function drawJuxtaCoilByAcid(group, protein_n, ending_acid, thisElementWidth, start, acid_count, startHeight, endHeight, col, highlight, preview) {
  let coil = group.append("g")
    .attr("name", "coil");
  let height = 3;
  let starting_acid = ending_acid - acid_count + 1;
  for (let currentAcid = 0; currentAcid < acid_count; currentAcid++) {
    let acid_index = starting_acid + currentAcid;
    let startX = start + (currentAcid * thisElementWidth);
    let endX = startX + thisElementWidth + 1;
    var quadPoints = (startX) + " " + (startHeight + height / 2) + ", "
      + (startX) + " " + (startHeight - height / 2) + ", "
      + (endX) + " " + (endHeight - height / 2) + ", "
      + (endX) + " " + (endHeight + height / 2);
    appendAcidPolygon(coil, col, quadPoints, acid_index, protein_n, highlight, preview);

    //console.log("acid", pos, "start", start, startQuad, "end", end, endQuad)

  }
}


function drawJuxtaListByAcid(group, protein_n, ending_acid, thisElementWidth, start, acid_count, startHeight, endHeight, col, highlight, preview) {
  let list = group.append("g")
    .attr("name", "helix");
  let starting_acid = ending_acid - acid_count + 1;


  let size = thisElementWidth * 2
  let height = size > 20 ? 20 : size;
  for (let currentAcid = 0; currentAcid < acid_count; currentAcid++) {
    let acid_index = starting_acid + currentAcid;
    let startX = start + (currentAcid * thisElementWidth);
    let endX = startX + thisElementWidth + 1;

    let points = ""
    if (currentAcid < acid_count - 1) {
      points = (startX) + " " + (startHeight + height / 4) + ", "
        + (startX) + " " + (startHeight - height / 4) + ", "
        + (endX) + " " + (endHeight - height / 4) + ", "
        + (endX) + " " + (endHeight + height / 4);
    } else {
      points = (startX) + " " + (startHeight + height / 2) + ", "
        + (startX) + " " + (startHeight - height / 2) + ", "
        + (endX) + " " + (endHeight);
    }

    appendAcidPolygon(list, col, points, acid_index, protein_n, highlight, preview);
  }
}

function drawJuxtaHelixByAcid(group, protein_n, ending_acid, thisElementWidth, start, acid_count, startHeight, endHeight, col, highlight, preview) {
  let helix = group.append("g")
    .attr("name", "helix");

  let starting_acid = ending_acid - acid_count + 1;


  let size = thisElementWidth * 2
  let height = size > 20 ? 20 : size;

  for (let currentAcid = 0; currentAcid < acid_count; currentAcid++) {
    let acid_index = starting_acid + currentAcid;

    let startX = start + (currentAcid * thisElementWidth) - size / 4 + 1;

    let isInBackground = acid_index % 2 == 0;

    let quadPoints = "";
    if (isInBackground) {
      quadPoints = (startX) + " " + (startHeight - height / 2) + ", "
        + (startX + size / 2) + " " + (startHeight - height / 2) + ", "
        + (startX + size) + " " + (startHeight + height / 2) + ", "
        + (startX + size / 2) + " " + (startHeight + height / 2);
    } else {
      quadPoints = (startX) + " " + (startHeight + height / 2) + ", "
        + (startX + size / 2) + " " + (startHeight + height / 2) + ", "
        + (startX + size) + " " + (startHeight - height / 2) + ", "
        + (startX + size / 2) + " " + (startHeight - height / 2);
    }
    appendAcidPolygon(helix, col, quadPoints, acid_index, protein_n, highlight, preview)
    .attr("opacity", isInBackground ? 0.5 : 1);

  }
}


function calculateCurve(loopEnd, loopBegin, elementWidth, height) {

  //Caluculate specific position in D3 canvas for start and length of curve
  var length = Math.ceil((loopEnd - loopBegin) * elementWidth);
  var startLoop = Math.ceil(loopBegin * elementWidth - 2);

  var Cx = startLoop; // position loops start
  var Cy = height;  // height
  var Dx = Cx + length; // position of loops end
  var Dy = height; // height

  var Jx = Cx + (Dx - Cx) / 2;
  var Jy = Cy + (Dy - Cy) / 2;

  // console.log('Cx,Cy,Dx,Dy,Jx,Jy ',Cx,Cy,Dx,Dy,Jx,Jy);

  var curve = length > 180 ? 5 : 3;
  Jy = Jy - length / curve;
  //so the loops would not overlap the protein glyphs above
  if (Cy - Jy > 50) Jy = Cy - 50;

  var curveData = [[Cx, Cy], [Jx, Jy], [Dx, Dy]];
  var lineGenerator = d3.line().curve(d3.curveNatural);
  return lineGenerator(curveData);
}

function drawNewLoop(group, n, loopBegin, loopEnd, height, color, elementWidth, loopIndex) {
  // console.log("LOOP: ",n,loopBegin,loopEnd,elementWidth);
  var loopCurve = group.append("g");

  var pathData = calculateCurve(loopEnd, loopBegin, elementWidth, height);

  loopCurve.append("path")
    .attr("d", pathData)
    .attr("stroke", color)
    .attr("stroke-width", 3)
    .attr("fill", "none")
    .attr("pointer-events", "visible") // handles click inside element which is not filled (fill:none)
    .on("click", function () {
      if (group !== superposition) {
        highlightLoop(n, loopBegin, loopEnd, loopIndex);
        drawLoopOutline(n, loopBegin, loopEnd, elementWidth, height);
      }
    });
}

function drawLoopOutline(n, loopBegin, loopEnd, elementWidth, height) {
  var outlineLoopCurve = juxtaposition.append("g")
    .attr("class", "loopOutLine");

  var pathData = calculateCurve(loopEnd, loopBegin, elementWidth, height);

  outlineLoopCurve.append("path")
    .attr("d", pathData)
    .attr("stroke", "grey")
    .attr("stroke-width", 10)
    .attr("opacity", 0.3)
    .attr("fill", "none")
    .attr("pointer-events", "visible") // handles click inside element which is not filled (fill:none)
    .on("click", function () {
      this.remove();
      var removeLoop = { start: loopBegin, end: loopEnd };
      highlighted[n].forEach(x => x.start === loopBegin && x.end === loopEnd ? highlighted[n].delete(x) : x);
      redrawD3();
      highlightPV(n);
      // loopInfo(event,loopBegin,loopEnd,n, elementWidth,loopIndex);
      // drawLoopOutline(loopBegin,loopEnd,height,elementWidth);
    });
}

function highlightLoop(n, start, end, loop_index) {

  //This shouldn't happen
  if (highlighted[n] == null) {
    highlighted[n] = new Set();
  }

  var newLoop = { start: start, end: end, index: loop_index };
  highlighted[n].add(newLoop);

  // console.log("Start: " + start + "  End: " + end);
  redrawD3();
  highlightPV(n);
}

function drawFluctation(n, height, length) {
  // var fluctationValues = [];

  var bfactElementWidth = length / atomsFromPdb[n].length;
  let start = 0;
  var bfactPoins = [start + " " + height + ", "];
  var svgHeight = 30;

  let lastAminoAcid = 0;
  let numOfAtomsInAA = 1;
  let totalAAfluctation = 0;

  for (var i = 0; i < atomsFromPdb[n].length; i++) {

    //case of new amino acid
    if (atomsFromPdb[n][i][5] != lastAminoAcid) {
      lastAminoAcid = atomsFromPdb[n][i][5];

      totalAAfluctation += (atomsFromPdb[n][i][10] - lowestBfact) / (highestBfact - lowestBfact);
      thisBfactNorm = totalAAfluctation / numOfAtomsInAA;
      var calcHeight = height - svgHeight * thisBfactNorm - 5;
      if (isNaN(calcHeight)) {
        calcHeight = height;
      }
      if (isNaN(height) || isNaN(start) || isNaN(i) || isNaN(bfactElementWidth) ){
        console.log(height, start, i, bfactElementWidth, length);
      }
      bfactPoins += [start + i * bfactElementWidth * 1.03 + " " + calcHeight + ", "];

      numOfAtomsInAA = 1;
      totalAAfluctation = 0;
    } else {
      numOfAtomsInAA++;
      totalAAfluctation += (atomsFromPdb[n][i][10] - lowestBfact) / (highestBfact - lowestBfact);
    }

  }

  var calcHeight = height - (svgHeight * totalAAfluctation / numOfAtomsInAA) - 5;
  if (isNaN(calcHeight)) calcHeight = height;
  bfactPoins += [start + (atomsFromPdb[n].length - 1) * bfactElementWidth * 1.03 + " " + calcHeight + ", "];

  bfactPoins += [start + atomsFromPdb[n].length * bfactElementWidth * 1.03 + " " + height];
  // console.log(fluctationValues)

  juxtaposition.append("polygon")
    .attr("fill", "#b6b6b6")
    .attr("opacity", 0.5)
    .attr("points", bfactPoins);
}

function drawJuxtaposition(n) {

  juxtaposition.append("text")
    .attr("y", n * 65 + 60)
    .attr("x", 0)
    .attr('text-anchor', 'left')
    .attr("class", "protein-name")
    .text(proteins[n]);

  var radialGradient = juxtaposition.append("defs")
    .append("radialGradient")
    .attr("id", "radial-gradient");

  radialGradient.append("stop")
    .attr("offset", "0%")
    .attr("stop-color", BallColors[n][0]);

  radialGradient.append("stop")
    .attr("offset", "100%")
    .attr("stop-color", "#fff");

  juxtaposition.append("circle")
    .attr("cx", 0 / 2) // well what do you expect when you hardcode xoffset in svg and decide to fuck it
    .attr("cy", n * 65 + 70)
    .attr("r", 5)
    .attr("fill", "url(#radial-gradient)")//BallColors[n][0])
    .on("click", function (d) {
      var chain = Math.round((this.cy.animVal.value - 70) / 65);
      var index = 0;
      viewer.forEach(function (object) {
        if (index === chain) object.setOpacity((BallColors[chain][0] === "green") ? 0 : 1);
        index++;
      });
      // viewer.get(proteins[chain])
      viewer.requestRedraw();
      BallColors[chain] = [BallColors[chain][1], BallColors[chain][0]]
      radialGradient.select("stop").transition().attr("offset", "0%").attr("stop-color", BallColors[n][0]);
      d3.select(this).style("fill", "url(#radial-gradient)");
    });

  var startAA = juxtaZoom[n][0]; //od kt po kt (pole poli) pre kazdu sekvenciu kt tam mam ze od kt po kt poziciu sa to zobrazuje
  var endAA = juxtaZoom[n][1];
  if (juxtaElementSet) {
    juxtaElementWidth = thisElementWidth;
    juxtaElementSet = false;
  }

  var globalI = 0;


  if (n !== reference) {
    juxtaposition.append("line")
      .attr('x1', 0)
      .attr('y1', 70 + n * 65)
      .attr('x2', thisElementWidth * (endAA - startAA))
      .attr('y2', 70 + n * 65)
      .style('stroke', 'lightgray')
      .style('stroke-width', 1)
  }

  var lastHeight = 0;
  var lastSS = 0;
  var initHeight = 70 + n * 65;
  var endHeight = 70 + n * 65;

  drawFluctation(n, initHeight, thisElementWidth * ss[n].length * 0.975);

  // for (let index = 0; index < loops.length; index++) {
  //   drawNewLoop(n,loops[index].start,loops[index].end,initHeight,colorMolecule[n],thisElementWidth,index,5);
  // }
  let loopIndex = 0;
  let loopShift = null;
  var lastLoopIndex = [];

  for (var i = 0; i < ss[n].length - 1; i++) {
    var thisSS = ss[n][i]; // ss[n] - retazec (HHHHEEEECCC..) sheetov coilov a helixov ss[n][i] - jeden item (pr. H)
    var start = i;
    // console.log('start i',start)
    while (ss[n][i + 1] === thisSS) {
      i++;
    }

    if (loopIndex < loops[n].length && i >= loops[n][loopIndex].start) {
      if (loopShift == null) {
        loopShift = loops[n][loopIndex].start - start;
        // loopShift = atomsFromPdb[n][0][1];
      }

      let strt = parseInt(loops[n][loopIndex].start) - parseInt(loopShift);
      let ennd = parseInt(loops[n][loopIndex].end) - parseInt(loopShift);

      //check if there is any nearby starting value
      var isThereCloseVal = false;
      for (var iii = 0; iii < lastLoopIndex.length; iii++) {
        if (Math.abs(lastLoopIndex[iii] - strt) < ss[n].length / 20) {
          isThereCloseVal = true;
          break;
        }
      }
      if (!isThereCloseVal) {
        lastLoopIndex.push(strt);
        juxtaposition.append("text")
          .attr("y", n * 65 + 90)
          .attr("x", strt * thisElementWidth + 0 / 2) // well fuck, xoffset
          .attr('text-anchor', 'left')
          // .attr("class", "myLabel")
          .attr("fill", colorMolecule[n])
          .style("font-size", "12px")
          // .style("stroke-width", 0.5)
          .text(strt);
      }

      //check if there is any nearby ending value
      isThereCloseVal = false;
      for (var jjj = 0; jjj < lastLoopIndex.length; jjj++) {
        if (Math.abs(lastLoopIndex[jjj] - ennd) < ss[n].length / 20) {
          isThereCloseVal = true;
          break;
        }
      }
      if (!isThereCloseVal) {
        lastLoopIndex.push(ennd);
        juxtaposition.append("text")
          .attr("y", n * 65 + 90)
          .attr("x", ennd * thisElementWidth + 0 / 2) //well fuck, xoffset
          .attr('text-anchor', 'left')
          // .attr("class", "myLabel")
          .attr("fill", colorMolecule[n])
          .style("font-size", "12px")
          // .style("stroke-width", 0.5)
          .text(ennd);
      }

      drawNewLoop(juxtaposition, n, strt, ennd, initHeight, colorMolecule[n], thisElementWidth, loopIndex);
      loopIndex++;
    }
    var gap = i - start + 1;
    // n - index of sequence
    // i - position in the sequence
    // globalI - position in the D3.js canvas
    // gap - number of residues in the ss

    var colors = colorMolecule[n]; //"#66FF00"
    // if (highlighted[n] != null) {
    //   highlighted[n].forEach(function callback(loopI) {
    //     //change colour if it is part of highlighted loop
    //     if ((start >= loopI.start - 1 && start < loopI.end) || (i >= loopI.start - 1 && start < i.end)){
    //       colors = "#66FF00";
    //     }
    //   });
    // }


    if (thisSS === 'E') drawJuxtaListByAcid(juxtaposition, n, i, thisElementWidth, globalI, gap, initHeight, endHeight, colors, highlight = true, preview = true);
    if (thisSS === 'H') drawJuxtaHelixByAcid(juxtaposition, n, i, thisElementWidth, globalI, gap, initHeight, endHeight, colors, highlight = true, preview = true);
    if (thisSS === '-') drawJuxtaCoilByAcid(juxtaposition, n, i, thisElementWidth, globalI, gap, initHeight, endHeight, "lightgray", highlight = false, preview = false);
    if (thisSS === 'C') drawJuxtaCoilByAcid(juxtaposition, n, i, thisElementWidth, globalI, gap, initHeight, endHeight, colors, highlight = true, preview = true);

    globalI += Math.round(gap * thisElementWidth);
  }

  if (highlighted[n] != null) {
    highlighted[n].forEach(function callback(i) {
      drawLoopOutline(n, i.start, i.end, thisElementWidth, initHeight);
    });

    // console.log(highlighted[n].length);
    // console.log(highlighted[n].get(0)[0] + "   " + highlighted[n].get(0)[1])
    // for (var i = 0; i < highlighted[n].length; i++){
    //   drawLoopOutline(n, highlighted[n].get(i)[0], highlighted[n].get(i)[1], thisElementWidth);
    // }
  }

}


function juxtaSelected(selected) {
  juxtapreviewing = selected;
  redrawD3();
}

function juxtaCanceled(selected){
  juxtapreviewing = null;
  redrawD3();
}