﻿/***********************
MooTools extensions
************************/
Selectors.Pseudo.checked = function() {
    return ('input' == this.get('tag') && ('radio' == this.get('type') || 'checkbox' == this.get('type')) && this.checked);
};
Array.prototype.findMin = function(fn, inputArrayIsSorted) {
    var smallestValue = 0;
    var smallestIndex = -1;
	for (var i = 0, l = this.length; i < l; i++){
        var minValue = fn.call(this, this[i]);
        if(smallestIndex<0 || minValue<smallestValue) {
            smallestValue = minValue
            smallestIndex = i;
        } else if(i>0 && inputArrayIsSorted)
            break;
	}
    return this[smallestIndex];
}


/****************************
Classes
****************************/

/*
    Class to wrap the functionality around the indicator used eg. on the AccessSearchPage
    and the SearchOffers page.
*/


var SFIndicator = new Class({
    options: {
        delayBeforeDim: 100,
        animateOnEnable: true,
        animateOnDisable: true
    },
    Implements: [Options, Events],
    initialize: function(indicatorElement, options) {
        this.setOptions(options)
        this.enabled = false;
        this.indicatorElement = indicatorElement;
    },
    disable: function() {
        if (this.enabled) {
            this.enabled = false;
            this.indicatorElement.fade(this.options.animateOnDisable ? 'out' : 'hide');
        }
    },
    enable: function(text) {
        if (!this.enabled) {
            this.enabled = true;
            this.indicatorElement.fade(this.options.animateOnEnable ? 'in' : 'show');
        }
    }
});

var SFDialog = new Class({

    Implements: [Options, Events],
    
    options: {
        content: '<p>Empty...</p>',
        height: 162,
        width: 250  
    },
    initialize: function(parentEl, options) {
        this.setOptions(options);
        this.parentEl = parentEl;
        this.contentLoaded = false;
        this.rootEl = this.createRootElement();
        if(this.parentEl!=null)
            this.moveTo(parentEl.getCoordinates());
        else this.moveTo({top: 0, left: 0});
    },
    createRootElement: function() {
        var rootEl = new Element('div');
        rootEl.className = "dialog";
        rootEl.id = new Date().getTime();
        rootEl.setStyles({
            position: 'absolute',
            height: this.options.height,
            width: this.options.width
        });
        //rootEl.innerHTML = '<div class="loading"><img src="images/loader.gif" alt="" />Laddar...</div>'
        return rootEl;
    },
    moveTo: function(coords) {
        this.rootEl.setStyles({
            top: coords.top-this.options.height-35,
            left: coords.left-50
        });
    },
    show: function() {
        if(!this.isVisible()) {
            this.rootEl.fade('hide');
            $(document).getElement('body').appendChild(this.rootEl);
            this.rootEl.fade('in');
            if(!this.contentLoaded) {
                //(function() {this.rootEl.innerHTML = this.options.content;}).delay(500, this);
                this.rootEl.innerHTML = this.options.content;
                this.contentLoaded = true;
            }
        }
    },
    close: function() {
        if(this.isVisible()) {
            this.rootEl.dispose();
        }
    },
    isVisible: function() {
        return $defined($(this.rootEl.id));
    }
});

