/* Copyright 2009 Palm, Inc.  All rights reserved. */

var GridAssistant = Class.create(
{
	$widget_noteGrid: null,
	$widget_filterField: { model: "filterModel" },
	$element_noteFormatter: null,
	$element_noteGrid: null,
	
    initialize: function(data)
    {
        this.data = data;
        this.model = data.model;
		this.filterModel = { disabled: true };
        
        // DONT LIKE THIS!!
        this.model.setScene(this);
    },
    
    setup: function()
    {
        var self = this;
        new GridList(
        {
            target: this.$element_noteGrid,
			templates:
			{
				first: 'grid/top-entry',
				next: 'grid/entry',
			},
			formatters: 
            {
                dummy: function(dummy, item)
                {
                    self._formatItem(dummy, item, self.filterString);
                }
            },
            columns: 3,
			onItemMoved: this._onItemMoved.bind(this)
        });
    },

	$aboutToActivate_reloadGrid: function(event)
	{
		this._refreshList(event.synchronizer.wrap(Mojo.doNothing));
	},
	
	$menu_palm_help_cmd: function(event)
	{
		new Mojo.Service.Request('palm://com.palm.applicationManager', 
		{
			method: 'open',
			parameters: 
			{
				id: 'com.palm.app.help',
				params:
				{
					target: 'http://help.palm.com/memos/index.html'
				}
			}
		});
	},
	
	$menu_new_memo_cmd: function(event)
	{
		this._handleNewNote();
	},

	$tap_noteGrid: function(event)
	{
		var item = this.$widget_noteGrid.findItem(event.target);
		if (item == null) 
		{
			// Do nothing
		}
		else if (item == this.$widget_noteGrid.getItemAt(0, 0)) 
		{
			this._handleNewNote();
		}
		else 
		{
			this._handleEditNote(item.id, item.color);
		}
    },
    
    $filter_filterField: function(event)
    {
        this.filterString = event.filterString;
        
        if (this.filterString) 
        {
            this.$widget_noteGrid.dragEnabled = false;
        }
        else 
        {
            this.$widget_noteGrid.dragEnabled = true;
        }
        
        this._refreshList();
		this.controller.getSceneScroller().mojo.revealTop(0);
    },
    
    _handleNewNote: function()
    {
        // Get top element.  We insert before this one so we need to know its position and its color
        var top = this.$widget_noteGrid.getItemAt(0, 1); // row-0,col-1 : Skip notepad which is element 0
        // Dont have a top element? use some defaults
        if (top == null) 
        {
            top =
            {
                color: Note.colors[0 | (Math.random() * Note.colors.length)],
                position: 'z'
            };
        }
        this.controller.stageController.pushScene('note', 
        {
            model: this.model,
            color: Note.colors[(Note.colors.indexOf(top.color) + 1) % Note.colors.length],
            position: this._findInsertPosition('a', top.position),
			text: this.filterString || ""
        });
    },
    
    _handleEditNote: function(id, color)
    {
        this.controller.stageController.pushScene('note', 
        {
            model: this.model,
            id: id,
			color: color
        });
    },
	
	_onItemMoved: function(item, before, after)
	{
        // Calculate the new position in the list
		var oposition = item.position;
        item.position = this._findInsertPosition((before ? before.position : 'a'), (after ? after.position : 'z'));
        
		if (item.position != oposition) 
		{
			// Save the new note position
			this.model.saveNote(new Note(
			{
				id: item.id,
				position: item.position
			}));
		}
    },
    
    _findInsertPosition: function(before, after)
    {
        Mojo.assert(before < after, "_findInsertPosition: before < after");
        var pos = this._findInsertPositionInternal(before, after);
        Mojo.assert(before < pos, "_findInsertPosition: before < pos");
        Mojo.assert(pos < after, "_findInsertPosition: pos < after");
		Mojo.Log.info("before %s after %s pos %s", before, after, pos);
        return pos;
    },
    
    _findInsertPositionInternal: function(before, after)
    {
        var beforelen = before.length;
        var afterlen = after.length;
        
        var i;
        for (i = 0; i < beforelen && i < afterlen && before.charAt(i) == after.charAt(i); i++) 
            ;
			
		if (afterlen == beforelen && afterlen == i)
		{
			return before + 'm';
		}
        else if (afterlen == i) 
        {
            var bc = before.charCodeAt(i);
            var diff = 'z'.charCodeAt(0) - bc
            if (diff > 1) 
            {
                return before.substring(0, i) + String.fromCharCode(bc + diff / 2);
            }
			else
			{
				return before.substring(0, i + 1) + this._findInsertPositionInternal(before.substring(i + 1), '');
			}
        }
        else if (beforelen == i) 
        {
            var ac = after.charCodeAt(i);
            var diff = ac - 'a'.charCodeAt(0);
            if (diff > 1) 
            {
                return before + String.fromCharCode('a'.charCodeAt(0) + diff / 2);
            }
            else 
            {
                return before + 'a' + this._findInsertPositionInternal('', after.substring(i + 1));
            }
        }
        else 
        {
            var bc = before.charCodeAt(i);
            var diff = after.charCodeAt(i) - bc;
            if (diff > 1) 
            {
                return before.substring(0, i) + String.fromCharCode(bc + diff / 2);
            }
            else 
            {
                return before.substring(0, i + 1) + this._findInsertPositionInternal(before.substring(i + 1), after.substring(i + 1));
            }
        }
    },
    
    _calculateFontSize: function(str)
    {
        var lowest = 6;
        var highest = 32;

		// Hidden formatter note
		var noteFormatter = this.$element_noteFormatter;
        
       	noteFormatter.innerHTML = str;
        var low = lowest;
        var high = highest + 1;
        for (;;) 
        {
            var fs = Math.floor((low + high) / 2);
			if (fs == low)
			{
				return fs;
			}
            noteFormatter.style.fontSize = fs + "pt";
            
            // The box size can change when we change the font size
            // (it can shrink but not grow beyond the box boundary)
            var boxwidth = noteFormatter.clientWidth;
            var boxheight = noteFormatter.clientHeight;
            
            var width = noteFormatter.scrollWidth;
            var height = noteFormatter.scrollHeight;
			
			// HACK - webkit is getting the scrollHeight wrong by one pixel
			height -= 1;
			
			//Mojo.Log.info("try font", fs, height, boxheight, width, boxwidth);
            
            if (height > boxheight || width > boxwidth) 
            {
                high = fs;
            }
            else
            {
                low = fs;
            }
        }
    },
    
    _enableDisableFilter: function(count)
    {
        var fdisabled = (count > 0 || this.filterString ? false : true);
        if (fdisabled != this.filterModel.disabled) 
        {
            this.filterModel.disabled = fdisabled;
            this.controller.modelChanged(this.filterModel);
        }
		if (!this.filterModel.disabled)
		{
			this.$widget_filterField.setCount(count);
		}
    },
    
    _refreshList: function(done)
    {
		var self = this;
		this.model.getNotes(this.filterString, 0, undefined, function(offset, list, total)
		{
			list.unshift({ position: 'a' });
			self._fixMissingInfo(list, 1);
			self.$widget_noteGrid.render(list);
			self._enableDisableFilter(total);
			done && done();
		});
    },
	
	_fixMissingInfo: function(list, offset)
	{
		Mojo.Log.info("fixMissingInfo:", list.length);
		// We can get items which dont have positions (via sync mostly).  We need to fix them up.
		// On the plus side, they will all be at the beginning of the list.
		var len;
		for (len = offset; len < list.length && !list[len].position; len++)
			;
		Mojo.Log.info("..... ", len);
		if (len > offset) 
		{
			var start = 'a';
			var end = (len < list.length ? list[len].position : 'z');
			var i = offset;
			var self = this;
			function dobatch(c)
			{
				for (; i < len && c-- > 0; i++) 
				{
					// Find insert position for note
					var item = list[i];
					item.position = self._findInsertPosition(start, end);
					item.font = self._calculateFontSize(item.title);
					
					// Save the new note position
					self.model.saveNote(new Note(
					{
						id: item.id,
						color: item.color ? item.color : 'yellow',
						position: item.position,
						font: item.font,
					}));
					
					// Move start so we insert the next note after this one
					start = item.position;
				}
				if (i < len) 
				{
					dobatch.defer(25);
				}
			}
			dobatch.defer(25);
		}
	},
    
    _formatItem: function(dummy, item, filterString)
    {
        item.textFormatted = item.title ? item.title.escapeHTML() : '...';
        
        if (filterString) 
        {
            var regexp = new RegExp('(' + this._regExpEscape(filterString) + ')', 'gi');
            var subst = '<span class="list-highlight">$1</span>';
            item.textFormatted = item.textFormatted.replace(regexp, subst);
        }
        
        switch (item.color)
        {
            case 'pink':
            case 'yellow':
            case 'green':
            case 'blue':
                item.colorFormatted = item.color;
                break;
            default:
                item.colorFormatted = 'yellow';
                break;
        }
		// If we dont have any font info, calculate it and save for next time
		if (!item.font && item.id)
		{
			item.font = this._calculateFontSize(item.textFormatted);
			this.model.saveNote(new Note(
	        {
	            id: item.id,
				font: item.font,
	        }));
		}
    },
	
	_regExpEscape: function(str)
	{
		return str.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&");
	},
});
