<!DOCTYPE html>
<html lang="en" class="h-100">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Name All the Cities</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body class="h-100 d-flex flex-column">
    <div class="container vh-100">
        <h1>Name All the Cities</h1>
        <div class="mb-3">
            <label for="state-select" class="form-label">State:</label>
            <div class="input-group">
                <select id="state-select" class="form-select" disabled>
                    <option select>Choose a stateā€¦</option>
                </select>
                <button class="btn btn-primary" type="button" id="go-button">Play</button>
            </div>
        </div>
        <div id="game" v-scope class="row h-100" style="visibility:hidden;">
            <div class="col-4">
                <div class="mb-3">
                    <label for="city-guess" class="form-label">City:</label>
                    <div class="input-group">
                        <input class="form-control" id="city-guess" v-model="city_guess" @keyup.enter="guess">
                        <button class="btn btn-primary" type="button" @click="guess">Guess</button>
                    </div>
                </div>
                <div v-if="message != ''" class="alert alert-secondary" role="alert">
                    {{ message }}
                </div>

                <h3>{{ state }} cities</h3>
                <div>
                    <span
                        v-for="(required_cities, name) in achievements"
                        :class="{ 'badge': true, 'me-1': true, 'text-bg-secondary': !required_cities(cities).every(c => c.guessed), 'text-bg-warning': required_cities(cities).every(c => c.guessed) }">
                        {{ name }}
                        ({{ required_cities(cities).filter(c => c.guessed).length }}/{{ required_cities(cities).length }})
                    </span>
                </div>
                <table class="table">
                    <thead>
                        <tr>
                            <th>Rank</th>
                            <th>Name</th>
                            <th>Population</th>
                        </tr>
                    </thead>
                    <tbody>
                        <template v-for="(city, rank) in cities">
                            <tr v-show="city.guessed">
                                <td>{{ rank + 1 }}</td>
                                <td>{{ city.name }}</td>
                                <td>{{ city.pop }}</td>
                            </tr>
                        </template>
                    </tbody>
                </table>
            </div>
            <div class="col-8 h-100">
                <canvas id="canvas" class="mx-3 my-auto" @vue:mounted="draw" width="800" height="600"></canvas>
            </div>
        </div>
    </div>
    <footer class="footer mt-auto py-3 text-center text-muted">
        Created by <a href="https://chandlerswift.com">Chandler Swift</a> using 2020 US Census data |
        <a href="https://git.chandlerswift.com/chandlerswift/name-all-cities-by-population-quiz">Source</a>
        (<a href="https://git.chandlerswift.com/chandlerswift/name-all-cities-by-population-quiz/src/branch/main/LICENSE">GPL3</a>)
    </footer>

    <script type="module">
        let state_shape_data;
        let state_shape_data_request;
        (async function init(){
            const response = await fetch("data/states.json");
            const states = await response.json();

            const ss = document.getElementById('state-select');
            ss.disabled = false;
            for (let state of states) {
                ss.options[ss.options.length] = new Option(state);
            }

            document.getElementById('go-button').addEventListener('click', async (e) => {
                e.target.disabled = true;
                ss.disabled = true;
                await selectState(ss.value);
                document.getElementById('game').style.visibility = 'visible';
            });

            // Check if there's a parameter in the URL already
            const state = (new URLSearchParams(window.location.search)).get('state');
            if (state) {
                ss.value = state;
                document.getElementById('go-button').click();
            }
            
            const shape_response = await fetch("data/states.geojson");
            state_shape_data_request = shape_response.json().then(data => state_shape_data = data);
        })();

        import { createApp } from 'https://unpkg.com/petite-vue?module'

        // https://stackoverflow.com/a/13627586
        function ordinal_suffix_of(i) {
            let j = i % 10,
                k = i % 100;
            if (j === 1 && k !== 11) {
                return i + "st";
            }
            if (j === 2 && k !== 12) {
                return i + "nd";
            }
            if (j === 3 && k !== 13) {
                return i + "rd";
            }
            return i + "th";
        }

        const simplify = s => s.toLowerCase().replace(/[^a-z]/g, '');

        async function selectState(stateName) {
            const response = await fetch(`data/${stateName}.json`);
            const cities = await response.json();
            await state_shape_data_request; // We can't draw until this is in
            const state_shape = state_shape_data.features.find(f => f.properties.NAME.toLowerCase() == stateName.toLowerCase());
            const state_bounds = find_bounds(state_shape.geometry.coordinates[0]);
            if (!state_shape) {
                console.error("Unable to find state in shapes");
            }
            createApp({
                eventListenerAdded: false,
                state: stateName,
                cities: cities,
                simplified_cities: cities.map(city => simplify(city.name)),
                city_guess: "",
                message: "",
                achievements: {
                    "Top Five": cities => cities.slice(0, 5),
                    "Top Ten": cities => cities.slice(0, 10),
                    "Top Twenty": cities => cities.slice(0, 20),
                    "Top Fifty": cities => cities.slice(0, 50),
                    "Top Hundred": cities => cities.slice(0, 100),
                    "Every Single One": cities => cities,
                    "All above 100k": cities => cities.filter(city => city.pop >= 100000),
                    "All above 50k": cities => cities.filter(city => city.pop >= 50000),
                    "All above 25k": cities => cities.filter(city => city.pop >= 25000),
                },
                guess() {
                    const rank = this.simplified_cities.indexOf(simplify(this.city_guess))
                    if (rank >= 0) {
                        const city = this.cities[rank];
                        
                        if (!city.guessed) {
                            city.guessed = true;
                            this.message = `${city.name} (population ${city.pop}) is the ${ordinal_suffix_of(rank + 1)} most populated city in ${this.state}.`;
                            this.city_guess = "";
                            this.draw();
                        } else {
                            this.message = `Already guessed ${city.name} (population ${city.pop}, rank ${rank}).`;
                            this.city_guess = "";
                        }
                    }
                },
                draw() {
                    const canvas = document.getElementById("canvas");
                    const ctx = canvas.getContext("2d");
                    ctx.clearRect(0, 0, canvas.width, canvas.height);

                    function transform(pt) {
                        // TODO: this just works for MN, and not very well at that
                        return [(pt[0]+98)*80, 600-(pt[1]-42)*80];
                    }

                    ctx.beginPath();
                    ctx.moveTo(...transform(state_shape.geometry.coordinates[0][0]))
                    console.log(transform(state_shape.geometry.coordinates[0][0]))
                    for (let coord of state_shape.geometry.coordinates[0].slice(1)) {
                        ctx.lineTo(...transform(coord));
                    }
                    ctx.stroke();
                    for (let city of cities) {
                        if (city.guessed) {
                            ctx.beginPath();
                            const c = transform([city.location[1], city.location[0]])
                            console.log(city.location, c)
                            ctx.arc(c[0], c[1], 2, 0, 2*Math.PI, true);
                            ctx.fill();
                        }
                    }
                },
            }).mount();
        }

        // returns minx, maxx, miny, maxy
        function find_bounds(coords) {
            return [
                Math.min(...coords.map(c => c[0])),
                Math.max(...coords.map(c => c[0])),
                Math.min(...coords.map(c => c[1])),
                Math.max(...coords.map(c => c[1]))
            ];
        }
    </script>
</body>
</html>