/*
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
 this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

solrAdminApp.controller('CloudController',
    function($scope, $location, Zookeeper, Constants) {

        $scope.showDebug = false;

        $scope.$on("cloud-dump", function(event) {
            $scope.showDebug = true;
        });

        $scope.closeDebug = function() {
            $scope.showDebug = false;
        }

        var view = $location.search().view ? $location.search().view : "graph";
        if (view == "tree") {
            $scope.resetMenu("cloud-tree", Constants.IS_ROOT_PAGE);
            treeSubController($scope, Zookeeper);
        } else if (view == "rgraph") {
            $scope.resetMenu("cloud-rgraph", Constants.IS_ROOT_PAGE);
            graphSubController($scope, Zookeeper, true);
        } else if (view == "graph") {
            $scope.resetMenu("cloud-graph", Constants.IS_ROOT_PAGE);
            graphSubController($scope, Zookeeper, false);
        }
    }
);

var treeSubController = function($scope, Zookeeper) {
    $scope.showTree = true;
    $scope.showGraph = false;
    $scope.tree = {};
    $scope.showData = false;

    $scope.showTreeLink = function(link) {
        var path = decodeURIComponent(link.replace(/.*[\\?&]path=([^&#]*).*/, "$1"));
        Zookeeper.detail({path: path}, function(data) {
            $scope.znode = data.znode;
            var path = data.znode.path.split( '.' );
            if(path.length >1) {
              $scope.lang = path.pop();
            } else {
              var lastPathElement = data.znode.path.split( '/' ).pop();
              if (lastPathElement == "managed-schema") {
                  $scope.lang = "xml";
              }
            }
            $scope.showData = true;
        });
    };

    $scope.hideData = function() {
        $scope.showData = false;
    };

    $scope.initTree = function() {
      Zookeeper.simple(function(data) {
        $scope.tree = data.tree;
      });
    };

    $scope.initTree();
};

var graphSubController = function ($scope, Zookeeper, isRadial) {
    $scope.showTree = false;
    $scope.showGraph = true;

    $scope.filterType = "status";

    $scope.helperData = {
        protocol: [],
        host: [],
        hostname: [],
        port: [],
        pathname: []
    };

    $scope.next = function() {
        $scope.pos += $scope.rows;
        $scope.initGraph();
    }

    $scope.previous = function() {
        $scope.pos = Math.max(0, $scope.pos - $scope.rows);
        $scope.initGraph();
    }

    $scope.resetGraph = function() {
        $scope.pos = 0;
        $scope.initGraph();
    }

    $scope.initGraph = function() {
        Zookeeper.liveNodes(function (data) {
            var live_nodes = {};
            for (var c in data.tree[0].children) {
                live_nodes[data.tree[0].children[c].data.title] = true;
            }

            var params = {view: "graph"};
            if ($scope.rows) {
                params.start = $scope.pos;
                params.rows = $scope.rows;
            }

            var filter = ($scope.filterType=='status') ? $scope.pagingStatusFilter : $scope.pagingFilter;

            if (filter) {
                params.filterType = $scope.filterType;
                params.filter = filter;
            }

            Zookeeper.clusterState(params, function (data) {
                    eval("var state=" + data.znode.data); // @todo fix horrid means to parse JSON

                    var leaf_count = 0;
                    var graph_data = {
                        name: null,
                        children: []
                    };

                    for (var c in state) {
                        var shards = [];
                        for (var s in state[c].shards) {
                            var shard_status = state[c].shards[s].state;
                            shard_status = shard_status == 'inactive' ? 'shard-inactive' : shard_status;
                            var nodes = [];
                            for (var n in state[c].shards[s].replicas) {
                                leaf_count++;
                                var replica = state[c].shards[s].replicas[n]

                                var uri = replica.base_url;
                                var parts = uri.match(/^(\w+:)\/\/(([\w\d\.-]+)(:(\d+))?)(.+)$/);
                                var uri_parts = {
                                    protocol: parts[1],
                                    host: parts[2],
                                    hostname: parts[3],
                                    port: parseInt(parts[5] || 80, 10),
                                    pathname: parts[6]
                                };

                                $scope.helperData.protocol.push(uri_parts.protocol);
                                $scope.helperData.host.push(uri_parts.host);
                                $scope.helperData.hostname.push(uri_parts.hostname);
                                $scope.helperData.port.push(uri_parts.port);
                                $scope.helperData.pathname.push(uri_parts.pathname);

                                var replica_status = replica.state;

                                if (!live_nodes[replica.node_name]) {
                                    replica_status = 'gone';
                                } else if(shard_status=='shard-inactive') {
                                    replica_status += ' ' + shard_status;
                                }

                                var node = {
                                    name: uri,
                                    data: {
                                        type: 'node',
                                        state: replica_status,
                                        leader: 'true' === replica.leader,
                                        uri: uri_parts
                                    }
                                };
                                nodes.push(node);
                            }

                            var shard = {
                                name: shard_status == "shard-inactive" ? s + ' (inactive)' : s,
                                data: {
                                    type: 'shard',
                                    state: shard_status
                                },
                                children: nodes
                            };
                            shards.push(shard);
                        }

                        var collection = {
                            name: c,
                            data: {
                                type: 'collection'
                            },
                            children: shards
                        };
                        graph_data.children.push(collection);
                    }

                    $scope.helperData.protocol = $.unique($scope.helperData.protocol);
                    $scope.helperData.host = $.unique($scope.helperData.host);
                    $scope.helperData.hostname = $.unique($scope.helperData.hostname);
                    $scope.helperData.port = $.unique($scope.helperData.port);
                    $scope.helperData.pathname = $.unique($scope.helperData.pathname);

                    if (!isRadial && data.znode && data.znode.paging) {
                        $scope.showPaging = true;

                        var parr = data.znode.paging.split('|');
                        if (parr.length < 3) {
                            $scope.showPaging = false;
                        } else {
                            $scope.start = Math.max(parseInt(parr[0]), 0);
                            $scope.prevEnabled = ($scope.start > 0);
                            $scope.rows = parseInt(parr[1]);
                            $scope.total = parseInt(parr[2]);

                            if ($scope.rows == -1) {
                                $scope.showPaging = false;
                            } else {
                                var filterType = parr.length > 3 ? parr[3] : '';

                                if (filterType == '' || filterType == 'none') filterType = 'status';

                                +$('#cloudGraphPagingFilterType').val(filterType);

                                var filter = parr.length > 4 ? parr[4] : '';
                                var page = Math.floor($scope.start / $scope.rows) + 1;
                                var pages = Math.ceil($scope.total / $scope.rows);
                                $scope.last = Math.min($scope.start + $scope.rows, $scope.total);
                                $scope.nextEnabled = ($scope.last < $scope.total);
                            }
                        }
                    }
                    else {
                        $scope.showPaging = false;
                    }
                    $scope.graphData = graph_data;
                    $scope.leafCount = leaf_count;
                    $scope.isRadial = isRadial;
                });
        });
    };

    $scope.initGraph();
};

