;(function(undefined) {
  'use strict';

  if (typeof sigma === 'undefined')
    throw 'sigma is not declared';

  if (typeof ShapeLibrary === 'undefined')
    throw 'ShapeLibrary is not declared';


  // Initialize package:
  sigma.utils.pkg('sigma.canvas.nodes');
  sigma.utils.pkg('sigma.svg.nodes');

  var sigInst = undefined;
  var imgCache = {};

  var initPlugin = function(inst) {
    sigInst = inst;
  }

  var drawImage = function (node,x,y,size,context) {
    if(sigInst && node.image && node.image.url) {
      var url = node.image.url;
      var ih = node.image.h || 1; // 1 is arbitrary, anyway only the ratio counts
      var iw = node.image.w || 1;
      var scale = node.image.scale || 1;
      var clip = node.image.clip || 1;

      // create new IMG or get from imgCache
      var image = imgCache[url];
      if(!image) {
        image = document.createElement('IMG');
        image.src = url;
        image.status = 'loading';
        image.onerror = function() {
          console.log("error loading", url);
          image.status = 'error';
        };
        image.onload = function(){
          // TODO see how we redraw on load
          // need to provide the siginst as a parameter to the library
          console.log("redraw on image load", url);
          image.status = 'ok';
          sigInst.refresh();
        };
        imgCache[url] = image;
      }

      // calculate position and draw
      var xratio = (iw<ih) ? (iw/ih) : 1;
      var yratio = (ih<iw) ? (ih/iw) : 1;
      var r = size*scale;

      // Draw the clipping disc:
      context.save(); // enter clipping mode
      context.beginPath();
      context.arc(x,y,size*clip,0,Math.PI*2,true);
      context.closePath();
      context.clip();

      if(image.status === 'ok') {
        // Draw the actual image
        context.drawImage(image,
            x+Math.sin(-3.142/4)*r*xratio,
            y-Math.cos(-3.142/4)*r*yratio,
            r*xratio*2*Math.sin(-3.142/4)*(-1),
            r*yratio*2*Math.cos(-3.142/4));
      }
      context.restore(); // exit clipping mode
    }
  }

  var drawSVGImage = function (node, group, settings) {
    if(sigInst && node.image && node.image.url) {
      var clipCircle = document.createElementNS(settings('xmlns'), 'circle'),
        clipPath = document.createElementNS(settings('xmlns'), 'clipPath'),
        clipPathId = settings('classPrefix') + '-clip-path-' + node.id,
        def = document.createElementNS(settings('xmlns'), 'defs'),
        image = document.createElementNS(settings('xmlns'), 'image'),
        url = node.image.url;

      clipPath.setAttributeNS(null, 'id', clipPathId);
      clipPath.appendChild(clipCircle);
      def.appendChild(clipPath);

      // angular's base tag will change the relative fragment id, so
      // #<clipPathId> doesn't work
      // HACKHACK: IE <=9 does not respect the HTML base element in SVG.
      // They don't need the current URL in the clip path reference.
      var absolutePath = /MSIE [5-9]/.test(navigator.userAgent) ?
        "" : document.location.href;
      // To fix cases where an anchor tag was used
      absolutePath = absolutePath.split("#")[0];
      image.setAttributeNS(null, 'class',
        settings('classPrefix') + '-node-image');
      image.setAttributeNS(null, 'clip-path',
        'url(' + absolutePath + '#' + clipPathId + ')');
      image.setAttributeNS(null, 'pointer-events', 'none');
      image.setAttributeNS('http://www.w3.org/1999/xlink', 'href',
        node.image.url);
      group.appendChild(def);
      group.appendChild(image);
    }
  }

  var register = function(name,drawShape,drawBorder) {
    sigma.canvas.nodes[name] = function(node, context, settings) {
      var args = arguments,
          prefix = settings('prefix') || '',
          size = node[prefix + 'size'],
          color = node.color || settings('defaultNodeColor'),
          borderColor = node.borderColor || color,
          x = node[prefix + 'x'],
          y = node[prefix + 'y'];

      context.save();

      if(drawShape) {
        drawShape(node,x,y,size,color,context);
      }

      if(drawBorder) {
        drawBorder(node,x,y,size,borderColor,context);
      }

      drawImage(node,x,y,size,context);

      context.restore();
    };

    sigma.svg.nodes[name] = {
      create: function(node, settings) {
        var group = document.createElementNS(settings('xmlns'), 'g'),
        circle = document.createElementNS(settings('xmlns'), 'circle');

        group.setAttributeNS(null, 'class',
          settings('classPrefix') + '-node-group');
        group.setAttributeNS(null, 'data-node-id', node.id);
        // Defining the node's circle
        circle.setAttributeNS(null, 'data-node-id', node.id);
        circle.setAttributeNS(null, 'class',
          settings('classPrefix') + '-node');
        circle.setAttributeNS(null, 'fill',
          node.color || settings('defaultNodeColor'));

        group.appendChild(circle);
        drawSVGImage(node, group, settings);
        return group;
      },
      update: function(node, group, settings) {
        var classPrefix = settings('classPrefix'),
          clip = node.image.clip || 1,
          // 1 is arbitrary, anyway only the ratio counts
          ih = node.image.h || 1,
          iw = node.image.w || 1,
          prefix = settings('prefix') || '',
          scale = node.image.scale || 1,
          size = node[prefix + 'size'],
          x = node[prefix + 'x'],
          y = node[prefix + 'y'];

          var r = scale * size,
          xratio = (iw<ih) ? (iw/ih) : 1,
          yratio = (ih<iw) ? (ih/iw) : 1;

        for(var i = 0, childNodes = group.childNodes; i < childNodes.length; i ++) {
          var className = childNodes[i].getAttribute('class');

          switch (className) {
            case classPrefix + '-node':
              childNodes[i].setAttributeNS(null, 'cx', x);
              childNodes[i].setAttributeNS(null, 'cy', y);
              childNodes[i].setAttributeNS(null, 'r', size);

              // // Updating only if not freestyle
              if (!settings('freeStyle')) {
                childNodes[i].setAttributeNS(
                  null,
                  'fill',
                  node.color || settings('defaultNodeColor'));
              }
              break;
            case classPrefix + '-node-image':
              childNodes[i].setAttributeNS(null, 'x',
                x+Math.sin(-3.142/4)*r*xratio);
              childNodes[i].setAttributeNS(null, 'y',
                y-Math.cos(-3.142/4)*r*yratio);
              childNodes[i].setAttributeNS(null, 'width',
                r*xratio*2*Math.sin(-3.142/4)*(-1));
              childNodes[i].setAttributeNS(null, 'height',
                r*yratio*2*Math.cos(-3.142/4));
              break;
            default:
              // no class name, must be the clip-path
              var clipPath = childNodes[i].firstChild;
              if (clipPath != null) {
                var clipPathId = classPrefix + '-clip-path-' + node.id;
                if (clipPath.getAttribute('id') === clipPathId) {
                  clipPath.firstChild.setAttributeNS(null, 'cx', x);
                  clipPath.firstChild.setAttributeNS(null, 'cy', y);
                  clipPath.firstChild.setAttributeNS(null, 'r',
                    clip * size);
                }
              }
              break;
          }
        }

        // showing
        group.style.display = '';
      }
    }
  }

  ShapeLibrary.enumerate().forEach(function(shape) {
    register(shape.name,shape.drawShape,shape.drawBorder);
  });

  /**
   * Exporting
   * ----------
   */
  this.CustomShapes = {

    // Functions
    init: initPlugin,
    // add pre-cache images

    // Version
    version: '0.1'
  };



}).call(this);
