/**
 * @namespace   Namespace for Herodotus functionality.
 */
var Herodotus = {
    
    /**
     * Whether to highlight toponyms in text
     * @type Boolean
     */
    highlightOn: true, 

    /**
     * Whether to show shadows with map markers
     * @type Boolean
     */
    useShadows: false,     
    
    /**
     * Structure for narrative labels - chapters per book
     * @type int[]
     */
    structure: [216, 182, 160, 205, 126, 140, 239, 144, 122],
    
    /**
     * Special cases - subdivided chapters
     * @type Object
     */
    specialChapters: {
        '2.121': ['','A','B','C','D','E','F'],
        '5.92': ['','A','B','C','D','E','F','G'],
        '6.86': ['','A','B','C','D'],
        '7.8': ['','A','B','C','D'],
        '7.9': ['','A','B','C'],
        '7.10': ['','A','B','C','D','E','F','G','H'],
        '7.16': ['','A','B','C'],
        '8.60': ['','A','B','C'],
        '8.68': ['','A','B','C'],
        '8.140': ['A','B'],
        '9.7': ['','A','B']
    },
    
    /**
     * Initialize timemap.
     */
    init: function() {
        // set up labels
        var labels = [], book, ch, label, subs, i,
            // number of chapters per book
            structure = Herodotus.structure,
            // special subdivided chapters
            specialChapters = Herodotus.specialChapters;
        // create labels
        for (book=1; book <= structure.length; book++) {
            for (ch=1; ch <= structure[book-1]; ch++) {
                label = book + "." + ch;
                if (label in specialChapters) {
                    subs = specialChapters[label];
                    for (i=0; i < subs.length; i++) {
                        labels.push(label + subs[i]);
                    }
                }
                else {
                    labels.push(label);
                }
            }
        }
        
        // remove shadow from theme icons
        if (!Herodotus.useShadows) {
            var themes = Herodotus.themes, k;
            for (k in themes) {
                if (themes.hasOwnProperty(k)) {
                    themes[k].icon.shadow = "";
                }
            }
        }
        
        // create band info
        var bandInfo = [
            Timeline.createBandInfo({
                width:          "88%", 
                intervalUnit:   Timeline.DateTime.YEAR, 
                intervalPixels: 110,
                eventSource:    false
            }),
            Timeline.createBandInfo({
                width:          "12%", 
                intervalUnit:   Timeline.DateTime.DECADE, 
                intervalPixels: 200,
                overview:       true,
                eventSource:    false
            })
        ];
        
        // add custom labeller
        Herodotus.labelUtils = new LabelUtils(bandInfo, labels, Herodotus.emphasizeLabel);
        
        // init timemap
        Herodotus.timemap = TimeMap.init({
            mapId: "map",
            timelineId: "timeline",
            options: {
                openInfoWindow: Herodotus.openInfoWindow
            },
            datasets: [
                {
                    id: "references",
                    title: "References",
                    theme: "green",
                    type: "progressive",
                    options: {
                        type: "json_string",
                        // url with start placeholder
                        url: "scripts/data/data_[start].js",
                        start: Herodotus.labelUtils.getStartDate(),
                        // lower cutoff date for data
                        dataMinDate: Herodotus.labelUtils.getStartDate(),
                        // 30 yrs in milliseconds
                        interval: 946684806845,   
                        // function to turn date into string appropriate for service
                        formatDate: Herodotus.formatDate,
                        // transform item data
                        transformFunction: Herodotus.transformItem
                    }
                }
            ],
            bands: bandInfo,
            dataLoadedFunction: function(tm) {
                // add geotype filter
                tm.addFilter("map", Herodotus.filterGeotype);
                tm.addFilter("timeline", Herodotus.filterGeotype);
                // add fade filter
                tm.addFilter("map", Herodotus.fadeFilter);
                // scroll to beginning of narrative
                tm.timeline.getBand(0).setCenterVisibleDate(Herodotus.labelUtils.labelToDate('1.1'));
                tm.timeline.layout();
                Herodotus.loadChapter('1.1');
            }
        });
    },
    
    /**
     * The TimeMap object
     * @type TimeMap
     */
    timemap: null,
    
    /**
     * The current chapter (in text)
     * @type String
     */
    currentPos: "",
    
    /**
     * The current language
     * @type String
     */
    currentLang: "English",
    
    /**
     * Geotype themes: Settlement, Region, Physical feature
     * @type Object
     */
    themes: {
        s: new TimeMapTheme({ 
                eventIconPath: "images/", 
                iconImage: "images/red-100.png"
            }),
        r: new TimeMapTheme({ 
                eventIconPath: "images/", 
                color: "#5A7ACF",
                eventIconImage: "blue-circle.png",
                iconImage: "images/blue-100.png"
            }),
        p: new TimeMapTheme({ 
                eventIconPath: "images/", 
                color: "#19CF54",
                eventIconImage: "green-circle.png",
                iconImage: "images/green-100.png"
            }),
    },
    
    /**
     * Fade images: Transparent images for each fade level, for each theme
     * @type Object
     */
    fadeImages: {
        s: ['red-100.png', 'red-80.png', 'red-60.png', 'red-40.png', 'red-20.png'],
        r: ['blue-100.png', 'blue-80.png', 'blue-60.png', 'blue-40.png', 'blue-20.png'],
        p: ['green-100.png', 'green-80.png', 'green-60.png', 'green-40.png', 'green-20.png']
    },
    
    /**
     * Whether each geotype is selected for display
     * @type Object
     */
    geotypeSelected: {
        s: true,
        r: true,
        p: true
    },
    
    /**
     * Toggle whether a geotype is selected for display
     *
     * @param {String} geotype      Code for geotype to toggle
     */
    toggleGeotype: function(geotype) {
        var selected = Herodotus.geotypeSelected,
            tm = Herodotus.timemap;
        if (geotype in selected) {
            selected[geotype] = !selected[geotype];
        }
        // update map and timeline
        tm.filter("map");
        tm.filter("timeline");
    },
    
    /**
     * Filter for item based on geotype
     *
     * @param {TimeMapItem} item    Item to check
     * @return {Boolean}            Whether to filter item
     */
    filterGeotype: function(item) {
        return (Herodotus.geotypeSelected[item.opts.geotype]);
    },
    
    /**
     * Filter to fade the icon image based on timeline location
     *
     * @param {TimeMapItem} item    Item to filter
     * @return {Boolean}            Always true
     */
    fadeFilter: function(item) {
        var topband = item.dataset.timemap.timeline.getBand(0),
            maxVisibleDate = topband.getMaxVisibleDate().getTime(),
            minVisibleDate = topband.getMinVisibleDate().getTime(),
            itemStart = item.event.getStart().getTime(),
            fadeImages = Herodotus.fadeImages[item.opts.geotype];
        // set image according to timeline position
        item.placemark.setImage("images/" + fadeImages[
            Math.floor(
                (maxVisibleDate - itemStart) / (maxVisibleDate - minVisibleDate)
                * fadeImages.length
            )   
        ]);
        return true;
    },
    
    /**
     * Template for info window HTML
     * @type String
     */
    infoTemplate: '<div class="refinfo"><div class="reftitle">{{title}}</div><div class="refpos">Book {{book}}, Ch. {{chapter}}</div><div>Type: <span class="refdata">{{geotype}} &gt; {{subtype}}</span></div><div>Region: <span class="refdata">{{region}}</span></div><div class="reflinks"><span class="zoomlink">Zoom in</span></div><div>',
    
    /**
     * Transform item position to start date.
     *
     * @param {Object} data     Item data
     * @return {Object}         Transformed data
     */
    transformItem: function(data) {
        // mapping from short keys
        var keyMap = {"title":"t","region":"r","point":"l","pos":"p","subtype":"s","type":"g","id":"i"};
        function getVal(key) {
            return data[keyMap[key]];
        }
        var pos = getVal("pos"),
            year = Herodotus.labelUtils.getLabelIndex(pos),
            themes = Herodotus.themes,
            geotype = getVal("type"),
            template = Herodotus.infoTemplate,
            bc = pos.split('.'),
            replacements, k;
        // decompress standard elements
        data.title = getVal("title");
        data.point = getVal("point");
        // set date
        if (year >= 0) {
            data.start = year + " AD";
        } // else console.log(data.pos);
        // get options
        data.options = {
            geotype: geotype,
            region: getVal("region"),
            subtype: getVal("subtype"),
            book: bc[0],
            chapter: bc[1],
            id: getVal("id")
        };
        // fill in template
        replacements = {
            title: data.title,
            book: bc[0],
            chapter: bc[1],
            geotype: Herodotus.geotypes[data.options.geotype],
            subtype: Herodotus.subtypes[data.options.subtype],
            region: Herodotus.regions[data.options.region]
        };
        data.options.infoHtml = Herodotus.fillTemplate(template, replacements);
        // get theme color according to geotype
        if (geotype) {
            // set theme
            if (geotype in themes) {
                data.options.theme = themes[geotype];
            }
        }
        return data;
    },
    
    /**
     * Custom info window opener to deal with link handlers
     */
    openInfoWindow: function() {
        // scroll timeline if necessary
        if (this.placemark && !this.visible && this.event) {
            var topband = this.dataset.timemap.timeline.getBand(0);
            topband.setCenterVisibleDate(this.event.getStart());
        }
        // make DOM node
        var html = $(this.opts.infoHtml),
            link = html.find('span.zoomlink'),
            item = this;
        // set the zoom handler
        html.find('span.zoomlink')
            link.click(function() {
                var map = item.dataset.timemap.map,
                    currZoom = map.getZoom(), zoom;
                if (currZoom < 10) {
                    zoom = currZoom + 2;
                    link.removeClass("off");
                } else {
                    zoom = 12;
                    link.addClass("off");
                }
                map.setCenter(item.getInfoPoint(), zoom);
            });
        // open window
        this.placemark.openInfoWindow($(html).get(0));
        // load chapter
        Herodotus.loadChapter(this.opts.book + "." + this.opts.chapter, this.getTitle());
        // custom functions will need to set this as well
        this.selected = true;
    },
    
    /**
     * Template for chapter HTML
     * @type String
     */
    textTemplate: '<div class="textpos">Book {{book}}, Ch. {{chapter}}</div><p id="texten">{{texten}}</p><p id="textgr">{{textgr}}</p>',
    
    /**
     * Load text for a particular chapter
     *
     * @param {String} pos          Chapter postion to load
     * @param {String} [highlight]  Toponym to highlight
     */
    loadChapter: function(pos, highlight) {
        if (Herodotus.currentPos != pos) {
            var urlTemplate = "scripts/data/ch_[pos].js",
                url = urlTemplate.replace("[pos]", pos);
            $.getJSON(url, function(json) {
                bc = pos.split(".");
                var html = Herodotus.fillTemplate(Herodotus.textTemplate, {
                    book: bc[0],
                    chapter: bc[1],
                    texten: json.english,
                    textgr: json.greek
                });
                $('#textpane').html(html);
                if (Herodotus.currentLang == "English") {
                    $('#textgr').hide();
                } else {
                    $('#texten').hide();
                }
                Herodotus.highlightText(highlight);
                Herodotus.currentPos = pos;
                Herodotus.highlightCurrentPos();
                Herodotus.updateNextPrevLinks();
            });
        } else {
            Herodotus.highlightText(highlight);
        }
    },
    
    /**
     * Highlight a toponym, with synonyms, in the chapter text.
     * Requires jquery.highlight
     *
     * @param {String} term     Toponym to highlight
     */
    highlightText: function(term) {
        // allow this function to be switched off
        if (Herodotus.highlightOn) {
            $('#textpane').removeHighlight();
            if (term) {
                var toponyms = Herodotus.toponyms,
                    terms = [], x;
                // look for synonyms
                if (term in toponyms) {
                    terms = toponyms[term];
                } else {
                    terms.push(term);
                }
                for (x=0; x<terms.length; x++) {
                    $('#textpane').highlight(terms[x]);
                }
            }
        }
    },
    
    /**
     * Add a highlight to the current chapter in the timeline
     */
    highlightCurrentPos: function() {
        var timeline = Herodotus.timemap.timeline,
            end = Herodotus.labelUtils.labelToDate(Herodotus.currentPos),
            start = new Date(end.getTime() - 31536000000);
        if (!Herodotus.highlightDecorator) {
            Herodotus.highlightDecorator = new Timeline.SpanHighlightDecorator({
                startDate: start, 
                endDate: end, 
                color: '#FFFFFF', 
                opacity: 50, 
                startLabel: '', 
                endLabel: ''
            });
            timeline.getBand(0)._decorators.push(Herodotus.highlightDecorator);
            Herodotus.highlightDecorator.initialize(timeline.getBand(0), timeline);
        } else {
            Herodotus.highlightDecorator._startDate = start;
            Herodotus.highlightDecorator._endDate = end;
        }
        Herodotus.highlightDecorator.paint();
    },
    
    /**
     * Decorator highlighting current position
     */
    highlightDecorator: null,
    
    /**
     * Go to a chapter by offset from current
     *
     * @param {int} offset      How many chapters forward or backward to move
     */
    goToPosOffset: function(offset) {
        var labels = Herodotus.labelUtils.labels,
            index = Herodotus.labelUtils.getLabelIndex(Herodotus.currentPos),
            newIndex = index + offset;
        if (newIndex < 0) newIndex = 0;
        if (newIndex >= labels.length) newIndex = labels.length - 1;
        Herodotus.goToPos(labels[newIndex]);
    },
    
    /**
     * Go to next chapter
     */
    goToNextPos: function() {
        Herodotus.goToPosOffset(1);
    },
    
    /**
     * Go to previous chapter
     */
    goToPrevPos: function() {
        Herodotus.goToPosOffset(-1);
    },
    
    /**
     * Update the next and previous links
     */
    updateNextPrevLinks: function() {
        var labels = Herodotus.labelUtils.labels,
            index = Herodotus.labelUtils.getLabelIndex(Herodotus.currentPos);
        if (index == 0) $("#prevlink").addClass("off");
        else $("#prevlink").removeClass("off");
        if (index == labels.length-1) $("#nextlink").addClass("off");
        else $("#nextlink").removeClass("off");
    },
    
    /**
     * Toggle between English and Greek text
     */
    toggleLang: function() {
        $('#texten').toggle();
        $('#textgr').toggle();
        $('#langtoggle').text("Switch to " + Herodotus.currentLang);
        Herodotus.currentLang = (Herodotus.currentLang == "English") ? "Greek" : "English";
    },
    
    /**
     * Whether or not a given label should be emphasized.
     * Currently, emphasizes first chapter and chapters divisible by 10.
     *
     * @param {String} label    Label to assess
     * @return {Boolean}        Whether the label should be emphasized
     */
    emphasizeLabel: function(label) {
        return label.endsWith('.1') || label.endsWith('0');
    },
    
    /**
     * Utility function - poor man's sprintf
     *
     * @param {String} template     Template with {{placeholders}}
     * @param {Object} replacements Array of replacement data, with placeholders as keys
     */
    fillTemplate: function(template, replacements) {
        for (var k in replacements) {
            template = template.replace('{{'+ k +'}}', replacements[k]);
        }
        return template;
    },
    
    /**
     * Scroll timeline to a particular spot in the narrative
     *
     * @param {String} pos      Position to jump to
     */
    goToPos: function(pos) {
        var d = Herodotus.labelUtils.labelToDate(pos);
        Herodotus.timemap.timeline.getBand(0).setCenterVisibleDate(d);
        Herodotus.loadChapter(pos);
    },
    
    /**
     * Wrapper for scope issues.
     */
    formatDate: function(date) {
        return Herodotus.labelUtils.dateToLabel(date);
    }
};