solrAdminApp.directive('graph', function(Constants) {
    return {
        restrict: 'EA',
        scope: {
            data: "=",
            leafCount: "=",
            helperData: "=",
            isRadial: "="
        },
        link: function (scope, element, attrs) {
            var helper_path_class = function (p) {
                var classes = ['link'];
                classes.push('lvl-' + p.target.depth);

                if (p.target.data && p.target.data.leader) {
                    classes.push('leader');
                }

                if (p.target.data && p.target.data.state) {
                    classes.push(p.target.data.state);
                }

                return classes.join(' ');
            };

            var helper_node_class = function (d) {
                var classes = ['node'];
                classes.push('lvl-' + d.depth);

                if (d.data && d.data.leader) {
                    classes.push('leader');
                }

                if (d.data && d.data.state) {
                    if(!(d.data.type=='shard' && d.data.state=='active')){
                        classes.push(d.data.state);
                    }
                }

                return classes.join(' ');
            };

            var helper_node_text = function (d) {
                if (!d.data || !d.data.uri) {
                    return d.name;
                }

                var name = d.data.uri.hostname;

                if (1 !== scope.helperData.protocol.length) {
                    name = d.data.uri.protocol + '//' + name;
                }

                if (1 !== scope.helperData.port.length) {
                    name += ':' + d.data.uri.port;
                }

                if (1 !== scope.helperData.pathname.length) {
                    name += d.data.uri.pathname;
                }

                return name;
            };

            scope.$watch("data", function(newValue, oldValue) {
                if (newValue) {
                    if (scope.isRadial) {
                        radialGraph(element, scope.data, scope.leafCount);
                    } else {
                        flatGraph(element, scope.data, scope.leafCount);
                    }
                }
            });


            function setNodeNavigationBehavior(node, view){
                node
                .attr('data-href', function (d) {
                    if (d.type == "node"){
                        return getNodeUrl(d, view);
                    }
                    else{
                        return "";
                    }
                })
                .on('click', function(d) {
                    if (d.data.type == "node"){
                        location.href = getNodeUrl(d, view);
                    }
                });
            }

            function getNodeUrl(d, view){
                var url = d.name + Constants.ROOT_URL + "#/~cloud";
                if (view != undefined){
                    url += "?view=" + view;
                }
                return url;
            }

            var flatGraph = function(element, graphData, leafCount) {
                var w = element.width(),
                    h = leafCount * 20;

                var tree = d3.layout.tree().size([h, w - 400]);

                var diagonal = d3.svg.diagonal().projection(function (d) {
                    return [d.y, d.x];
                });

                d3.select('#canvas', element).html('');
                var vis = d3.select('#canvas', element).append('svg')
                    .attr('width', w)
                    .attr('height', h)
                    .append('g')
                    .attr('transform', 'translate(100, 0)');

                var nodes = tree.nodes(graphData);

                var link = vis.selectAll('path.link')
                    .data(tree.links(nodes))
                    .enter().append('path')
                    .attr('class', helper_path_class)
                    .attr('d', diagonal);

                var node = vis.selectAll('g.node')
                    .data(nodes)
                    .enter().append('g')
                    .attr('class', helper_node_class)
                    .attr('transform', function (d) {
                        return 'translate(' + d.y + ',' + d.x + ')';
                    })

                node.append('circle')
                    .attr('r', 4.5);

                node.append('text')
                    .attr('dx', function (d) {
                        return 0 === d.depth ? -8 : 8;
                    })
                    .attr('dy', function (d) {
                        return 5;
                    })
                    .attr('text-anchor', function (d) {
                        return 0 === d.depth ? 'end' : 'start';
                    })                    
                    .text(helper_node_text);

                setNodeNavigationBehavior(node);
            };

            var radialGraph = function(element, graphData, leafCount) {
                var max_val = Math.min(element.width(), $('body').height())
                var r = max_val / 2;

                var cluster = d3.layout.cluster()
                    .size([360, r - 160]);

                var diagonal = d3.svg.diagonal.radial()
                    .projection(function (d) {
                        return [d.y, d.x / 180 * Math.PI];
                    });

                d3.select('#canvas', element).html('');
                var vis = d3.select('#canvas').append('svg')
                    .attr('width', r * 2)
                    .attr('height', r * 2)
                    .append('g')
                    .attr('transform', 'translate(' + r + ',' + r + ')');

                var nodes = cluster.nodes(graphData);

                var link = vis.selectAll('path.link')
                    .data(cluster.links(nodes))
                    .enter().append('path')
                    .attr('class', helper_path_class)
                    .attr('d', diagonal);

                var node = vis.selectAll('g.node')
                    .data(nodes)
                    .enter().append('g')
                    .attr('class', helper_node_class)
                    .attr('transform', function (d) {
                        return 'rotate(' + (d.x - 90) + ')translate(' + d.y + ')';
                    })

                node.append('circle')
                    .attr('r', 4.5);

                node.append('text')
                    .attr('dx', function (d) {
                        return d.x < 180 ? 8 : -8;
                    })
                    .attr('dy', '.31em')
                    .attr('text-anchor', function (d) {
                        return d.x < 180 ? 'start' : 'end';
                    })
                    .attr('transform', function (d) {
                        return d.x < 180 ? null : 'rotate(180)';
                    })
                    .text(helper_node_text);

                setNodeNavigationBehavior(node, "rgraph");
            }
        }
    };
})

