Timeline.PointHighlightDecorator = function(params) {
    this._unit = ("unit" in params) ? params.unit : Timeline.NativeDateUnit;
    this._date = (typeof params.date == "string") ? 
        this._unit.parseFromObject(params.date) : params.date;
    this._width = ("width" in params) ? params.width : 10;
    this._color = params.color;
    this._opacity = ("opacity" in params) ? params.opacity : 100;
    this._label = ("label" in params) ? params.label : undefined;
};

Timeline.PointHighlightDecorator.prototype.initialize = function(band, timeline) {
    this._band = band;
    this._timeline = timeline;
    
    this._layerDiv = null;
};

Timeline.PointHighlightDecorator.prototype.paint = function() {
    if (this._layerDiv != null) {
        this._band.removeLayerDiv(this._layerDiv);
    }
    this._layerDiv = this._band.createLayerDiv(10);
    this._layerDiv.setAttribute("name", "span-highlight-decorator"); // for debugging
    this._layerDiv.style.display = "none";
    
    var minDate = this._band.getMinDate();
    var maxDate = this._band.getMaxDate();
    
    if (this._unit.compare(this._date, maxDate) < 0 &&
        this._unit.compare(this._date, minDate) > 0) {
        
        var pixel = this._band.dateToPixelOffset(this._date);
        var minPixel = pixel - Math.round(this._width / 2);
        
        var doc = this._timeline.getDocument();
    
        var div = doc.createElement("div");
        div.style.position = "absolute";
        div.style.overflow = "hidden";
        div.style.background = this._color;
        if (this._opacity < 100) {
            Timeline.Graphics.setOpacity(div, this._opacity);
        }
        this._layerDiv.appendChild(div);
        
        if (this._label) {
        
            var createTable = function() {
                var table = doc.createElement("table");
                table.insertRow(0).insertCell(0);
                return table;
            };
        
            var tableLabel = createTable();
            tableLabel.style.position = "absolute";
            tableLabel.style.overflow = "hidden";
            tableLabel.style.fontSize = "300%";
            tableLabel.style.color = this._color;
            tableLabel.rows[0].cells[0].innerHTML = this._label;
            this._layerDiv.appendChild(tableLabel);
        }
            
        if (this._timeline.isHorizontal()) {
            div.style.left = minPixel + "px";
            div.style.width = this._width + "px";
            div.style.top = "0px";
            div.style.height = "100%";
            
            if (this._label) {
                tableLabel.style.left = (pixel + this._width) + "px";
                tableLabel.style.width = (this._label.length) + "em";
                tableLabel.style.top = "10%";
                tableLabel.style.height = "100%";
            }
            
        } else {
            div.style.top = minPixel + "px";
            div.style.height = this._width + "px";
            div.style.left = "0px";
            div.style.width = "100%";
        }
    }
    this._layerDiv.style.display = "block";
};

Timeline.PointHighlightDecorator.prototype.softPaint = function() {
};

Timeline.DefaultEventSource.prototype.loadJSON = function(data, url) {
    var base = this._getBaseURL(url);
    var added = false;  
    if (data && data.events){
        var wikiURL = ("wikiURL" in data) ? data.wikiURL : null;
        var wikiSection = ("wikiSection" in data) ? data.wikiSection : null;
    
        var dateTimeFormat = ("dateTimeFormat" in data) ? data.dateTimeFormat : null;
        var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
       
        for (var i=0; i < data.events.length; i++){
            var event = data.events[i];
            var evt = new Timeline.DefaultEventSource.Event(
                parseDateTimeFunction(event.start),
                parseDateTimeFunction(event.end),
                parseDateTimeFunction(event.latestStart),
                parseDateTimeFunction(event.earliestEnd),
                !event.isDuration,
                event.title,
                event.description,
                this._resolveRelativeURL(event.image, base),
                this._resolveRelativeURL(event.link, base),
                this._resolveRelativeURL(event.icon, base),
                event.color,
                event.textColor
            );
            evt._obj = event;
            evt.getProperty = function(name) {
                return this._obj[name];
            };
            evt.setWikiInfo(wikiURL, wikiSection);

            this._events.add(evt);
            added = true;
        }
    }
   
    if (added) {
        this._fire("onAddMany", []);
    }
};