var SFMapManager = new Class({

    Implements: [Options, Events],
    options: {
        clusterMarkers: false
    },
    initialize: function(options) {

        this.setOptions(options);
        this.options.clusterMarkers = mapInit.clusterMarkers;
        var map = new GMap2($('map'));
        this.map = map;
        map.enableContinuousZoom();
        map.enableScrollWheelZoom();
        map.setCenter(new GLatLng(mapInit.latitude, mapInit.longitude), mapInit.zoomLevel);
        map.addControl(new GLargeMapControl());

        if ($defined(mapInit.dslArea.polylines)) {
            var dslAreaPolygon = new GPolygon.fromEncoded(mapInit.dslArea);
            map.addOverlay(dslAreaPolygon);
        }

        var baseIcon = new GIcon();
        baseIcon.iconSize = new GSize(14, 14);
        baseIcon.iconAnchor = new GPoint(7, 7);
        baseIcon.infoWindowAnchor = new GPoint(7, 7);

        var iconFinished = new GIcon(baseIcon);
        var iconPlanned = new GIcon(baseIcon);
        var iconNotPlanned = new GIcon(baseIcon);
        iconFinished.image = "/images/iconFinished.png";
        iconPlanned.image = "/images/iconPlanned.png";
        iconNotPlanned.image = "/images/iconNotPlanned.png";

        var icons = {
            Finished: iconFinished,
            Planned: iconPlanned,
            NotPlanned: iconNotPlanned
        }

        var self = this;
        this.markerContainer = {};
        var markerArray = [];
        this.json = new Request.JSON({
            url: '/ajaxProxies/queryAddresses.ashx',
            method: 'get',
            data: 'mode=markers&areaCode=' + areaCode,
            onSuccess: function(markerObjs) {
                markerObjs.each(function(markerObj) {

                    //Dont display markers that for some reason does not have any geographic location
                    if (markerObj.Latitude.length == 0) return;

                    var latLng = new GLatLng(markerObj.Latitude, markerObj.Longitude);
                    var marker = new GMarker(latLng, icons[markerObj.Status]);
                    self.markerContainer[markerObj.ID] = {
                        gMarker: marker,
                        address: markerObj.Address
                    };
                    var encodedAddress = encodeURI(markerObj.Address);
                    GEvent.addListener(marker, 'click', function() {
                        var popupHtmlContent = self.createPopupHtmlContent(markerObj);
                        marker.openInfoWindowHtml('<div class="mapPopup">' + popupHtmlContent + '</div>', { maxWidth: 330 });
                    });
                    markerArray.push(marker);
                });
                if (self.options.clusterMarkers)
                    var markerCluster = self.createMarkerClusterer(map, markerArray);
                else {
                    var markerManager = new GMarkerManager(map);
                    markerManager.addMarkers(markerArray, 0, 17);
                    markerManager.refresh();
                }
                self.fireEvent('markersLoaded');

            }
        });

    },
    createMarkerClusterer: function(map, markerArray) {
        var self = this;
        return new MarkerClusterer(map, markerArray, {
            gridSize: 40,
            maxZoom: 16,
            styles: [
                self.createClusterStyle(1),
                self.createClusterStyle(2),
                self.createClusterStyle(3),
                self.createClusterStyle(4),
                self.createClusterStyle(5)
            ]
        });
    },
    createClusterStyle: function(i) {
        var sizes = [53, 56, 66, 78, 90];
        return {
            height: sizes[i - 1],
            width: sizes[i - 1],
            opt_textColor: 'white',
            url: '/images/mapicons/m' + i + '.png'
        }
    },
    createPopupHtmlContent: function(markerObj) {
        switch (markerObj.Status) {
            case 'NotPlanned': return '<h4>' + markerObj.Address + '</h4><p>Anslutning på denna adress är planerad, men du kan ännu inte skicka in beställningar ännu. Om du vill kan du skicka in en <a href="'+ mapInit.InformationSubscriptionLink +'">intresseanmälan</a> så kontaktar vi dig så fort du kan beställa.</p>';
            case 'Planned': return '<h4>' + markerObj.Address + '</h4><p>Anslutning på denna adress är planerad till<br />' + markerObj.DevelopmentPlanDate + ', du kan redan nu <a href="' + mapInit.LANLink + '">beställa</a> tjänster</p>';
            case 'Finished': return '<h4>' + markerObj.Address + '</h4><p>Adressen är ansluten, du kan gå vidare för att <a href="' + mapInit.LANLink + '">beställa</a> erbjudanden.</p>';
            default: return '';
        }
    },
    loadMarkers: function() {
        this.json.send();
    },
    activateMarker: function(markerId) {
        if (!$defined(this.markerContainer[markerId])) {
            alert('Markören kunde tyvärr inte hittas i kartan');
            return;
        }
        if (!this.options.clusterMarkers)
            GEvent.trigger(this.markerContainer[markerId].gMarker, 'click');
        else {
            this.map.setCenter(this.markerContainer[markerId].gMarker.getLatLng(), 17);
            GEvent.trigger(this.markerContainer[markerId].gMarker, 'click');
        }
    }
});

