"use strict";

var patterns = {

  'gospelgun': {
    x: 36,
    rle: "24bo11b22bobo11b12b2o6b2o12b2o11bo3bo4b2o12b2o2o8bo5bo3b2o14b2o8bo3bob2o4bobo11b10bo5bo7bo11b11bo3bo20b12b2o22b"
},

  'diamond': {

    x: 12,
    rle: '4b4o4b12b2b8o2b12b12o12b2b8o2b12b4b4o4b'

  },

  'flammenkamp': {
    x: 13,
    rle: '7b2o4b7bobo3b2bo4bob2o2bb2o5bo4bo2bo9b3o10b23b3o9bo2bo4bo5b2ob2b2obo4bo2b3bobo7b4b2o7b'
  },

  'c2ship': {
    x: 13,
    rle: '1o12b4o9b2b2o9b5b1o7b6o7b6b1o1b1o4bobob6o3bob2o5b2o2b4b2o4b2ob5o4b2o2b13b5o4b2o2b4b2o4b2obob2o5b2o2bobob6o3b6b1o1b1o4b6o7b5b1o7b2b2o9b4o9bo12b'
  } 

};



function curry(fn) {
  return function(...args) {
    if(fn.length > args.length) {
      var arg = args;
      return function(...args) {return fn.apply(null, arg.concat(args));};
    }
    return fn.apply(null, args);
  };
}


function compose(f,g) {
  return function(x) { return f(g(x)); };
}


function pipe(...fns) {
  return fns.reduce(compose);
}

var getEle = curry(function(ele) {
  return (document.querySelectorAll(ele).length === 1) ? document.querySelector(ele) : [...document.querySelectorAll(ele)]; 
});

var map = curry(function(fn,arr) {
   return Array.prototype.map.call(arr, fn);
});

var get = curry(function(what,x) {
   return x[what];
});


var forEach = curry(function(fn,arr) {
   return Array.prototype.forEach.call(arr, fn);

});


var log = function(x) {
   console.log(x); return x;
};


var calcRules = function(ele, index, arr) {

var gridLength = Math.sqrt(arr.length);

var r = Math.floor(index/gridLength);

var neighbours = 
  arr[(index-1+arr.length)%arr.length]+
  arr[(index - gridLength+arr.length)%arr.length]+
  arr[(index + gridLength+arr.length)%arr.length]+
  arr[(index+1+arr.length)%arr.length]+
  arr[(index - gridLength - 1+arr.length)%arr.length]+
  arr[(index - gridLength + 1+arr.length)%arr.length]+
  arr[(index + gridLength - 1+arr.length)%arr.length]+
  arr[(index + gridLength + 1+arr.length)%arr.length];

if((ele === 1 && neighbours < 2) || (ele === 1 && neighbours > 3)) {
      return 0;
}

if((ele === 1 && neighbours === 2) || (ele === 1 && neighbours === 3)) {
      return 1;
}

if(ele === 0 && neighbours === 3) {
      return  1; 
}

else {  
    return ele;   
}

};


var getCanvas = curry(function(type, id) {
  return HTMLCanvasElement.prototype.getContext.call(getEle(id), type);
});



var draw = curry(function(ctx, arr) {
  ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
  forEach((ele, index)=>{
    if(ele === 1) {
      var gridLength = Math.sqrt(arr.length);
      var r = Math.floor(index/gridLength);
      ctx.fillStyle = "#000000";
      ctx.fillRect(index%gridLength*ctx.canvas.width/gridLength+0.5, 
                   r*ctx.canvas.height/gridLength+0.5,
                   ctx.canvas.width/gridLength-0.5, 
                   ctx.canvas.height/gridLength-0.5);
      ctx.fillStyle = "#cccccc";
      ctx.fillRect(index%gridLength*ctx.canvas.width/gridLength+1.0, 
                   r*ctx.canvas.height/gridLength+1.0,
                   ctx.canvas.width/gridLength-1.0, 
                   ctx.canvas.height/gridLength-1.0);
    }

  },arr);

  var fps = setTimeout(()=>{
    clearTimeout(fps);
    newState(arr);

  },1000/60);
});

var decodeRLE = function(str) {
    return str.replace(/(\d+)(\w)/g, 
        function(m,n,c){
            return new Array( parseInt(n,10)+1 ).join(c);
        });
};

var arrayPattern = function(ele) {  return ele==="b"?0:1;  };

var generateGrid = function(initPattern) {
  //var size = 512;
  var size = 256;
  var grid = [];
  while(grid.length < size*size) {
    if(initPattern.length && grid.length%size === (size/2) - Math.ceil(initPattern[0].length/2) 
      && grid.length/size > (size/2) - Math.ceil(initPattern.length/2)) {
      grid.push(...initPattern.shift());
    } else {
       grid.push(0);
    }
  
  }

  return grid;
};

var rleArray = compose(map(arrayPattern), decodeRLE);

var to2dArr = function(pattern) { 
  var arr = rleArray(patterns[pattern].rle);
      var result = []; 
      var chunk = patterns[pattern].x; 
  while(arr.length !== 0) { 
    result.push(arr.splice(0, chunk)); 
  } 
  return result; 
};

var newState = compose(draw(getCanvas('2d', '#board')), map(calcRules));

var setInitGrid = compose(generateGrid, to2dArr);

var render = compose(newState, setInitGrid);

render('gospelgun');
//render('diamond');
//render('flammenkamp');
//render('c2ship');