Timeline.StaticTrackBasedLayout.prototype._layout = function() {
    if (this._eventSource == null) {
        return;
    }
    
    var streams = [ Number.NEGATIVE_INFINITY ];
    var layout = this;
    var showText = this._showText;
    var theme = this._theme;
    var eventTheme = theme.event;
    
    var layoutInstant = function(evt, startPixel, endPixel, streamOffset) {
        var finalPixel = startPixel - 1;
        if (evt.isImprecise()) { // imprecise time
            finalPixel = endPixel;
        }
        if (showText) {
            finalPixel = Math.max(finalPixel, startPixel + eventTheme.label.width);
        }
        
        return finalPixel;
    };
    var layoutDuration = function(evt, startPixel, endPixel, streamOffset) {
        if (evt.isImprecise()) { // imprecise time
            var startDate = evt.getStart();
            var endDate = evt.getEnd();
                
            var startPixel2 = Math.round(layout._ether.dateToPixelOffset(startDate));
            var endPixel2 = Math.round(layout._ether.dateToPixelOffset(endDate));
        } else {
            var startPixel2 = startPixel;
            var endPixel2 = endPixel;
        }
        
        var finalPixel = endPixel2;
        var length = Math.max(endPixel2 - startPixel2, 1);
            
        if (showText) {
            if (length < eventTheme.label.width) {
                finalPixel = endPixel2 + eventTheme.label.width;
            }
        }
        
        return finalPixel;
    };
    var layoutEvent = function(evt) {
        var startDate = evt.getStart();
        var endDate = evt.getEnd();
        
        /* SJB */
        if (evt._obj.isDuration != undefined && evt._obj.isDuration == false) {
            endDate = startDate;
        }
        /**/
        
        var startPixel = Math.round(layout._ether.dateToPixelOffset(startDate));
        var endPixel = Math.round(layout._ether.dateToPixelOffset(endDate));
        
        var streamIndex = 0;
        for (; streamIndex < streams.length; streamIndex++) {
            if (streams[streamIndex] < startPixel) {
                break;
            }
        }
        if (streamIndex >= streams.length) {
            streams.push(Number.NEGATIVE_INFINITY);
        }
        
        var streamOffset = (eventTheme.track.offset + 
            streamIndex * (eventTheme.track.height + eventTheme.track.gap)) + "em";
            
        layout._tracks[evt.getID()] = streamIndex;
        
        if (evt.isInstant()) {
            streams[streamIndex] = layoutInstant(evt, startPixel, endPixel, streamOffset);
        } else {
            streams[streamIndex] = layoutDuration(evt, startPixel, endPixel, streamOffset);
        }
    };
    
    var iterator = this._eventSource.getAllEventIterator();
    while (iterator.hasNext()) {
        var evt = iterator.next();
        layoutEvent(evt);
    }
    
    this._trackCount = streams.length;
    
    /* SJB... trying to figure out how to resize to fit tracks...
    var p = this._timeline._eventPainter;

    var trackOffset = eventTheme.track.offset;
    var trackHeight = ("trackHeight" in p._params) ? p._params.trackHeight : eventTheme.track.height;
    var trackGap = ("trackGap" in p._params) ? p._params.trackGap : eventTheme.track.gap;
    
    //console.log((this._trackCount * (trackHeight + trackGap)) + trackOffset);
    //console.log(parseInt(this._timeline._bandInfo.width));
    //console.log(this._timeline);
    */
};

