// $Id: rrd-monitor.js 1338 2010-03-14 16:03:09Z sdalu $

//
// Copyright (c)  Stephane D'Alu  2008,2010
// http://www.sdalu.com/
//


// HOST
// MISSING

Person = Ext.extend(Object, {
	constructor: function(first, last){
	    this.firstName = first;
	    this.lastName = last;
	},

	getName: function(){
	    return this.firstName + ' ' + this.lastName;
	}
    });


build_url = function(url, opts) {
    opts = opts || {};
    var idx   = url.indexOf('?');
    var param = {};
    if (idx >= 0) {
	url   = url.substring(0, idx);
	param = Ext.urlDecode(url.substring(idx+1), true);
    } 
    Ext.apply(param, opts);
    var empty = true;
    for (p in param) { if (param[p] === null) delete param[p]; }
    for (p in param) { empty = false; break;                   }
    if (!empty)
	url += '?' + Ext.urlEncode(param);
    return url;
};


function duration2str(seconds, format, sep) {
    var fmt  = Ext.apply({ day   : '%2dd', hour  : '%2dh',
                           minute: '%2dm', second: '%2ds' }, format);
    var sec  = Math.floor(Math.abs(seconds));
    var min  = Math.floor(sec  / 60); sec  = sec  % 60;
    var hour = Math.floor(min  / 60); min  = min  % 60;
    var day  = Math.floor(hour / 24); hour = hour % 60;
                                      day  = day  % 24;
    var res  = [];
    if (day)  res.push(sprintf(fmt.day, day));
    if (hour) res.push(sprintf(fmt.hour, hour));
    if (min)  res.push(sprintf(fmt.minute, min));
    if (sec)  res.push(sprintf(fmt.seconde, sec));
    return res.join(sep);
};

Ext.namespace('RRD');

