(function () {
  'use strict';

  angular.module('uql.orgs')
      .controller('uqlOrgsAsjcCodesCloudCtrl', ['$scope', function ($scope) {
        $scope.expectedViz = 'asjc-codes-cloud';
        $scope.filename = 'report-' + $scope.expectedViz + '.csv';

        var maxTags = 50, maxTagChars = 20;
        /**
         * Take the data returned from our json call and format it
         * suitably for output:
         *
         * var output = [
         *  {
         *    text: "Noam Chomsky",
         *    size: 122
         *  },
         *  etc...
         * ];
         *
         * @param {Object[]} jsonData
         * @return {Array[]}
         */
        $scope.formatData = function (jsonData) {
          var returnVal = [];
          var lookup = {};

          for (var i = 0, l = jsonData.length; i < l; i++) {
            var row = jsonData[i];
            if (lookup.hasOwnProperty(row.asjc_value)) {
              lookup[row.asjc_value] += parseInt(row.papers, 10);
            } else {
              lookup[row.asjc_value] = parseInt(row.papers, 10);
            }
          }

          for (var el in lookup) {
            if (lookup.hasOwnProperty(el)) {
              if (returnVal.length >= maxTags) {
                break;
              }
              returnVal.push({text: shorten(el), size: parseInt(lookup[el], 10)});
            }
          }

          return returnVal;
        };

        /**
         * Format the objects into a suitable form including fixing datatypes etc
         *
         * @param {Object[]} data
         */
        $scope.formatTableData = function (data) {
          var returnVal = [
            [
              {type: 'string', name: 'ASJC Code'},
              {type: 'string', name: 'Description'},
              {type: 'string', name: 'Pub Year'},
              {type: 'number', name: 'Papers'},
              {type: 'number', name: 'Citations'},
              {type: 'number', name: 'Cit Avg'},
              {type: 'number', name: 'World Avg'}
            ]
          ];
          for (var i = 0, l = data.length; i < l; i++) {
            var row = data[i];
            var papers = parseInt(row.papers, 10);
            var citations = parseInt(row.citations, 10);
            var worldPapers = parseInt(row.world_papers, 10);
            var worldCitations = parseInt(row.world_citations, 10);
            var newRow = [
              row.asjc_code + '',
              row.asjc_value + '',
              row.pub_year + '',
              papers,
              citations,
              citations ? parseFloat((citations / papers).toFixed(2)) : 0,
              worldCitations ? parseFloat((worldCitations / worldPapers).toFixed(2)) : 0
            ];
            returnVal.push(newRow);
          }
          return returnVal;
        };

        /**
         * Words which are too long are less likely to be displayed,
         * the D3 cloud code just discards them if it can't find a place for them,
         * so shorten them to an arbitrary length
         *
         * @param word
         * @returns {*}
         */
        function shorten(word) {
          if (word.length > maxTagChars) {
            return word.substr(0, maxTagChars) + '..';
          } else {
            return word;
          }
        }
      }])
      .directive('uqlOrgsAsjcCodesCloud', ['$compile', function ($compile) {
        return {
          restrict: 'E',
          replace: true,
          scope: {
            viz: '=',
            jsonData: '=data'
          },
          controller: 'uqlOrgsAsjcCodesCloudCtrl',
          template: '<div></div>',
          link: function postLink(scope, element, attrs) {
            var canvas;
            var fill = d3.scale.category20b();
            var cloud = d3.layout.cloud();
            var minFontSize = 13, maxFontSize = 50;
            var height = attrs.height || 480;
            var width = attrs.width || 640;

            /**
             * Call back function to draw the tags
             *
             * Uses globals height and width
             *
             * @param {Object[]} tags
             */
            function draw(tags) {
              // this specifies the center around which elements will be laid
              canvas.attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')');

              // get all the existing text elements
              var text = canvas.selectAll('text')
                  .data(tags, function (d) {
                    return d.text;
                  });

              // any text we don't have, create a new element for
              text.enter().append('text')
                  .style('opacity', 0)
                  .style('font-size', function (d) {
                    return d.size + 'px';
                  })
                  .style('font-family', 'Impact')
                  .attr('text-anchor', 'middle')
                  .attr('transform', function (d) {
                    return 'translate(' + [d.x, d.y] + ')rotate(' + d.rotate + ')';
                  })
                  .style('fill', function (d) {
                    return fill(d.text);
                  })
                  .on('click', function (d) {
                    return scope.clickfn(d);
                  })
                  .text(function (d) {
                    return d.text;
                  })
                  .transition().duration(1000).style('opacity', 1)
              ;

              // any no longer existing text we remove
              text.exit().transition()
                  .duration(1000)
                  .style('opacity', 0)
                  .remove();
            }

            /**
             * Take the data and setup the cloud
             *
             * @param {Object[]} data
             * @param {int} minFontSize
             * @param {int} maxFontSize
             */
            function showCloud(data, minFontSize, maxFontSize) {
              var valuesRange = d3.extent(data, function (d) {
                return d.size;
              });

              var fontScale = d3.scale.linear().domain(valuesRange).range([minFontSize, maxFontSize]);

              cloud.fontSize(function (d) {
                return fontScale(d.size);
              })
                  .words(data)
                  .start();
            }

            /**
             * Make the enclosing div and svg objects
             *
             * @param {number} height
             * @param {number} width
             */
            function createContainers(height, width) {
              d3.select('#chart_div > span').remove();

              // Initialisation functions to make canvas
              d3.select('#chart_div').append('svg')
                  .attr('width', width)
                  .attr('height', height)
                  .attr('viewbox', '0 0 ' + width + ' ' + height)
                  .attr('preserveAspectRatio', 'xMidYMid')
                  .append('g')
                  .attr('id', 'canvas')
              ;
            }

            /**
             * Perform once only initialisation on the cloud
             *
             * Uses global param cloud
             *
             * @param {number} height
             * @param {number} width
             */
            function initCloud(height, width) {
              cloud.size([width, height])
                  .timeInterval(10)
                  .rotate(function () {
                    var angle = 5;
                    // double bitwise not removes anything from right of decimal
                    /*jshint bitwise:false*/
                    return ~~(Math.random() * angle * 2 * 10) - (angle * 10);
                  })
                  .padding(5)
                  .on('end', draw);
            }

            var htmlText = '<div id="chart_div"><span>Information loading...</span></div>';
            htmlText += '<uql-table filename="{{filename}}" data="tableData"></uql-table>';
            element.html('<span>' + htmlText + '</span>');
            $compile(element.contents())(scope);

            // do some init work
            createContainers(height, width);
            initCloud(height, width);
            canvas = d3.select('#canvas');

            // go...
            scope.$watch('jsonData', function (jsonData) {
              if ((scope.viz.name === scope.expectedViz) && angular.isArray(jsonData)) {
                var formattedData = scope.formatData(jsonData);
                showCloud(formattedData, minFontSize, maxFontSize);
                scope.tableData = scope.formatTableData(jsonData);
              }
            });
          }
        };
      }]);
})();