/*

========================
var init_debug = function( cloud_element )
{
  var debug_element = $( '#debug', cloud_element );
  var debug_button = $( '#menu #cloud .dump a' );

  var clipboard_element = $( '.clipboard', debug_element );
  var clipboard_button = $( 'a', clipboard_element );

  $( '.clipboard', debug_element )
    .die( 'click' )
    .live
    (
      'click',
      function( event )
      {
        return false;
      }
    );

            url : app.config.solr_path + '/zookeeper?wt=json&dump=true',
              ZeroClipboard.setMoviePath( 'img/ZeroClipboard.swf' );

              clipboard_client = new ZeroClipboard.Client();

              clipboard_client.addEventListener
              (
                'load',
                function( client )
                {
                }
              );

              clipboard_client.addEventListener
              (
                'complete',
                function( client, text )
                {
                  clipboard_element
                    .addClass( 'copied' );

                  clipboard_button
                    .data( 'text', clipboard_button.text() )
                    .text( clipboard_button.data( 'copied' ) );
                }
              );
            },
            success : function( response, text_status, xhr )
            {
              clipboard_client.glue
              (
                clipboard_element.get(0),
                clipboard_button.get(0)
              );

              clipboard_client.setText( response.replace( /\\/g, '\\\\' ) );

              $( '.debug', debug_element )
                .removeClass( 'loader' )
                .text( response );
            },
            error : function( xhr, text_status, error_thrown )
            {
            },
            complete : function( xhr, text_status )
            {
            }
          }
        );
      }
    )
    .die( 'hide' )
    .live
    (
      'hide',
      function( event )
      {
        $( '.debug', debug_element )
          .empty();

        clipboard_element
          .removeClass( 'copied' );

        clipboard_button
          .data( 'copied', clipboard_button.text() )
          .text( clipboard_button.data( 'text' ) );

        clipboard_client.destroy();

        debug_element.hide();
      }
    );
};

*/
