﻿var PriceGraph = Class.create(
{
    initialize: function(orig, dest, oneway, departYear, departMonth, departDay, returnYear, returnMonth, returnDay, options)
    {

        this.useJSON = true;

        this.orig = orig;
        this.dest = dest;
        this.oneway = oneway;
        this.departYear = departYear;
        this.departMonth = departMonth;
        this.departDay = departDay;
        this.returnYear = returnYear;
        this.returnMonth = returnMonth;
        this.returnDay = returnDay;
        this.mode = 'chart';

        this.visible = true;

        this.dataDepart = {};
        this.dataReturn = {};
        this.baseDepart = {};
        this.baseReturn = {};
        this.unboundDepart = {};
        this.unboundReturn = {};
        this.boundDepart = {};
        this.boundReturn = {};
        this.bound = {};

        this.options = options;

        if (typeof options.mode != 'undefined')
            this.mode = options.mode;

        if (typeof options.useJSON != 'undefined')
            this.useJSON = options.useJSON;

        if (options.onDataLoaded)
            this.onDataLoaded = options.onDataLoaded;

        if (options.onDisplayFlight)
            this.onDisplayFlight = options.onDisplayFlight;

        if (options.onSelectDepart)
            this.onSelectDepart = options.onSelectDepart;

        if (options.onSelectReturn)
            this.onSelectReturn = options.onSelectReturn;

        if (options.onMouseOverDepart)
            this.onMouseOverDepart = options.onMouseOverDepart;

        if (options.onMouseOverReturn)
            this.onMouseOverReturn = options.onMouseOverReturn;

        this.useRelativeImgRefs = typeof options.useRelativeImgRefs != 'undefined' ? options.useRelativeImgRefs : true;
        this.imagePathPrefix = this.useRelativeImgRefs ? '' : 'http://www.momondo.com';
        
        var dn = Sys.CultureInfo.CurrentCulture.dateTimeFormat.DayNames;
        this.dayNames = dn;
        var sn = [];
        for (var i = 0; i < dn.length; i++)
            sn[i] = dn[i].substr(0, 1);
        this.shortDayNames = sn;

        this.initContainers();

        this.departCalendar = new MomondoCalendar(this.containers.departCalendar, {
            date: new Date(this.departYear, this.departMonth - 1, this.departDay),
            //onMouseOver: this.departCalendarMouseOver.bind(this),
            onSelect: this.departCalendarSelect.bind(this)
        });

        this.returnCalendar = new MomondoCalendar(this.containers.returnCalendar, {
            date: new Date(this.returnYear, this.returnMonth - 1, this.returnDay),
            //onMouseOver: this.returnCalendarMouseOver.bind(this),
            onSelect: this.returnCalendarSelect.bind(this)
        });

        var chartTemplate = null;
        if (options.chartTemplate)
            chartTemplate = options.chartTemplate;

        this.departChart = new Chart(this.containers.departChart, {
            animation: true,
            chartTemplate: chartTemplate,
            onMouseOver: this.departChartMouseOver.bind(this),
            onSelect: this.departChartSelect.bind(this)
        });

        this.returnChart = new Chart(this.containers.returnChart, {
            animation: true,
            chartTemplate: chartTemplate,
            onMouseOver: this.returnChartMouseOver.bind(this),
            onSelect: this.returnChartSelect.bind(this)
        });

        this.departMonthSelector = new MonthSelector(this.containers.departMonth, {
            year: this.departYear,
            month: this.departMonth,
            prefixText: Language['FlightResults_LabelDeparture.Text'] + ' - ',
            suffixText: '<img src="' + this.imagePathPrefix + '/themes/momondo/images/flight/arrow_down.png" class="arrow" />',
            onChange: this.departMonthChange.bind(this)
        });

        this.returnMonthSelector = new MonthSelector(this.containers.returnMonth, {
            year: this.returnYear,
            month: this.returnMonth,
            prefixText: Language['FlightResults_LabelReturn.Text'] + ' - ',
            suffixText: '<img src="' + this.imagePathPrefix + '/themes/momondo/images/flight/arrow_down.png" class="arrow" />',
            onChange: this.returnMonthChange.bind(this)
        });
    },

    initContainers: function()
    {
        var containers = {
            departCalendar: $(this.options.departCalendarId),
            departChart: $(this.options.departChartId),
            departDetails: $(this.options.departDetailsId),
            returnCalendar: $(this.options.returnCalendarId),
            returnChart: $(this.options.returnChartId),
            returnDetails: $(this.options.returnDetailsId),
            departMonth: $(this.options.departMonthId),
            returnMonth: $(this.options.returnMonthId),
            totalDetails: $(this.options.totalDetailsId),
            loading: $(this.options.chartLoadingId)
        };
        this.containers = containers;
        if (!containers.departChart)
            alert('departChartId not found');
        if (!containers.returnChart)
            alert('returnChartId not found');
        if (containers.loading)
        {
            containers.loading.setOpacity(0.85);
            containers.loading.hide();
        }
    },

    search: function(orig, dest, oneway, departYear, departMonth, departDay, returnYear, returnMonth, returnDay)
    {
        if (orig)
            this.orig = orig;

        if (dest)
            this.dest = dest;

        if (typeof oneway != 'undefined' && oneway != null);
        this.oneway = oneway;

        if (departYear && departMonth)
        {
            this.departYear = departYear;
            this.departMonth = departMonth;
            this.departMonthSelector.set(departYear, departMonth);
            if (departDay)
                this.departDay = departDay;
        }

        if (returnYear && returnMonth)
        {
            this.returnYear = returnYear;
            this.returnMonth = returnMonth;
            this.returnMonthSelector.set(returnYear, returnMonth);
            if (returnDay)
                this.returnDay = returnDay;
        }

        this.getData();
    },

    showLoading: function()
    {
        if (!this.loadingTimer && !this.renderingDisabled && this.visible)
        {
            var t = this;
            this.loadingTimer = (function()
            {
                t.containers.loading.show();
                t.loadingTimer = null;
            }).delay(0.3);
        }
    },

    hideLoading: function()
    {
        if (this.loadingTimer)
        {
            clearTimeout(this.loadingTimer);
            this.loadingTimer = null;
        }

        this.containers.loading.hide();
    },

    getData: function()
    {
        this.showLoading();
        this.dataReady = false;

        if (this.orig && this.orig.length == 3 && this.dest && this.dest.length == 3)
        {
            Momondo.GetPriceGraphData(this.orig, this.dest, this.oneway, this.departYear, this.departMonth, this.returnYear, this.returnMonth, this.getDataSuccess.bind(this), this.getDataFailure.bind(this));
        }
        else
        {
            this.setData({ d: { u: [], b: [] }, i: { u: [], b: []} });
        }
    },

    getDataSuccess: function(data)
    {
        this.setData(this.evalData(data));
    },

    setData: function(data)
    {
        this.data = data;
        this.dataReady = true;

        this.hideLoading();

        if (this.onDataLoaded)
            this.onDataLoaded(data);

        if (!this.renderingDisabled)
        {
            this.render();
        }
    },

    disableRendering: function(value)
    {
        if (this.renderingDisabled)
            this.renderingDisabled += value ? 1 : -1;
        else
            this.renderingDisabled = value ? 1 : 0;
    },

    render: function()
    {
        this.renderingDisabled = 0;

        if (this.data.d.u.length == 0 && this.data.d.b.length == 0)
            this.includeIndirect = true;
        this.indexIndirect();
        this.update();
    },

    update: function()
    {
        if (!this.data)
            return;

        this.calc();
        this.calcReturn();
        this.draw();
    },

    getDataFailure: function()
    {
        this.hideLoading();
        // alert('failed to retrieve price graph data!');
    },

    evalData: function(data)
    {
        var result = null;
        if (data && data.length > 0)
        {
            if (this.useJSON)
            {
                try
                {
                    if (JSON && JSON.parse)
                        result = JSON.parse(data);
                }
                catch (e)
                {
                    this.useJSON = false;
                }
            }

            if (result == null)
                result = eval(data);
        }
        return result;
    },

    best: function(fl1, fl2)
    {
        if (fl1 == null)
            return fl2;
        if (fl2 == null)
            return fl1;
        if (fl2.p < fl1.p)
            return fl2;
        return fl1;
    },

    bestCombi: function(combis)
    {
        var best = null;
        var bestP = 999999;
        for (var i = 0; i < combis.length; i++)
        {
            var combi = combis[i];
            var dep = combi[0];
            var ret = combi[1];
            if (dep && ret)
            {
                var p = dep.p + ret.p;
                if (p < bestP)
                {
                    best = combi;
                    bestP = p;
                }
            }
        }
        if (best)
            return { dep: best[0], ret: best[1] };
        else
            return null;
    },

    addIndirect: function()
    {
        this.includeIndirect = true;
        this.update();
    },

    removeIndirect: function()
    {
        this.includeIndirect = false;
        this.update();
    },

    indexIndirect: function()
    {
        var hash = {};
        var indirect = this.data.i;
        for (var i = 0; i < indirect.u.length; i++)
        {
            var fl = indirect.u[i];
            if (fl.hb == 0)
            {
                hash[fl.dd + ':' + fl.hb] = fl;
            }
        }
        for (var i = 0; i < indirect.b.length; i++)
        {
            var fl = indirect.b[i];
            hash[fl.dd + '-' + fl.cd] = fl;
        }
        this.indirect = hash;
    },

    compareIndirect: function(fl)
    {
        if (this.includeIndirect)
        {
            var ifl = null;
            if (fl.cd == 0)
                ifl = this.indirect[fl.dd + ':' + fl.hb];
            else
                ifl = this.indirect[fl.dd + '-' + fl.cd];
            if (ifl)
                return ifl;
        }
        return fl;
    },

    fillData: function(data, days, homebound)
    {
        var prev = null;
        for (var i = days; i > 0; i--)
        {
            var d = data[i];
            if (!d && prev)
            {
                d = this.makeUnboundCopy(prev, homebound);
                d.dd = i;
                d.cd = 0;
                d.cl = 'filler estimated';
                data[i] = d;
            }
            else if (d)
            {
                prev = d;
            }
        }

        prev = null;
        for (var i = 1; i <= days; i++)
        {
            var d = data[i];
            if (!d && prev)
            {
                d = this.makeUnboundCopy(prev, homebound);
                d.dd = i;
                d.cd = 0;
                d.cl = 'filler estimated';
                data[i] = d;
            }
            else if (d)
            {
                prev = d;
            }
        }

        return data;
    },

    getBaseData: function()
    {
        var direct = this.data.d;
        var indirect = this.data.i;

        var unbound = new Hash();
        var bound = new Hash();

        for (var i = 0; i < direct.u.length; i++)
        {
            var fl = direct.u[i];
            var key = fl.dd + ':' + fl.hb;
            unbound.set(key, fl);
        }

        for (var i = 0; i < direct.b.length; i++)
        {
            var fl = direct.b[i];
            var key = fl.dd + '-' + fl.cd;
            bound.set(key, fl);
        }

        if (this.indexIndirect)
        {
            for (var i = 0; i < indirect.u.length; i++)
            {
                var fl = indirect.u[i];
                var key = fl.dd + ':' + fl.hb;
                unbound.set(key, fl);
            }

            for (var i = 0; i < indirect.b.length; i++)
            {
                var fl = indirect.b[i];
                var key = fl.dd + '-' + fl.cd;
                bound.set(key, fl);
            }
        }

        return { u: unbound.values(), b: bound.values() };
    },

    calc: function()
    {
        var baseDepart = {};
        var baseReturn = null;
        var unboundDepart = null;
        var unboundReturn = null;
        var boundDepart = null;
        var boundReturn = null;
        var bound = null;

        var baseData = this.getBaseData();

        var maxPrice = 0;
        var totalPrice = 0;
        var totalCount = 0;

        if (this.oneway)
        {
            unboundDepart = {};
            var data = baseData.u;
            for (var i = 0; i < data.length; i++)
            {
                var fl = data[i];
                if (fl.hb == 0)
                {
                    unboundDepart[fl.dd] = fl;
                    var p = fl.p;
                    fl.pc = p;  // reset calculated price
                    if (p > maxPrice)
                        maxPrice = p;

                    totalPrice += p;
                }
            }
            baseDepart = Object.clone(unboundDepart);
            totalCount = data.length;
        }
        else
        {
            baseReturn = {};
            unboundDepart = {};
            unboundReturn = {};
            boundDepart = {};
            boundReturn = {};
            bound = {};

            var data = baseData.u;
            for (var i = 0; i < data.length; i++)
            {
                var fl = data[i];
                var p = fl.p;
                fl.pc = p; // reset calculated price
                if (p > maxPrice)
                    maxPrice = p;

                totalPrice += p;

                var day = fl.dd;
                if (fl.hb == 0)
                    unboundDepart[day] = fl;
                else
                    unboundReturn[day] = fl;
            }

            totalCount += data.length;

            baseDepart = Object.clone(unboundDepart);
            baseReturn = Object.clone(unboundReturn);

            data = baseData.b;
            for (var i = 0; i < data.length; i++)
            {
                var fl = data[i];
                var p = fl.p;
                fl.pc = p; // reset calculated price
                if (p > maxPrice)
                    maxPrice = p;

                totalPrice += p;

                var key = fl.dd + '-' + fl.cd;
                bound[key] = fl;

                baseDepart[fl.dd] = this.best(baseDepart[fl.dd], fl);
                baseReturn[fl.cd] = this.best(baseReturn[fl.cd], fl);
                boundDepart[fl.dd] = this.best(boundDepart[fl.dd], fl);
                boundReturn[fl.cd] = this.best(boundReturn[fl.cd], fl);
            }

            totalCount += data.length;
        }

        this.baseDepart = baseDepart;
        this.baseReturn = baseReturn;
        this.unboundDepart = unboundDepart;
        this.unboundReturn = unboundReturn;
        this.boundDepart = boundDepart;
        this.boundReturn = boundReturn;
        this.bound = bound;

        this.maxPrice = maxPrice;
        this.avgPrice = totalCount > 0 ? Math.ceil(totalPrice / (1.0 * totalCount)) : this.maxPrice;

        this.dataReturn = null;

        var dataDepart = Object.clone(baseDepart);
        var daysInMonth = Date.daysInMonth(this.departMonth - 1, this.departYear);
        this.dataDepart = this.fillData(dataDepart, daysInMonth);
    },

    calcReturn: function()
    {
        if (!this.oneway)
        {
            dataReturn = {};

            var departDay = this.departDay;
            if (departDay == 0)
            {
                dataReturn = Object.clone(this.baseReturn);
            }
            else
            {
                var daysInMonth = Date.daysInMonth(this.returnMonth - 1, this.returnYear);

                var flDep = this.baseDepart[departDay];
                var flDepU = this.unboundDepart[departDay];

                if (flDep)
                {
                    for (var d = 1; d <= daysInMonth; d++)
                    {
                        var fl = null;

                        var flRetU = null;
                        var priceU = 99999999;

                        if (flDepU)
                        {
                            flRetU = this.unboundReturn[d];
                            if (flRetU)
                            {
                                priceU = flDepU.p + flRetU.p;
                            }
                        }

                        var key = departDay + '-' + d;
                        flBound = this.bound[key];

                        if (flBound && flBound.pt < priceU)
                        {
                            fl = flBound;
                            fl.pc = flBound.pt - flDep.p;
                        }
                        else if (flRetU)
                        {
                            fl = flRetU;
                            fl.pc = priceU.p - flDep.p;
                        }

                        if (!fl)
                        {
                            var up = this.unboundReturn[d];
                            var bp = this.boundReturn[d];
                            fl = this.best(up, bp);
                            if (fl)
                            {
                                fl.pc = (fl.p * 2) - flDep.p;
                                fl.cl = 'estimated';
                            }
                        }

                        dataReturn[d] = fl;
                    }
                }
                else
                {
                    dataReturn = Object.clone(this.baseReturn);
                }
            }

            if (this.departMonth == this.returnMonth)
            {
                for (var d = 1; d < this.departDay; d++)
                    dataReturn[d] = null;
            }

            var daysInMonth = Date.daysInMonth(this.returnMonth - 1, this.returnYear);
            this.dataReturn = this.fillData(dataReturn, daysInMonth);
        }
    },

    setMode: function(mode, redraw)
    {
        if (typeof redraw == 'undefined')
            redraw = true;
        this.mode = mode;
        if (redraw)
            this.draw();
    },

    draw: function()
    {
        this.drawDepart();
        if (!this.oneway)
            this.drawReturn();

        this.display();
    },

    drawDepart: function()
    {
        if (this.mode == 'calendar')
            this.drawDepartCalendar();
        else
            this.drawDepartChart();
    },

    drawReturn: function()
    {
        if (this.mode == 'calendar')
            this.drawReturnCalendar();
        else
            this.drawReturnChart();
    },

    drawDepartCalendar: function()
    {
        var dt = new Date(this.departYear, this.departMonth - 1, this.departDay);

        var cal = this.departCalendar;
        cal.year = this.departYear;
        cal.month = this.departMonth;
        cal.date = dt;

        var data = {};
        var daysInMonth = dt.daysInMonth();

        for (var d = 1; d <= daysInMonth; d++)
        {
            var fl = this.dataDepart[d];
            if (fl)
            {
                data[d] = { label: GCurrency.displayPrice(fl.p) };
            }
        }

        cal.data = data;
        cal.render();
    },

    drawReturnCalendar: function()
    {
        var dt = new Date(this.returnYear, this.returnMonth - 1, this.returnDay);

        var cal = this.returnCalendar;
        cal.year = this.returnYear;
        cal.month = this.returnMonth;
        cal.date = dt;

        var depDate = null;
        if (this.departDay > 0)
            depDate = new Date(this.departYear, this.departMonth - 1, this.departDay);
        cal.minDate = depDate;

        var data = {};
        var daysInMonth = dt.daysInMonth();

        for (var d = 1; d <= daysInMonth; d++)
        {
            var fl = this.dataReturn[d];
            if (fl)
            {
                data[d] = { label: GCurrency.displayPrice(fl.p) };
            }
        }

        cal.data = data;
        cal.render();
    },

    drawDepartChart: function()
    {
        var daysInMonth = Date.daysInMonth(this.departMonth - 1, this.departYear);

        var today = new Date();
        var year = today.getFullYear();
        today = new Date(year, today.getMonth(), today.getDate());

        var series = [];
        var dayNames = this.shortDayNames;

        for (var d = 1; d <= daysInMonth; d++)
        {
            var dt = new Date(this.departYear, this.departMonth - 1, d);

            var dw = dt.getDay();
            var pt = { y: 0, x: d, ex: dayNames[dw] };

            var cl = '';
            if (dw == 0 || dw == 6)
                cl = 'weekend ';

            if (dt < today)
            {
                cl += 'past ';
                pt.unselectable = true;
            }
            else
            {
                var fl = this.dataDepart[d];
                if (fl)
                {
                    pt.y = fl.p;
                    if (fl.cl)
                        cl += fl.cl;
                }
            }

            pt.cl = cl;
            pt.y = this.convertPrice(pt.y);
            series[d - 1] = pt;
        }

        this.drawChart(this.departChart, series);
        if (this.departDay)
            this.departChart.select(this.departDay - 1);
    },

    drawReturnChart: function()
    {
        var daysInMonth = Date.daysInMonth(this.returnMonth - 1, this.returnYear);

        var today = new Date();
        var year = today.getFullYear();
        today = new Date(year, today.getMonth(), today.getDate());

        var series = [];
        var dayNames = this.shortDayNames;

        for (var d = 1; d <= daysInMonth; d++)
        {
            var dt = new Date(this.returnYear, this.returnMonth - 1, d);

            var dw = dt.getDay();
            var pt = { y: 0, x: d, ex: dayNames[dw] };

            var cl = '';
            if (dw == 0 || dw == 6)
                cl = 'weekend ';

            if (this.departMonth == this.returnMonth && d < this.departDay)
            {
                cl += 'past beforedepart ';
                pt.unselectable = true;
            }
            else
            {
                if (dt < today)
                {
                    cl += 'past';
                    pt.unselectable = true;
                }
                else
                {
                    var fl = this.dataReturn[d];
                    if (fl)
                    {
                        pt.y = fl.p;
                        if (fl.cl)
                            cl += fl.cl;
                    }
                }
            }

            pt.cl = cl;
            pt.y = this.convertPrice(pt.y);
            series[d - 1] = pt;
        }

        this.drawChart(this.returnChart, series);
        if (this.returnDay)
            this.returnChart.select(this.returnDay - 1);
    },

    drawChart: function(chart, series)
    {
        if (chart)
        {
            var opts = {};
            /*
            if (this.avgPrice)
            opts.maxValue = this.convertPrice(this.avgPrice * 2);
            else if (this.maxPrice)
            opts.maxValue = this.convertPrice(this.maxPrice);
            */
            if (this.maxPrice)
                opts.maxValue = this.convertPrice(this.maxPrice);
            chart.draw(series, opts);
        }
    },

    getSelectedDay: function(chart)
    {
        if (chart)
        {
            var sel = chart.selectedIndex;
            if (sel >= 0)
            {
                return sel + 1;
            }
        }
        return 0;
    },

    departCalendarMouseOver: function()
    {
    },

    returnCalendarMouseOver: function()
    {
    },

    departChartMouseOver: function(index)
    {
        if (this.onMouseOverDepart && this.dataDepart)
        {
            var d = null;
            var fl = null;
            if (index >= 0)
            {
                var day = index + 1;
                d = new Date(this.departYear, this.departMonth - 1, this.departDay);
                fl = this.dataDepart[day];
            }
            this.onMouseOverDepart(d, fl);
        }
    },

    returnChartMouseOver: function(index)
    {
        if (this.onMouseOverReturn && this.dataReturn)
        {
            var d = null;
            var fl = null;
            if (index > 0)
            {
                var day = index + 1;
                d = new Date(this.returnYear, this.returnMonth - 1, this.returnDay);
                fl = this.dataReturn[day];
            }
            this.onMouseOverReturn(d, fl);
        }
    },

    departChartSelect: function()
    {
        var day = this.getSelectedDay(this.departChart);
        this.departDaySelect(day);
    },

    returnChartSelect: function()
    {
        var day = this.getSelectedDay(this.returnChart);
        this.returnDaySelect(day);
    },

    departCalendarSelect: function()
    {
        var dt = this.departCalendar.date;
        var day = dt.getDate();
        this.departDaySelect(day);
    },

    returnCalendarSelect: function()
    {
        var dt = this.returnCalendar.date;
        var day = dt.getDate();
        this.returnDaySelect(day);
    },

    departDaySelect: function(day)
    {
        this.departDay = day;
        this.notifySelectDepart();
        if (!this.oneway)
        {
            this.calcReturn();
            this.drawReturn();
        }
        this.display();
    },

    returnDaySelect: function(day)
    {
        this.returnDay = day;
        this.notifySelectReturn();
        this.display();
    },

    departMonthChange: function(year, month)
    {
        this.setDepartMonth(year, month, true);
    },

    returnMonthChange: function(year, month)
    {
        this.setReturnMonth(year, month, true);
    },

    setDepartMonth: function(year, month, notify)
    {
        this.departYear = year;
        this.departMonth = month;
        var daysInMonth = Date.daysInMonth(month - 1, year);
        if (this.departDay > daysInMonth)
            this.departDay = daysInMonth;

        this.departCalendar.setMonth(year, month);

        if (notify)
            this.notifySelectDepart();

        this.getData();
    },

    setReturnMonth: function(year, month, notify)
    {
        this.returnYear = year;
        this.returnMonth = month;
        var daysInMonth = Date.daysInMonth(month - 1, year);
        if (this.returnDay > daysInMonth)
            this.returnDay = daysInMonth;

        this.returnCalendar.setMonth(year, month);

        if (notify)
            this.notifySelectReturn();

        this.getData();
    },

    notifySelectDepart: function()
    {
        if (this.onSelectDepart)
        {
            var d = new Date(this.departYear, this.departMonth - 1, this.departDay);
            this.onSelectDepart(d);
        }
    },

    notifySelectReturn: function()
    {
        if (this.onSelectReturn)
        {
            var d = new Date(this.returnYear, this.returnMonth - 1, this.returnDay);
            this.onSelectReturn(d);
        }
    },

    setDepartDay: function(day)
    {
        this.departDay = day;
        this.departChart.select(day - 1);

        this.calcReturn();

        this.draw();
        this.display();
    },

    setReturnDay: function(day)
    {
        this.returnDay = day;
        this.returnChart.select(day - 1);

        this.draw();
        this.display();
    },

    clearSelection: function()
    {
        this.departDay = 0;
        this.returnDay = 0;
        this.update();
    },

    makeUnboundCopy: function(fl, homebound)
    {
        result = null;
        if (fl)
        {
            result = Object.clone(fl);
            if (fl.cd > 0)
            {
                // bound flight
                if (homebound)
                    result = { id: fl.id, o: fl.o, d: fl.d, hb: 1, dd: fl.cd, cd: fl.dd, sid: fl.sid, di: fl.hd, s: fl.hs, v: fl.hv, p: fl.p, qt: fl.qt };
                else
                    result = { id: fl.id, o: fl.o, d: fl.d, hb: 0, dd: fl.dd, cd: fl.cd, sid: fl.sid, di: fl.od, s: fl.os, v: fl.ov, p: fl.p, qt: fl.qt };
            }
        }
        return result;
    },

    display: function()
    {
        var dep = null;
        var ret = null;

        var departDay = this.departDay;
        if (departDay > 0)
        {
            dep = this.dataDepart[departDay];
        }

        if (!this.oneway)
        {
            var returnDay = this.returnDay;
            if (returnDay > 0)
            {
                ret = this.dataReturn[returnDay];
            }

            if (departDay && returnDay)
            {
                var key = departDay + '-' + returnDay;
                var b = this.bound[key];

                // try "real" fares first
                var combinations = [
                    [this.unboundDepart[departDay], this.unboundReturn[returnDay]],
                    [b, b]
                ];
                var combi = this.bestCombi(combinations);

                if (!combi)
                {
                    // if none found, then try "estimated" fares
                    combinations = [
                        [this.unboundDepart[departDay], this.boundReturn[returnDay]],
                        [this.boundDepart[departDay], this.unboundReturn[returnDay]]
                    ];
                    combi = this.bestCombi(combinations);
                }

                if (combi)
                {
                    dep = combi.dep;
                    ret = combi.ret;
                }
            }
        }

        dep = this.makeUnboundCopy(dep, false);
        ret = this.makeUnboundCopy(ret, true);

        this.internalDisplayFlight(dep, ret);

        if (this.onDisplayFlight)
            this.onDisplayFlight(dep, ret);
    },

    getElement: function(e, selector)
    {
        return e.select(selector)[0];
    },

    setElement: function(e, selector, content)
    {
        var p = e.select(selector)[0];
        if (p)
        {
            if (!content)
                content = '&nbsp;';
            p.update(content);
        }
    },

    internalDisplayFlight: function(dep, ret)
    {
        var c = this.containers.departDetails;
        if (c)
        {
            var d = new Date(this.departYear, this.departMonth - 1, this.departDay);
            this.setElement(c, '.date', d.format('d. MMM yyyy'));

            if (dep)
            {
                var ago = '';
                var dt = Date.momondoParse(dep.qt);
                if (dt)
                {
                    ago = dt.momondoGetRelativeDate();
                }

                this.setElement(c, '.route', dep.o + '/' + dep.d);
                this.setElement(c, '.stops', this.getStopOverText(dep));
                this.setElement(c, '.supplier', dep.sid);
                this.setElement(c, '.price', GCurrency.displayPrice(dep.p));
                this.setElement(c, '.quotetime', Language['PriceGraph_FlightInfo_QuoteTime'].replace('{0}', ago));
            }
            else
            {
                this.setElement(c, '.route', this.orig + '/' + this.dest);
                this.setElement(c, '.stops');
                this.setElement(c, '.supplier');
                this.setElement(c, '.quotetime');
                this.setElement(c, '.price');
            }
        }

        c = this.containers.returnDetails;
        if (c)
        {
            var d = new Date(this.returnYear, this.returnMonth - 1, this.returnDay);
            this.setElement(c, '.date', d.format('d. MMM yyyy'));

            if (ret)
            {
                var ago = '';
                var dt = Date.momondoParse(ret.qt);
                if (dt)
                {
                    ago = dt.momondoGetRelativeDate();
                }

                this.setElement(c, '.route', ret.d + '/' + ret.o);
                this.setElement(c, '.stops', this.getStopOverText(ret));
                this.setElement(c, '.supplier', ret.sid);
                this.setElement(c, '.price', GCurrency.displayPrice(ret.p));
                this.setElement(c, '.quotetime', Language['PriceGraph_FlightInfo_QuoteTime'].replace('{0}', ago));
            }
            else
            {
                this.setElement(c, '.route', this.dest + '/' + this.orig);
                this.setElement(c, '.stops');
                this.setElement(c, '.supplier');
                this.setElement(c, '.quotetime');
                this.setElement(c, '.price');
            }
        }

        var basedOn = '';
        if (this.oneway)
        {
            if (dep)
            {
                basedOn = Language['PriceGraph_BasedOn_SingleOneway'];
            }
        }
        else if (dep && ret)
        {
            if (dep.cd == 0 && ret.cd == 0)
            {
                basedOn = Language['PriceGraph_BasedOn_TwoOneways'];
            }
            else if (dep.cd == 0 || ret.cd == 0)
            {
                basedOn = Language['PriceGraph_BasedOn_Calculated'];
            }
            else
            {
                if (dep.dd == ret.cd && dep.cd == ret.dd)
                {
                    basedOn = Language['PriceGraph_BasedOn_SingleReturn'];
                }
                else
                {
                    basedOn = Language['PriceGraph_BasedOn_Calculated'];
                }
            }

            if (dep.sid != ret.sid)
            {
                basedOn += ' ' + Language['PriceGraph_BasedOn_MultiSuppliers'];
            }
        }

        c = this.containers.totalDetails;
        if (c)
        {
            if (this.oneway && dep)
            {
                this.setElement(c, '.text', Language['PriceGraph_Total_Caption']);
                this.setElement(c, '.price', GCurrency.displayPrice(dep.p, "{0}"));
                this.setElement(c, '.based-on', basedOn);
            }
            else if (dep && ret)
            {
                this.setElement(c, '.text', Language['PriceGraph_Total_Caption']);
                this.setElement(c, '.price', GCurrency.displayPrice(dep.p + ret.p, "{0}"));
                this.setElement(c, '.based-on', basedOn);
            }
            else
            {
                this.setElement(c, '.text');
                this.setElement(c, '.price');
                this.setElement(c, '.based-on');
            }
        }
    },

    getStopOverText: function(fl)
    {
        if (fl.s == 0)
            return Language['PriceGraph_FlightInfo_Direct'];
        else
        {
            var id = 'PriceGraph_FlightInfo_Stop';
            if (fl.s > 1)
                id += 's';
            if (fl.v)
                id += 'Via';
            return Language[id].replace('{0}', fl.s).replace('{1}', fl.v);
        }
    },

    convertPrice: function(p)
    {
        return GCurrency.convertFromEUR(p);
    }
});
