/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
/* vim: set et ts=4 sw=4: */
/*
 * Copyright (c) 2019 Marcus Lundblad
 *
 * GNOME Maps is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * GNOME Maps is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Marcus Lundblad <ml@update.uu.se>
 */

const Champlain = imports.gi.Champlain;
const GLib = imports.gi.GLib;
const Soup = imports.gi.Soup;

const Utils = imports.utils;

const BASE_URL = 'https://gis.gnome.org/services/aux';

var TransitTweaks = class {
    constructor(params) {
        this._name = params.name;
        this._session =
            new Soup.Session({ user_agent: 'gnome-maps/' + pkg.version });

        if (!this._name)
            throw new Error('Missing tweak name');
    }

    applyTweaks(itineraries, callback) {
        if (!this._tweaks) {
            let variable = 'TRANSIT_TWEAKS_' + this._name.toUpperCase();
            let filename = GLib.getenv(variable);

            if (filename) {
                this._readTweaksFromFile(filename);
                this._doApplyTweaks(itineraries, callback);
            } else {
                this._fetchTweaksAsync(itineraries, callback);
            }
        } else {
            this._doApplyTweaks(itineraries, callback);
        }
    }

    _doApplyTweaks(itineraries, callback) {
        if (Object.keys(this._tweaks).length !== 0) {
            itineraries.forEach((itinerary) =>
                this._applyTweaksToItinerary(itinerary));
        }

        callback();
    }

    _readTweaksFromFile(filename) {
        let data = Utils.readFile(filename);

        if (!data) {
            Utils.debug('Failed to read from tweak file');
            this._tweaks = {};
            callback();
        }

        try {
            this._tweaks = JSON.parse(Utils.getBufferText(data));
        } catch (e) {
            Utils.debug('Failed to parse tweaks: ' + e);
            this._tweaks = {};
        }
    }

    _fetchTweaksAsync(itineraries, callback) {
        let uri = new Soup.URI(BASE_URL + '/' + 'tweaks-' + this._name + '.json');
        let request = new Soup.Message({ method: 'GET', uri: uri });

        this._session.queue_message(request, (obj, message) => {
            if (message.status_code !== Soup.Status.OK) {
                Utils.debug('Failed to download tweaks');
                this._tweaks = {};
                callback();
            } else {
                try {
                    this._tweaks = JSON.parse(message.response_body.data);
                } catch (e) {
                    Utils.debug('Failed to parse tweaks: ' + e);
                    this._tweaks = {};
                }

                this._doApplyTweaks(itineraries, callback);
            }
        });
    }

    _applyTweaksToItinerary(itinerary) {
        itinerary.legs.forEach((leg) => {
            if (leg.transit)
                this._applyTweaksToLeg(leg);
        });
    }

    _applyTweaksToLeg(leg) {
        let agencyTweaks = this._tweaks.agencies[leg.agencyName];

        if (agencyTweaks) {
            let routeTypeTweaks = agencyTweaks.routeTypes[leg.routeType];

            if (routeTypeTweaks) {
                let tweakToApply;
                let bboxTweaks = routeTypeTweaks.bboxes;
                let routeTweaks = routeTypeTweaks.routes ?
                                  routeTypeTweaks.routes[leg.route] || null : null;
                let routePatternTweaks = routeTypeTweaks.routePatterns;

                // first check for boundingbox-specific tweaks
                if (bboxTweaks) {
                    bboxTweaks.forEach((tweak) => {
                        let bbox = tweak.bbox;
                        let cbbox = new Champlain.BoundingBox({ bottom: bbox[0],
                                                                left: bbox[1],
                                                                top: bbox[2],
                                                                right: bbox[3] });

                        if (cbbox.covers(leg.polyline[0].latitude,
                                         leg.polyline[0].longitude)) {
                            /* if boundingbox fits, use embedded route or
                             * route pattern tweaks
                             */
                            routeTweaks = tweak.routes ?
                                          tweak.routes[leg.route] : null;
                            routePatternTweaks = tweak.routePatterns;
                        }
                    });
                }

                if (routeTweaks) {
                    tweakToApply = routeTweaks;
                } else if (routePatternTweaks) {
                    routePatternTweaks.forEach((pattern) => {
                        if (!(pattern.regex instanceof RegExp)) {
                            pattern.regex = new RegExp(pattern.regex);
                        }

                        if (leg.route.match(pattern.regex))
                            tweakToApply = pattern;
                    });
                }

                if (!tweakToApply) {
                    tweakToApply = routeTypeTweaks;
                }

                this._applyRouteTweaksToLeg(leg, tweakToApply);
            }
        }
    }

    _applyRouteTweaksToLeg(leg, tweaks) {
        if (tweaks.route)
            leg.route = tweaks.route;

        if (tweaks.routeType)
            leg.routeType = tweaks.routeType;

        if (tweaks.color)
            leg.color = tweaks.color;

        if (tweaks.textColor)
            leg.textColor = tweaks.textColor;
    }
}
