// Amsterdump is a small program that populates a BigQuery table with
// a matrix of origin points scattered around Amsterdam and each
// respective points travel time to a given destination.
//
// The two destinations used here are the Schiphol Airport and
// Amsterdam Central station.
//
// To accomplish this the Google Maps Distance Matrix API [1] is
// queried with the points. A visualisation is later done using
// BigQuery GeoViz[2].
//
// [1]: https://developers.google.com/maps/documentation/distance-matrix/start#quotas
// [2]: https://bigquerygeoviz.appspot.com/
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"googlemaps.github.io/maps"
)

func failOn(err error, msg string) {
	if err != nil {
		log.Fatalln(msg, err)
	}
}

type LocationResult struct {
	Address  string                      `json:"address"`
	URL      string                      `json:"url"`
	Schiphol *maps.DistanceMatrixElement `json:"schiphol"`
	Centraal *maps.DistanceMatrixElement `json:"centraal"`
}

type Listing struct {
	URL     string `json:"url"`
	Address string `json:"address"`
}

func requestMatrix(ctx context.Context, client *maps.Client, listings []Listing) {
	origins := make([]string, len(listings))
	for i, l := range listings {
		origins[i] = l.Address
	}

	request := &maps.DistanceMatrixRequest{
		Mode:    maps.TravelModeTransit,
		Units:   maps.UnitsMetric,
		Origins: origins,

		Destinations: []string{
			"Schiphol Airport",
			"Amsterdam Centraal",
		},
	}

	response, err := client.DistanceMatrix(ctx, request)
	failOn(err, "could not retrieve distance matrix:")

	for idx, addr := range response.OriginAddresses {
		result := LocationResult{
			Address:  addr,
			URL:      listings[idx].URL,
			Schiphol: response.Rows[idx].Elements[0],
			Centraal: response.Rows[idx].Elements[1],
		}

		j, _ := json.Marshal(result)
		fmt.Println(string(j))
	}
}

func main() {
	var listings []Listing
	input, err := ioutil.ReadFile("fun/amsterdump/input.json")
	failOn(err, "could not read input file:")

	err = json.Unmarshal(input, &listings)
	failOn(err, "could not deserialise listings:")

	ctx := context.Background()
	apiKey := os.Getenv("MAPS_API_KEY")
	if apiKey == "" {
		log.Fatalln("API key must be supplied via MAPS_API_KEY")
	}

	client, err := maps.NewClient(maps.WithAPIKey(apiKey))
	failOn(err, "could not create Google Maps API client:")

	var chunk []Listing
	for _, l := range listings {
		if len(chunk) == 25 {
			requestMatrix(ctx, client, chunk)
			chunk = []Listing{}
		} else {
			chunk = append(chunk, l)
		}
	}

	if len(chunk) > 1 {
		requestMatrix(ctx, client, chunk)
	}
}
