package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "path/filepath"
    "sync"
    "time"
)

func downloadTile(z, x, y int, wg *sync.WaitGroup, ch chan string, sem chan struct{}) {
    defer wg.Done()
    defer func() { <-sem }() // Release the semaphore slot

    client := &http.Client{}

    // Create a new request to the OpenStreetMap tile URL
    url := fmt.Sprintf("https://tile.openstreetmap.org/%d/%d/%d.png", z, x, y)
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        ch <- fmt.Sprintf("Error creating request: %v", err)
        return
    }

    // Set the User-Agent header
    req.Header.Set("User-Agent", "MyTileDownloader/1.0")

    // Make the request
    response, err := client.Do(req)
    if err != nil {
        ch <- fmt.Sprintf("Error making request: %v", err)
        return
    }

    defer response.Body.Close()

    if response.StatusCode == http.StatusNotFound {
        ch <- fmt.Sprintf("Tile %d/%d/%d not found (404)\n", z, x, y)
        return
    }

    if response.StatusCode != http.StatusOK {
        ch <- fmt.Sprintf("Failed to download tile %d/%d/%d: %s\n", z, x, y, response.Status)
        return
    }

    // Create the destination directory
    tilePath := filepath.Join(fmt.Sprintf("%d", z), fmt.Sprintf("%d", x), fmt.Sprintf("%d", y)+".png")
    os.MkdirAll(filepath.Dir(tilePath), os.ModePerm)

    outFile, err := os.Create(tilePath)
    if err != nil {
        ch <- fmt.Sprintf("Error creating file: %v", err)
        return
    }
    defer outFile.Close()

    _, err = io.Copy(outFile, response.Body)
    if err != nil {
        ch <- fmt.Sprintf("Error saving file: %v", err)
        return
    }
    ch <- fmt.Sprintf("Downloaded tile: z %d, x %d, y %d\n", z, x, y)
}

func main() {
    var wg sync.WaitGroup
    ch := make(chan string)
    sem := make(chan struct{}, 1500) // Semaphore with 1500 slots

    // Goroutine to print messages from the channel
    go func() {
        for msg := range ch {
            fmt.Print(msg)
        }
    }()
    i := 0
    j := 0
    for z := 10; z < 19; z++ {
        finz := 1 << z // equivalent to 2^z
        for x := i; x < finz; x++ {
            for y := j; y < finz; y++ {
                wg.Add(1)
                sem <- struct{}{} // Acquire a semaphore slot
                go downloadTile(z, x, y, &wg, ch, sem)
                time.Sleep(15 * time.Millisecond) // Pause between starting new goroutines
            }
    	    j = 0
            i = 0
        }
    }

    wg.Wait()
    close(ch)
}