/*
Class: Slider
        Creates a slider with two elements: a knob and a container. Returns the values.
*/
var SFSlider = new Class({

    Implements: [Options, Events],

    options: {
        onChange: Class.empty,
        onTick: function(pos) {
            pos -= this.knobWidth;
            this.knob.setStyle('left', pos);
            this.drag.value.now['x'] = pos;
        },
        onMaxTick: function(pos) {
            pos += 1;
            this.maxknob.setStyle('left', pos);
            this.maxdrag.value.now['x'] = pos;
        },
        onDragStart: Class.empty,
        onDragComplete: Class.empty,
        steps: 100,
        knobheight: 39,
        spread: 6,
        range: false,
        initValues: false
    },

    initialize: function(el, knobs, options) {
        this.setOptions(options);
        this.element = $(el);
        this.knob = $(knobs[0]);
        this.maxknob = $(knobs[1]);
        this.previousStep = -1;
        this.previousMaxStep = -1;

        this.knobWidth = this.knob['offsetWidth'];
        this.minPixelValue = 0;
        this.maxPixelValue = this.element['offsetWidth'] - this.knobWidth;
        this.minValue = $chk(this.options.range) ? this.options.range[0] : 0;
        this.maxValue = $chk(this.options.range) ? this.options.range.getLast() : this.options.steps;
        this.distinctValues = $chk(this.options.range) ? this.options.range.length > 2 : false;
        this.intervalLength = (this.maxValue - this.minValue);
        this.intervalPixelLength = (this.maxPixelValue - this.minPixelValue - this.knobWidth - 1);

        this.step = this.minValue;
        this.maxstep = this.maxValue;

        this.knob.setStyles({
            'position': 'absolute',
            'left': 0
        });
        this.maxknob.setStyles({
            'position': 'absolute',
            'left': this.maxPixelValue
        });

        var mod, offset;
        mod = { 'x': 'left', 'y': false };

        var lim = {};
        lim['x'] = [0, this.maxPixelValue - this.knobWidth];
        this.drag = new Drag(this.knob, {
            limit: lim,
            modifiers: mod,
            snap: 0,
            onStart: function() {
                this.setLimits(false);
                this.draggedKnob(false);
                this.fireEvent('dragStart');
            } .bind(this),
            onDrag: function() {
                this.draggedKnob(false);
            } .bind(this),
            onComplete: function() {
                this.draggedKnob(false);
                this.end(false);
                this.fireEvent('dragComplete', [this.getLowestValue(), this.getHighestValue()]);
            } .bind(this)
        });
        var maxLim = {};
        maxLim['x'] = [this.knobWidth , this.maxPixelValue];
        this.maxdrag = new Drag(this.maxknob, {
            limit: maxLim,
            modifiers: mod,
            snap: 0,
            onStart: function() {
                this.setLimits(true);
                this.draggedKnob(true);
                this.fireEvent('dragStart');
            } .bind(this),
            onDrag: function() {
                this.draggedKnob(true);
            } .bind(this),
            onComplete: function() {
                this.draggedKnob(true);
                this.end(true);
                this.fireEvent('dragComplete', [this.getLowestValue(), this.getHighestValue()]);
            } .bind(this)
        });

        if (this.distinctValues)
            this.initializeDistinctValues();
        if(this.options.initValues) {
            this.initializeValues();
        }
    },

    initializeValues: function() {
        this.setValue(this.options.initValues[0], false);
        this.setValue(this.options.initValues[1], true);
    },

    setValue: function(value, isMaxKnob) {
        if(isMaxKnob) {
            this.maxstep = value;
            this.fireEvent('onMaxTick', this.toPosition(this.maxstep));
        }
        else {
            this.step = value;
            this.fireEvent('onTick', this.toPosition(this.step));
        }
    },

    getCriteriaID: function() {
        return this.element.get('criteriaId');
    },

    unLoad: function() {
        this.drag.detach();
        this.maxdrag.detach();
    },

    setLimits: function(isMaxKnob) {
        if (isMaxKnob) {
            var lowerLimit = this.drag.value.now['x'];
            if ($defined(lowerLimit))
                this.maxdrag.limit['x'][0] = lowerLimit + this.knobWidth;
        } else {
            var upperLimit = this.maxdrag.value.now['x'];
            if ($defined(upperLimit))
                this.drag.limit['x'][1] = upperLimit - this.knobWidth;
        }
    },

    /*
    Will create "marker elements", and make the slider snap to the allowed values
    when dragging is complete
    */
    initializeDistinctValues: function() {
        this.options.range.each(function(currentValue) {
            var leftValue = this.toPosition(currentValue);
            var el = new Element('div', {
                'class': 'i',
                'styles': {
                    'position': 'absolute',
                    'left': leftValue
                }
            });
            el.inject(this.element);
        }, this);
    },

    //Called when a knob (min or maxknob) has been dragged
    draggedKnob: function(isMaxKnob) {
        if (isMaxKnob) {
            var value = this.maxdrag.value.now['x'];
            this.maxstep = this.toValue(value, true);
            this.checkStep(true);
        }
        else {
            var value = this.drag.value.now['x'];
            this.step = this.toValue(value, false);
            this.checkStep(false);
        }
    },

    //Called when min or maxknob has been dragged. If step/maxstep has changed - onChange is fired.
    checkStep: function(isMaxKnob) {
        var changed = false;
        if (isMaxKnob) {
            if (this.maxstep != this.previousMaxStep) {
                this.previousMaxStep = this.maxstep;
                changed = true;
            }
        }
        else {
            if (this.step != this.previousStep) {
                this.previousStep = this.step;
                changed = true;
            }
        }
        if(changed)
            this.fireEvent('change', [this.step, this.maxstep]);
    },

    /*
    Will return a value given a pixel position.
    */
    toValue: function(pixelPosition, isMaxKnob) {
        if(!isMaxKnob) pixelPosition += this.knobWidth;
        var value = ( (pixelPosition-this.knobWidth) / this.intervalPixelLength) * this.intervalLength + this.minValue;
        return this.distinctValues ? this.getClosestStep(value) : value;
    },

    //Will return a pixel value along the x axis from a value
    toPosition: function(step) {
        return this.knobWidth + Math.round((step-this.minValue) / this.intervalLength * this.intervalPixelLength) + this.minPixelValue;
    },

    getClosestStep: function(step) {
        return this.options.range.findMin(function(item) {
            return Math.abs(item-step);
        }, true);
    },

    isMaxedOut: function() {
        return (this.getHighestValue() == this.maxValue && this.getLowestValue() == this.minValue);
    },

    getLowestValue: function() {
        return Math.min(this.step, this.maxstep);
    },

    getHighestValue: function() {
        return Math.max(this.step, this.maxstep);
    },

    //Called after a drag is completed
    end: function(isMaxDrag) {
        if (this.distinctValues) {
            if(isMaxDrag)
                this.fireEvent('onMaxTick', this.toPosition(this.maxstep));
            else
                this.fireEvent('onTick', this.toPosition(this.step));
        }

        if (this.step < this.maxstep)
            this.fireEvent('onComplete', { minpos: this.step + '', maxpos: this.maxstep + '' });
        else
            this.fireEvent('onComplete', { minpos: this.maxstep + '', maxpos: this.step + '' });

    }
});