RRD.app = function() { return { 

refresh: [ 1*60, 5*60, 15*60, 30*60, 60*60 ],




init: function(el, height) {

Ext.QuickTips.init();
Ext.QuickTips.enable();

/* Global variables */
var w = { ui:{}, tpl:{}, data:{}, str:{}, action:{} };

// String
w.str.please_select = '<i>Please, select a graph</i>';
w.str.enlarged_view = 'Enlarged view';


w.data.current = {}


var refresher = function() { 
    if (refresher.current) {
	clearTimeout(refresher.current);
	refresher.current = null;
    }
    if (refresher.next) {
	update_rrdgraph({ _ts: (new Date()).getTime() });
	setTimeout(refresher, refresher.next);
    };
}



//== Format ============================================================

w.data.rrd_main = { nolegend: '', notitle: '', width: 250 };
w.data.rrd_zoom = { nolegend: '', notitle: '', transparent: '' };



//== Store =============================================================

var store = new Ext.data.Store({
	proxy    : new FetchDescriptionProxy(),
	reader   : new Ext.data.JsonReader({}, [ 'title', 'id', 'items' ])
    });

store.fetchGraphInfo = function(id) {
    var found = null;
    this.each(function(rec) {
	Ext.each(rec.get('items'), function(item) {
	    if (item.id == id) {
		found = { grptitle: rec.get('title'), graph   : item };
		return false ;
	    }
	  });
	if (found) return false;
      });
    return found;
}


//== Templates =========================================================

w.tpl.entry = new Ext.XTemplate(
    '<tpl for=".">',
      '<div class="entry" hidefocus="on">{title:htmlEncode}</div>',
    '</tpl>' );

w.tpl.graph_view = new Ext.XTemplate(
    '<tpl for=".">',
      '<div class="rrd-group-monitoring">',
        '<h3 class="x-grid-group-hd"><div unselectable="on" class="x-unselectable x-grid-group-title">{title:htmlEncode}</div></h3>',
	'<div class="x-grid-group-body">',
        '<tpl for="items">',
	  '<div class="rrd-graph">',
            '<div class="rrd-caption">',
              '<div unselectable="on" class="rrd-title x-unselectable"><img src="/images/common/icons/16x16/help.png" alt="?" ext:qtip="{title:htmlEncode}"/><span>{title:htmlEncode}</span></div>',
  	      '<table class="rrd-labels">',
	      '<tpl for="labels">',
                '<tr><th style="background-color: {color};">&nbsp;</th>',
                    '<td>{text:htmlEncode}</td></tr>',
	      '</tpl>',
	      '</table>',
            '</div>',
            '<img class="rrdgraph" alt="{title}" id="rrd-graph-{id}" src="{[this.main_url(values.url)]}"/>',
          '</div>',
        '</tpl>',
        '</div>',
      '</div>',
      '<div class="x-clear"></div>',
    '</tpl>', {
	main_url: function(url) { return build_url(url, w.data.rrd_main); }
    });

w.tpl.zoom_view = new Ext.XTemplate(
    '<tpl for=".">',
          '<img class="rrdgraph" alt="{title}"' + 
              ' src="{[this.zoom_url(values.url)]}"/>',
    '</tpl>', {
	zoom_url: function(url) { return build_url(url, w.data.rrd_zoom); }
    });



//== Handlers ==========================================================

w.ui.handler = {};

w.ui.handler.host_n_refresh = function(item, evt) {
    switch(item.group) {
    case 'host'   : 
	w.data.current.host    = item.text;
        z = Ext.partition(RRD.app.hosts, function(val) {
		return val.host == item.text; });
        store.load({ params: { describe: '' },
		    minidata : z[0][0].graph } );

	// TODO: reload store
	break;
    case 'refresh':
	w.data.current.refresh = item.text;
	refresher.next         = item.value * 1000;
	break;
    }    
    w.ui.card.setTitle((w.data.current.host    || '<i>unknown</i>') + ' @ ' + 
		       (w.data.current.refresh || '<i>???</i>'));
};

w.ui.handler.view_switcher = function(item) {
    Ext.each([ w.ui.card.b_view_full, 
	       w.ui.card.b_view_compact,
	       w.ui.card.b_view_mini ], function(elt) {
	if (elt != item) {
	    w.ui.card.f_graph.removeClass(elt.value.renderCls);
	} else {
	    w.ui.card.f_graph.addClass(elt.value.renderCls);		     
	    update_rrdgraph(elt.value.opts);
	}
      });
};

w.ui.handler.graph_select = function(dataview, sel) {
    var cmp = Ext.getCmp('zoom-view');
    if (sel.length > 0) {
	var img     = Ext.query('img.rrdgraph/@id', sel[0])[0];
	var graphid = img.firstChild.nodeValue.substr(10); // rrd-graph-
	var info    = store.fetchGraphInfo(graphid);
	w.tpl.zoom_view.overwrite(cmp.body, info.graph); 
	cmp.setTitle(info.grptitle + ' | ' + info.graph.title);
    } else {
	Ext.DomHelper.overwrite(cmp.body, w.str.please_select);
	cmp.setTitle(w.str.enlarged_view);
    }    
};

//== User interface ====================================================

new Ext.menu.Menu({
    id       : 'menu-refresh',
    items    : this.refresh.map(function(elt) {
        return new Ext.menu.CheckItem({
	    text   : '<tt>' + duration2str(elt) + '</tt>',
	    group  : 'refresh', value: elt,
	    handler: w.ui.handler.host_n_refresh }); })
  });
new Ext.menu.Menu({
    id       : 'menu-host',
    items    : this.hosts.map(function(elt) {
	return new Ext.menu.CheckItem({
	    text   : elt.host, 
	    group  : 'host', value: elt.host,
	    handler: w.ui.handler.host_n_refresh }); })
  });

w.ui.card = new Ext.Panel({
    renderTo   : el,
    layout     : 'border',
    title      : 'Current',
    autoWidth  : true,
    stateful   : false,
    items      : [ { 
        region      : 'center',
	id          : 'monitoring-view',
	autoScroll  : true,
	items       : w.ui.dv = new Ext.DataView({
	    xtype        : 'dataview',	    ref          : '../f_graph',
	    store        : store,	    tpl          : w.tpl.graph_view,
	    autoHeight   : true,
	    multiSelect  : false,	    singleSelect : true,
	    selectedClass: 'selected',      overClass    : 'over',
	    itemSelector : '.rrd-graph',
	    listeners    : {
	        'containerclick'  : function(obj, evt) {
		    if (t = evt.getTarget('h3', 3, true))
			t.up('.rrd-group-monitoring')
                         .toggleClass('x-grid-group-collapsed'); },
		'selectionchange' : w.ui.handler.graph_select
		}
	    })
      }, {
        region      : 'west',
	id          : 'shortcut-view',
	collapsible : true,
	split       : true,
	floatable   : false,
	minWidth    : 110,	width       : 110,
	items       : {
	    xtype        : 'dataview',	    ref          : '../f_entry',
	    store        : store,	    tpl          : w.tpl.entry,
	    autoHeight   : true,
	    multiSelect  : false,	    singleSelect : true,
	    selectedClass: 'selected',      overClass    : 'over',
	    itemSelector : 'div.entry',
	    listeners    : {
	        click : function(dv, idx, node, evt) { 
		    var cmp = w.ui.dv;
		    var offset = cmp.getEl().query('.rrd-group-monitoring')[idx].offsetTop;
		    cmp.ownerCt.body.scrollTo('top', offset, true);

 },
		render: function(dv) {
		    this.refresh = this.refresh.createSequence(function() {
			this.select(0); }, this); }
	    }
	 }
      }, { 
	region      : 'south',
	id          : 'zoom-view',
	title       : w.str.enlarged_view,
	collapsible : true,	collapsed   : true,
	split       : false,	useSplitTips: true,
	floatable   : true,	animFloat   : true,
	autoHide    : true,	autoReveal  : true,
	height      : 180,
	html        : w.str.please_select
      } ],
    defaults   : { border:false },
    tbar       :  [ {
	icon        : '/images/common/icons/16x16/computer.png',
	xtype       : 'button',         ref         : '../m_host',
	text        : 'Host',
	cls         : 'x-btn-text-icon',
	menu        : 'menu-host'
      },'-', {
	xtype       : 'tbsplit',	ref	    : '../m_refresh',
	icon        : '/images/common/icons/16x16/refresh.png',
	text        : 'Refresh',
	cls         : 'x-btn-text-icon',
	handler     : function() { 
		          update_rrdgraph({ _ts: (new Date()).getTime() }); },
	menu        : 'menu-refresh'
      }, '->', {
        icon        : '/images/common/icons/16x16/full-view.png',
	xtype       : 'button',         ref         : '../b_view_full',
        cls         : 'x-btn-icon',     minWidth    : 40,
        toggleGroup : 'view-mode',
        enableToggle: true,             allowDepress: false,
        tooltip     : 'Full view',      tooltipType : 'title',
        handler     : w.ui.handler.view_switcher,
	value       : { renderCls: 'view-full',
                        opts     : { width: 'max', mini: false } }
      }, { 
        icon        : '/images/common/icons/16x16/condensed-view.png',
	xtype       : 'button',         ref         : '../b_view_compact',
        cls         : 'x-btn-icon',     minWidth    : 40,
        toggleGroup : 'view-mode',	pressed     : true,
        enableToggle: true,             allowDepress: false,
        tooltip     : 'Compact view', tooltipType : 'title',
        handler     : w.ui.handler.view_switcher,
	value       : { renderCls: 'view-compact',
	                opts     : { width: 250, mini: false } }
      }, {
        icon        : '/images/common/icons/16x16/mini-view.png',
	xtype       : 'button',         ref         : '../b_view_mini',
        cls         : 'x-btn-icon',     minWidth    : 40,
        toggleGroup : 'view-mode',
        enableToggle: true,             allowDepress: false,
        tooltip     : 'Mini view',      tooltipType : 'title',
        handler     : w.ui.handler.view_switcher,
        value       : { renderCls: 'view-mini', 
			opts     : { width: 100, mini: true } }
      }, '-', {
        icon        : '/images/common/icons/16x16/month.png',
	xtype       : 'button',         ref         : '../b_duration_month',
	cls         : 'x-btn-icon',     minWidth    : 40,
        toggleGroup : 'duration-mode',
        enableToggle: true,             allowDepress: false,
        tooltip     : '1 month view',   tooltipType : 'title',
        handler     : update_rrdgraph.createCallback({ start: '-1m' })
      }, { 
        icon        : '/images/common/icons/16x16/week.png',
	xtype       : 'button',         ref         : '../b_duration_week',
	cls         : 'x-btn-icon',     minWidth    : 40,
        toggleGroup : 'duration-mode',	pressed     : false,
        enableToggle: true,             allowDepress: false,
        tooltip     : '1 week view',    tooltipType : 'title',
        handler     : update_rrdgraph.createCallback({ start: '-7d' })
      }, { 
        icon        : '/images/common/icons/16x16/day.png',
	xtype       : 'button',         ref         : '../b_duration_day',
	cls         : 'x-btn-icon',     minWidth    : 40,
        toggleGroup : 'duration-mode',	pressed     : true,
        enableToggle: true,             allowDepress: false,
        tooltip     : '1 day view',     tooltipType : 'title',
        handler     : update_rrdgraph.createCallback({ start: '-1d' })
      }, {
        icon        : '/images/common/icons/16x16/hour.png',
	xtype       : 'button',         ref         : '../b_duration_hour',
	cls         : 'x-btn-icon',     minWidth    : 40,
        toggleGroup : 'duration-mode',
        enableToggle: true,             allowDepress: false,
        tooltip     : '1 hour view',    tooltipType : 'title',
        handler     : update_rrdgraph.createCallback({ start: '-1h' })
      } ]
});

w.ui.loadmask = new Ext.LoadMask(w.ui.card.getEl(), {
	msg: 'Fetching descriptions', store: store });
w.ui.loadmask.enable();




//======================================================================

store.load({ params: { describe: '' },
	    minidata : this.hosts[0].graph } );



function update_rrdgraph(opts) {
    var main = {}, zoom = {};

    if (opts == null) {
	if (w.ui.card.b_view_full.pressed)
	    main.width = w.data.current.width_view_full;
	zoom.width = w.data.current.width_view_enlarged;
    } else {
	for (var o in opts) {
	    switch(o) {
	    case '_ts':
		main._ts = zoom._ts = opts._ts;
		break;
	    case 'start':
		main.start = zoom.start = opts.start;
		break;
	    case 'width':
		main.width = opts.width == 'max' ? 
		    w.data.current.width_view_full : opts.width;
	        break;
	    case 'mini':
		main.onlygraph = main.transpartent = opts.mini ? '' : null;
		break;
	    case 'xgrid':
		main.xgrid = opts.xgrid;
		break;
	    }
	}
    }

    for (var o in main) {
	Ext.apply(w.data.rrd_main, main);
	Ext.each(w.ui.dv.getEl().query('img.rrdgraph'), function(item) {
	    item.src = build_url(item.src, w.data.rrd_main); });
	break;
    }
    for (var o in zoom) {
	Ext.apply(w.data.rrd_zoom, zoom);
	Ext.each(Ext.getCmp('zoom-view').body.query('img.rrdgraph'),
	    function(item){ item.src = build_url(item.src, w.data.rrd_zoom); });
	break;
    }
}


//== Deal with window resize ===========================================


w.ui.card.addListener('afterlayout', function(obj, layout) {
    w.data.current.width_view_enlarged = layout.south.getSize().width  - 80;
    w.data.current.width_view_full     = layout.center.getSize().width - 150;
    if (w.data.current.width_view_full < 400)
	w.data.current.width_view_full = 400;
    update_rrdgraph(null);
});

Ext.EventManager.onWindowResize(function() {
    w.ui.card.doLayout(true);
});


w.ui.card.doLayout(true);

var resizer = new Ext.Resizable(w.ui.card.getEl(), {
    minHeight : 350,
    pinned    : true,
    handles   : 's'
  });
resizer.addListener('resize', function(obj, width, height) {
    w.ui.card.setHeight(height); });
resizer.resizeTo('auto', height);


//== Binding menu <-> scrollbar ========================================

w.ui.dv.ownerCt.body.addListener('scroll', function() {
    var last      = -1;
    var threshold = -25;
    var container = this;
    container.select('.rrd-group-monitoring').each(function(el) {
	last += 1;
	if (el.getOffsetsTo(container)[1] > threshold)
	    return false; 
      });
    if (last >= 0)
	w.ui.card.f_entry.select(last);
  }, w.ui.dv.ownerCt.body, {buffer:250});


//== Apply UI default values ===========================================

var host    = Ext.menu.MenuMgr.get('menu-host').items.itemAt(0);
host.setChecked(true);
host.handler(host);
var refresh = Ext.menu.MenuMgr.get('menu-refresh').items.itemAt(1);
refresh.setChecked(true);
refresh.handler(refresh);
var duration = w.ui.card.b_duration_week;
duration.toggle(true);
duration.handler(duration);

refresher();


} }; }();

// Local Variables:
// c-indent-level: 4
// c-basic-offset: 4
// End:
