/* KUTE.js SVG Morph utilities */
  var element = document.getElementById('path1'),
    svg = element.ownerSVGElement,
    input1 = document.getElementById('input1'),
    input2 = document.getElementById('input2'),
    // define the tween object, but only as undefined    
    tween,
    viewbox = document.getElementById('viewbox'),
    reverse1 = document.getElementById('reverse1'),
    reverse2 = document.getElementById('reverse2'),
    index = document.getElementById('index'),
    precision = document.getElementById('precision'),
    idx, log = document.querySelector('.log'),
    ns = 'http://www.w3.org/2000/svg',
    pathReg = /(m[^(h|v|l)]*|[vhl][^(v|h|l|z)]*)/gmi,
    clone = function(a) {
      var copy;
      if (a instanceof Array) {
        copy = [];
        for (var i = 0, len = a.length; i < len; i++) {
          copy[i] = clone(a[i]);
        }
        return copy;
      }
      return a;
    },
    getOnePath = function(p){ return p.split(/z/i).shift() + 'z'; },
    createPath = function (p){ // createPath
      var c = document.createElementNS(ns,'path'), d = typeof p === 'object' ? p.getAttribute('d') : p; 
      c.setAttribute('d',d); return c;
    },
    getSegments = function(s,e,r){ // getSegments returns an array of points based on a sample size morphPrecision
      var s1 = [], e1 = [], le1 = s.getTotalLength(), le2 = e.getTotalLength(), ml = Math.max(le1,le2),
        d = r, ar = ml / r, j = 0, sl = ar*r; // sl = sample length

      while ( (j += r) < sl ) { // populate the points arrays based on morphPrecision as sample size
        s1.push( [s.getPointAtLength(j).x, s.getPointAtLength(j).y]);
        e1.push( [e.getPointAtLength(j).x, e.getPointAtLength(j).y]);
      }
      return [s1,e1];
    },
    getClosestPoint = function(p,t,s){
      var x, y, a = [], l = s.length, dx, nx, pr;
      for (i=0; i<l; i++){
        x = Math.abs(s[i][0] - t.x);
        y = Math.abs(s[i][1] - t.y);
        a.push( Math.sqrt( x * x + y * y ) );
      }
      dx = a.indexOf(Math.min.apply(null,a));
      pr = !!s[dx-1] ? dx-1 : l-1;
      nx = !!s[dx+1] ? dx+1 : 0;
      return Math.abs(s[pr][0] - t.x) < p && Math.abs(s[pr][1] - t.y) < p ? s[pr]
      : Math.abs(s[nx][0] - t.x) < p && Math.abs(s[nx][1] - t.y) < p ? s[nx] 
      : Math.abs(s[dx][0] - t.x) < p && Math.abs(s[dx][1] - t.y) < p ? s[dx] 
      : [t.x,t.y];
    },
    pathToAbsolute = function(p) {
      var np = p.match(pathReg), wp = [], l = np.length, s, c, r, x = 0, y = 0;
      for (var i = 0; i<l; i++){
        np[i] = np[i]; c = np[i][0]; r = new RegExp(c+'[^\\d|\\-]*','i'); 
        np[i] = np[i].replace(/(^|[^,])\s*-/g, '$1,-').replace(/(\s+\,|\s|\,)/g,',').replace(r,'').split(',');
        np[i][0] = parseFloat(np[i][0]);
        np[i][1] = parseFloat(np[i][1]);
        if (i === 0) { x+=np[i][0]; y +=np[i][1]; }
        else {
          x = np[i-1][0]; 
          y = np[i-1][1]; 
          if (/l/i.test(c)) {
            np[i][0] = c === 'l' ? np[i][0] + x : np[i][0];
            np[i][1] = c === 'l' ? np[i][1] + y : np[i][1];  
          } else if (/h/i.test(c)) {
            np[i][0] = c === 'h' ? np[i][0] + x : np[i][0];
            np[i][1] = y;  
          } else if (/v/i.test(c)) {
            np[i][0] = x;
            np[i][1] = c === 'v' ? np[i][0] + y : np[i][0];
          }
        }
      }
      return np;
    },
    getBestIndex = function(s,e){ // getBestIndex for shape rotation
      var s1 = clone(s), e1 = clone(e), d = [], i, l = e.length, t, ax, ay;
      for (i=0; i<l; i++){
        t = e1.splice(i,l-i); e1 = t.concat(e1);
        ax = Math.abs(s1[i][0] - e1[i][0]);
        ay = Math.abs(s1[i][1] - e1[i][1]);
        d.push( Math.sqrt( ax * ax + ay * ay ) );
        e1 = []; e1 = clone(e); t = null;
      }
      return d.indexOf(Math.min.apply(null,d));
    },
    startingPoints = [],
    showStartingPoints = function(s,e,isp){ // showPoints helper function to visualize the points on the path
      var c, a = arguments, cl, p, l;
      for (var i=0; i<2; i++){
        p = a[i]; l = p.length; cl = i=== 0 ? { f: 'orangered', o: 'orange' } : { f: 'Lime', o: 'LimeGreen' };
        for (var j=0; j<l; j++) {
          c = document.createElementNS(ns,'circle');
          c.setAttribute('cx',p[j][0]); c.setAttribute('cy',p[j][1]);
          c.setAttribute('r', j===0 ? 20 : 10 ); c.setAttribute('fill', j===0 ? cl.f : cl.o);
          if (isp) { svg.appendChild(c); } else if (!isp && j===0 ) { svg.appendChild(c);}
          startingPoints.push(c);
        }
      }
      c = null;
    },
    removeStartingPoints = function(){
      for (var i=0,l = startingPoints.length; i<l; i++ ) { startingPoints[i].parentNode === svg && svg.removeChild(startingPoints[i]); } startingPoints = [];
    },
    showInfo = function(s1,e1){
      var isPolygon = !/[CSQTA]/i.test(input1.value) && !/[CSQTA]/i.test(input2.value),
          mpi = isPolygon ? 'the polygon with the most points.<br>' : (parseInt(precision.value) === 15 ? 'the default' : 'your') +' <i class="text-pink">morphPrecision</i> value of <b>'+precision.value+'</b>.<br>',
          theLog = '<b>KUTE.js Path Morph Log</b><br>The morph used <b>' + s1.length + '</b> points to draw both paths based on ' + mpi
        + (index.value ? 'You\'ve configured the <i class="text-blue">morphIndex</i> to ' + index.value + ' while the recommended is <b>' + idx + '</b>. You can then increase/decrease this value till you get the perfect morph animation.<br>' : 'You may also consider a <i class="text-blue">morphIndex</i> for your morph. Currently the best index seems to be around <b>' + idx + '</b> value.<br>')
        + (!reverse1.checked && !reverse2.checked ? 'If the current animation is still not satisfactory after using a <i class="text-blue">morphIndex</i> value, consider reversing one of the paths.<br>Maybe the paths do not intersect or they really have different draw directions.' : 'You\'ve chosen that the first path to have ' + ( reverse1.checked  ? '<i class="text-orange">reversed</i> draw direction, ' : 'unchanged draw direction, ') + 'while second path is to be ' + (reverse2.checked ? '<i class="text-lime">reversed</i>.<br>' : 'unchanged.<br>') )
        + (reverse1.checked && reverse2.checked ? '<b>Remember</b>: when both shapes are reversed the recommended <i class="text-blue">morphIndex</i> is actually <b>' + (s1.length - idx)+'</b>.' : '' );
      
      showStartingPoints(s1,e1,isPolygon);
      
      log.innerHTML = theLog;      
    },
    pathCross = function(s,e,svg){ // pathCross
      var s1, e1, arr, arL, sm, lg, smp, lgp, nsm = [], sml, cl = [], len, tl, cs,
          isPolygon = !/[CSQTA]/i.test(input1.value) && !/[CSQTA]/i.test(input2.value);

      if (!isPolygon) {
        s = createPath(s); e = createPath(e);  
        arr = getSegments(s,e,parseInt(precision.value)); 
        s1 = arr[0]; e1 = arr[1]; arL = e1.length;
      } else {
        s = pathToAbsolute(s); e = pathToAbsolute(e);

        if ( s.length !== e.length ){
          arL = Math.max(s.length,e.length);
          if ( arL === e.length) { sm = s; lg = e; } else { sm = e; lg = s; }
          sml = sm.length;

          smp = createPath('M'+sm.join('L')+'z'); len = smp.getTotalLength() / arL;
          for (var i=0; i<arL; i++){
            tl = smp.getPointAtLength(len*i);
            cs = getClosestPoint(len,tl,sm);
            nsm.push( [ cs[0], cs[1] ] );
          }

          if (arL === e.length) { e1 = lg; s1 = nsm; } else { s1 = lg; e1 = nsm; }
        } else {
          s1 = s; e1 = e;
        }
      }

      // determine index for best/minimum distance between points
      idx = getBestIndex(s1,e1);
      console.log(idx);

      // shift second array to for smallest tween distance
      if (index.value) {
        var e11 = e1.splice(parseInt(index.value),arL-parseInt(index.value));
        e1 = e11.concat(e1);
      }
      
      showInfo(s1,e1);

      s = e = null;
    };

// get the inputs
function init(){
  svg.setAttribute('viewBox',viewbox.value);
  removeStartingPoints();
  element.setAttribute('d',getOnePath(input1.value));
  // showInfo
  paths = pathCross(getOnePath(input1.value),getOnePath(input2.value),element.ownerSVGElement);
  // write a simple tween object
  tween = KUTE.fromTo('#path1',  // target shape
   { path: input1.value, attr : { fill: 'orangered' } }, // from shape
   { path: input2.value, attr : { fill: 'lime' } }, // to shape
   { // options
    easing: 'easingCubicOut', 
    yoyo: true, repeat: 1, duration: 5000,
    morphIndex: parseInt(index.value),
    morphPrecision: parseInt(precision.value),
    reverseFirstPath: reverse1.checked || false,
    reverseSecondPath: reverse2.checked || false
  });
}
input1.value && input2.value && init();

// trigger it whenever you want
document.getElementById('wrapper').onclick = function() {
  input1.value && input2.value && init();
  tween.start();
}