Timeline.DurationEventPainter.prototype.paint = function() {
    var eventSource = this._band.getEventSource();
    if (eventSource == null) {
        return;
    }
    
    if (this._highlightLayer != null) {
        this._band.removeLayerDiv(this._highlightLayer);
    }
    this._highlightLayer = this._band.createLayerDiv(105);
    this._highlightLayer.setAttribute("name", "event-highlights");
    this._highlightLayer.style.display = "none";
    
    if (this._eventLayer != null) {
        this._band.removeLayerDiv(this._eventLayer);
    }
    this._eventLayer = this._band.createLayerDiv(110);
    this._eventLayer.setAttribute("name", "events");
    this._eventLayer.style.display = "none";
    
    var minDate = this._band.getMinDate();
    var maxDate = this._band.getMaxDate();
    
    var doc = this._timeline.getDocument();
    
    var p = this;
    var eventLayer = this._eventLayer;
    var highlightLayer = this._highlightLayer;
    
    var showText = this._showText;
    var theme = this._params.theme;
    var eventTheme = theme.event;
    var trackOffset = eventTheme.track.offset;
    var trackHeight = ("trackHeight" in this._params) ? this._params.trackHeight : eventTheme.track.height;
    var trackGap = ("trackGap" in this._params) ? this._params.trackGap : eventTheme.track.gap;
    
    var appendIcon = function(evt, div) {
        /* SJB... */
        if (evt._color != null) {
            var img = doc.createElement("div");
            img.className = 'evt-icon';
            img.style.width = (trackHeight - 0.25) + 'em';
            img.style.height = (trackHeight - 0.25) + 'em';
            img.style.background = evt._color;
            //Timeline.Graphics.setOpacity(img, 70);
            
            /**/
            
        } else {
            var icon = evt.getIcon();
            var img = Timeline.Graphics.createTranslucentImage(
                doc, icon != null ? icon : eventTheme.instant.icon
            );
        }
        div.appendChild(img);
        div.style.cursor = "pointer";
        
        Timeline.DOM.registerEvent(div, "mousedown", function(elmt, domEvt, target) {
            p._onClickInstantEvent(img, domEvt, evt);
        });
    };
    var createHighlightDiv = function(highlightIndex, startPixel, length, highlightOffset, highlightWidth) {
        if (highlightIndex >= 0) {
            var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
            
            var div = doc.createElement("div");
            div.style.position = "absolute";
            div.style.overflow = "hidden";
            div.style.left = (startPixel - 3) + "px";
            div.style.width = (length + 6) + "px";
            div.style.top = highlightOffset + "em";
            div.style.height = highlightWidth + "em";
            div.style.background = color;
            Timeline.Graphics.setOpacity(div, 70);
            
            highlightLayer.appendChild(div);
        }
    };
    
    var createInstantDiv = function(evt, startPixel, endPixel, streamOffset, highlightIndex, highlightOffset, highlightWidth) {
        if (evt.isImprecise()) { // imprecise time
            var length = Math.max(endPixel - startPixel, 1);
        
            var divImprecise = doc.createElement("div");
            divImprecise.style.position = "absolute";
            divImprecise.style.overflow = "hidden";
            
            divImprecise.style.top = streamOffset;
            /* SJB */
            divImprecise.style.height = (trackHeight - 0.12) + "em";
            /**/
            divImprecise.style.left = startPixel + "px";
            divImprecise.style.width = length + "px";
            
            /* SJB...
            divImprecise.style.background = eventTheme.instant.impreciseColor;
            */
            divImprecise.style.background = evt._color;
            if (eventTheme.instant.impreciseOpacity < 100) {
                Timeline.Graphics.setOpacity(divImprecise, eventTheme.instant.impreciseOpacity);
            }
            
            eventLayer.appendChild(divImprecise);
        }
        
        var div = doc.createElement("div");
        div.style.position = "absolute";
        div.style.overflow = "hidden";
        eventLayer.appendChild(div);
        
        var foreground = evt.getTextColor();
        var background = evt.getColor();
        
        var realign = -8; // shift left so that icon is centered on startPixel
        var length = 16;
        if (showText) {
            div.style.width = eventTheme.label.width + "px";
            div.style.color = foreground != null ? foreground : eventTheme.label.outsideColor;
            
            appendIcon(evt, div);
            div.appendChild(doc.createTextNode(evt.getText()));
        } else {
            if (p._showLineForNoText) {
                div.style.width = "1px";
                div.style.borderLeft = "1px solid " + (background != null ? background : eventTheme.instant.lineColor);
                realign = 0; // no shift
                length = 1;
            } else {
                appendIcon(evt, div);
            }
        }
        
        div.style.top = streamOffset;
        div.style.height = trackHeight + "em";
        div.style.left = (startPixel + realign) + "px";

        createHighlightDiv(highlightIndex, (startPixel + realign), length, highlightOffset, highlightWidth);
    };
    var createDurationDiv = function(evt, startPixel, endPixel, streamOffset, highlightIndex, highlightOffset, highlightWidth) {
        var attachClickEvent = function(elmt) {
            if (!elmt) return;
            elmt.style.cursor = "pointer";
            Timeline.DOM.registerEvent(elmt, "mousedown", function(elmt, domEvt, target) {
                p._onClickDurationEvent(domEvt, evt, target);
            });
        };
        
        var length = Math.max(endPixel - startPixel, 1);
        if (evt.isImprecise()) { // imprecise time
            var div = doc.createElement("div");
            div.style.position = "absolute";
            div.style.overflow = "hidden";
            
            div.style.top = streamOffset;
            div.style.height = trackHeight + "em";
            div.style.left = startPixel + "px";
            div.style.width = length + "px";
            
            div.style.background = eventTheme.duration.impreciseColor;
            if (eventTheme.duration.impreciseOpacity < 100) {
                Timeline.Graphics.setOpacity(div, eventTheme.duration.impreciseOpacity);
            }
            
            eventLayer.appendChild(div);
            
            var startDate = evt.getLatestStart();
            var endDate = evt.getEarliestEnd();
            
            var startPixel2 = Math.round(p._band.dateToPixelOffset(startDate));
            var endPixel2 = Math.round(p._band.dateToPixelOffset(endDate));
        } else {
            var startPixel2 = startPixel;
            var endPixel2 = endPixel;
        }
        
        var foreground = evt.getTextColor();
        var outside = true;
        if (startPixel2 <= endPixel2) {
            length = Math.max(endPixel2 - startPixel2, 1);
            outside = !(length > eventTheme.label.width);
            
            div = doc.createElement("div");
            div.style.position = "absolute";
            div.style.overflow = "hidden";
            div.style.top = streamOffset;
            div.style.height = trackHeight + "em";
            div.style.left = startPixel2 + "px";
            div.style.width = length + "px";
            
            var background = evt.getColor();
            
            div.style.background = background != null ? background : eventTheme.duration.color;
            if (eventTheme.duration.opacity < 100) {
                /* SJB - for main track only (a hack, I know) */
                div.style.border = '1px solid #fff';
                div.style.height = (trackHeight - 0.25) + "em";
                /**/
                Timeline.Graphics.setOpacity(div, eventTheme.duration.opacity);
            }
            
            eventLayer.appendChild(div);
        } else {
            var temp = startPixel2;
            startPixel2 = endPixel2;
            endPixel2 = temp;
        }
        if (div == null) {
            console.log(evt);
        }
        attachClickEvent(div);
            
        if (showText) {
            var divLabel = doc.createElement("div");
            divLabel.style.position = "absolute";
            
            divLabel.style.top = streamOffset;
            divLabel.style.height = trackHeight + "em";
            /* SJB: + 5 pixels for some comfort space... */
            divLabel.style.left = (((length > eventTheme.label.width) ? startPixel2 : endPixel2) + 3) + "px";
            /**/
            divLabel.style.width = eventTheme.label.width + "px";
            divLabel.style.color = foreground != null ? foreground : (outside ? eventTheme.label.outsideColor : eventTheme.label.insideColor);
            divLabel.style.overflow = "hidden";
            
            var txt = evt.getText();
            if (evt._obj.children > 0) {
                txt += ' (+' + (evt._obj.children) + ')';
            }
            if (evt._obj.featured) {
                //txt = '*' + txt;
                divLabel.style.fontWeight = 'bold';
            }
            divLabel.appendChild(doc.createTextNode(txt));
            
            eventLayer.appendChild(divLabel);
            attachClickEvent(divLabel);
        }
        
        createHighlightDiv(highlightIndex, startPixel, endPixel - startPixel, highlightOffset, highlightWidth);
    };
    var createEventDiv = function(evt, highlightIndex) {
        var startDate = evt.getStart();
        var endDate = evt.getEnd();
        
        var startPixel = Math.round(p._band.dateToPixelOffset(startDate));
        var endPixel = Math.round(p._band.dateToPixelOffset(endDate));
        
        var streamOffset = (trackOffset + 
            p._layout.getTrack(evt) * (trackHeight + trackGap));

        if (evt.isInstant()) {
            createInstantDiv(evt, startPixel, endPixel, streamOffset + "em", 
                highlightIndex, streamOffset - trackGap, trackHeight + 2 * trackGap);
        } else {
            createDurationDiv(evt, startPixel, endPixel, streamOffset + "em",
                highlightIndex, streamOffset - trackGap, trackHeight + 2 * trackGap);
        }
    };
    
    var filterMatcher = (this._filterMatcher != null) ? 
        this._filterMatcher :
        function(evt) { return true; };
    var highlightMatcher = (this._highlightMatcher != null) ? 
        this._highlightMatcher :
        function(evt) { return -1; };
    
    var iterator = eventSource.getEventIterator(minDate, maxDate);
    while (iterator.hasNext()) {
        var evt = iterator.next();
        if (filterMatcher(evt)) {
            createEventDiv(evt, highlightMatcher(evt));
        }
    }
    
    this._highlightLayer.style.display = "block";
    this._eventLayer.style.display = "block";
};


