var util = require('util');
var polyline = require('polyline');

module.exports = function () {
    function add(a, b) {
        return a + b;
    }

    this.When(/^I plan a trip I should get$/, (table, callback) => {
        var got;

        this.reprocessAndLoadData((e) => {
            if (e) return callback(e);
            var testRow = (row, ri, cb) => {
                var afterRequest = (err, res) => {
                    if (err) return cb(err);
                    var headers = new Set(table.raw()[0]);

                    for (var k in row) {
                        var match = k.match(/param:(.*)/);
                        if (match) {
                            if (row[k] === '(nil)') {
                                params[match[1]] = null;
                            } else if (row[k]) {
                                params[match[1]] = [row[k]];
                            }
                            got[k] = row[k];
                        }
                    }

                    var json;
                    got.code = 'unknown';
                    if (res.body.length) {
                        json = JSON.parse(res.body);
                        got.code = json.code;
                    }

                    if (headers.has('status')) {
                        got.status = json.code;
                    }

                    if (headers.has('message')) {
                        got.message = json.message;
                    }

                    if (headers.has('data_version')) {
                        got.data_version = json.data_version || '';
                    }

                    if (headers.has('geometry')) {
                        if (this.queryParams['geometries'] === 'polyline') {
                            got.geometry = polyline.decode(json.trips[0].geometry).toString();
                        } else if (this.queryParams['geometries'] === 'polyline6') {
                            got.geometry = polyline.decode(json.trips[0].geometry, 6).toString();
                        } else {
                            got.geometry = json.trips[0].geometry.coordinates;
                        }
                    }

                    if (headers.has('#')) {
                        // comment column
                        got['#'] = row['#'];
                    }

                    var subTrips;
                    var trip_durations;
                    var trip_distance;
                    var ok = res.statusCode === 200;
                    if (ok) {
                        if (headers.has('trips')) {
                            subTrips = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map((sl, i) => {
                                var toAdd = [];
                                if (i === 0) toAdd.push(sl.steps[0].intersections[0].location);
                                toAdd.push(sl.steps[sl.steps.length-1].intersections[0].location);
                                return toAdd;
                            })));
                        }
                        if(headers.has('durations')) {
                            var all_durations = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map(sl => {
                                return sl.duration;
                            })));
                            trip_durations = all_durations.map( a => a.reduce(add, 0));
                        }
                        if(headers.has('distance')) {
                            var all_distance = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map(sl => {
                                return sl.distance;
                            })));
                            trip_distance = all_distance.map( a => a.reduce(add, 0));
                        }
                    }

                    var encodedResult = '';

                    if (json.trips && row.trips) row.trips.split(',').forEach((sub, si) => {
                        if (si >= subTrips.length) {
                            ok = false;
                        } else {
                            // TODO: Check all rotations of the round trip
                            for (var ni=0; ni<sub.length; ni++) {
                                var node = this.findNodeByName(sub[ni]),
                                    outNode = subTrips[si][ni];
                                if (this.FuzzyMatch.matchLocation(outNode, node)) {
                                    encodedResult += sub[ni];
                                } else {
                                    ok = false;
                                    encodedResult += util.format('? [%s,%s]', outNode[0], outNode[1]);
                                }
                            }
                        }
                    });

                    if (ok) {
                        got.trips = row.trips;
                        got.via_points = row.via_points;
                    } else {
                        got.trips = encodedResult;
                    }

                    got.durations = trip_durations;
                    got.distance = trip_distance;

                    for (var key in row) {
                        if (this.FuzzyMatch.match(got[key], row[key])) {
                            got[key] = row[key];
                        }
                    }

                    cb(null, got);
                };

                if (row.request) {
                    got.request = row.request;
                    this.requestUrl(row.request, afterRequest);
                } else {
                    var params = this.queryParams,
                        waypoints = [];
                    if (row.from && row.to) {
                        var fromNode = this.findNodeByName(row.from);
                        if (!fromNode) throw new Error(util.format('*** unknown from-node "%s"', row.from));
                        waypoints.push(fromNode);

                        var toNode = this.findNodeByName(row.to);
                        if (!toNode) throw new Error(util.format('*** unknown to-node "%s"', row.to));
                        waypoints.push(toNode);

                        got = { from: row.from, to: row.to };
                        this.requestTrip(waypoints, params, afterRequest);
                    } else if (row.waypoints) {
                        row.waypoints.split(',').forEach((n) => {
                            var node = this.findNodeByName(n);
                            if (!node) throw new Error(util.format('*** unknown waypoint node "%s"', n.trim()));
                            waypoints.push(node);
                        });
                        got = { waypoints: row.waypoints };

                        if (row.source) {
                            params.source = got.source = row.source;
                        }

                        if (row.destination) {
                            params.destination = got.destination = row.destination;
                        }

                        if (row.hasOwnProperty('roundtrip')) { //roundtrip is a boolean so row.roundtrip alone doesn't work as a check here
                            params.roundtrip = got.roundtrip = row.roundtrip;
                        }

                        this.requestTrip(waypoints, params, afterRequest);
                    } else {
                        throw new Error('*** no waypoints');
                    }
                }
            };

            this.processRowsAndDiff(table, testRow, callback);
        });
    });
};