var SFSearchHelper = new Class({

    Implements: [Options, Events],
    options: {
        onSearchStart: $empty,
        onSearchComplete: $empty,
        searchInit: null
    },

    initialize: function(wrapperEl, options) {
        this.setOptions(options);
        this.wrapperEl = wrapperEl;
        var self = this;
        this.sliders = wrapperEl.getElements('.slider').map(function(sliderEl) {

            var knobs = sliderEl.getElements('div');
            var sliderParent = sliderEl.getParent();
            var textEls = [sliderParent.getElement('span.min'), sliderParent.getElement('span.max')];
            var dropDowns = sliderParent.getElements('select');
            var sliderValues = $A(dropDowns[0].options).map(function(option) {
                return option.value.toFloat();
            });
            var criteriaId = sliderEl.get('criteriaId');
            return new SFSlider(sliderEl, knobs, {
                range: sliderValues,
                initValues: [
                    textEls[0].get('text').toFloat(),
                    textEls[1].get('text').toFloat()
                ],
                onChange: function(min, max) {
                    textEls[0].set('text', min);
                    textEls[1].set('text', max);
                },
                onDragStart: function() {
                    self.fireEvent('searchStart');
                },
                onDragComplete: function(min, max) {
                    self.selectValue(dropDowns[0], min);
                    self.selectValue(dropDowns[1], max);
                    self.performSearch();
                }
            });
        }, this);
        $$('.serviceCriteria label', '.booleanCriteria label').each(function(labelEl) {
            labelEl.addEvent('click', function() { self.performSearch.delay(100, self) });
        });
    },
    
    selectValue: function(dropDownList, value) {
        var selectIndex = -1;
        $A(dropDownList.options).each(function(option, index) {
            if(option.value==value) selectIndex = index;
        });
        dropDownList.selectedIndex = selectIndex;
    },
    
    performSearch: function() {
        var self = this;
        fireEvent('searchStart');
        var queryObj = {
            AreaCode: areaCode,
            ServiceCriterias: this.getBoolCriteriaArray($$('.serviceCriteria input:checked')),
            BoolCriterias: this.getBoolCriterias(),
            NumberCriterias: this.getNumberCriterias(),
            OfferCategories: this.options.searchInit.offerCategories,
            ProviderID: this.options.searchInit.providerID,
            ProductIDList: this.options.searchInit.productIDList
        }
        new Request.JSON({
            data: 'json=' + JSON.encode(queryObj),
            url: '/ajaxProxies/searchOffers.ashx',
            onSuccess: function(jsonResponse) {
                this.fireEvent('searchComplete', jsonResponse);
            }.bind(this)
        }).post();
    },

    getBoolCriterias: function() {
        //1. Get all checked bool criterias,
        //2. Filter out elements whose corresponding service is unchecked
        //3. Add general boolcriterias
        return this.getBoolCriteriaArray($$('.servicePropertyCriterias .booleanCriteria input:checked')
            .filter(function(checkBox) {
                return this.getCorrespondingServiceCheckBox(checkBox).checked;
            }, this))
                .extend(this.getBoolCriteriaArray($$('.generalCriterias .booleanCriteria input:checked')));
    },

    getNumberCriterias: function() {
        return this.sliders.filter(function(slider) {
                var serviceCheckBox = this.getCorrespondingServiceCheckBox(slider.element);
                return (!slider.isMaxedOut() && (serviceCheckBox==null || serviceCheckBox.checked))
            }, this).map(function(slider) {
                return {
                    CriteriaID: slider.getCriteriaID(),
                    LowerLimit: slider.getLowestValue(),
                    UpperLimit: slider.getHighestValue()
                };
            });
    },

    getCorrespondingServiceCheckBox: function(element) {
        var servicePropCriteriaEl = element.getParent('.servicePropertyCriterias');
        if(!$defined(servicePropCriteriaEl)) return //Not belonging to service (general criteria)
        return servicePropCriteriaEl.getPrevious('.serviceCriteria').getElement('input');
    },

    getBoolCriteriaArray: function(inputElements) {
        return inputElements.map(function(inputEl) {
            return {
                CriteriaID: inputEl.getParent().get('criteriaId')
            };
        })
    }
});

