package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

const (
	MaxInt      = int(^uint(0) >> 1)
	MinInt      = -MaxInt - 1
	ClearScreen = "\033[H\033[2J"
)

var sky *Sky

func main() {
	inp := StdinToStringSlice()
	buildSky(inp)
	part1()
}

func buildSky(inp []string) {
	sky = &Sky{
		minPos: Pos{x: MaxInt, y: MaxInt},
		maxPos: Pos{x: MinInt, y: MinInt},
	}
	for _, v := range inp {
		sky.addStar(v)
	}
}

func part1() {
	for i := 1; i < 100000; i++ {
		sky.tick()
		if sky.maxPos.y-sky.minPos.y <= 10 {
			sky.printSky()
			fmt.Println("Seconds:", i)
			break
		}
	}
}

type Sky struct {
	stars          []*Star
	minPos, maxPos Pos
}

func (s *Sky) tick() {
	s.setMinPosX(MaxInt)
	s.setMinPosY(MaxInt)
	s.setMaxPosX(MinInt)
	s.setMaxPosY(MinInt)
	for _, v := range s.stars {
		v.tick()
		s.considerStar(v)
	}
}

func (s *Sky) considerStar(v *Star) {
	if v.position.x < s.minPos.x {
		s.setMinPosX(v.position.x)
	}
	if v.position.y < s.minPos.y {
		s.setMinPosY(v.position.y)
	}
	if v.position.x > s.maxPos.x {
		s.setMaxPosX(v.position.x)
	}
	if v.position.y > s.maxPos.y {
		s.setMaxPosY(v.position.y)
	}
}

func (s *Sky) starAt(p *Pos) bool {
	for _, v := range s.stars {
		if p.equals(v.position) {
			return true
		}
	}
	return false
}

func (s *Sky) printSky() {
	for i := s.minPos.y; i <= s.maxPos.y; i++ {
		for j := s.minPos.x; j <= s.maxPos.x; j++ {
			if s.starAt(&Pos{x: j, y: i}) {
				fmt.Print("*")
			} else {
				fmt.Print(" ")
			}
		}
		fmt.Println("")
	}
}

func (s *Sky) addStar(inp string) {
	v := NewStar(inp)
	s.stars = append(s.stars, v)
	s.considerStar(v)
}

func (s *Sky) setMinPosX(x int) {
	s.minPos.x = x
}
func (s *Sky) setMinPosY(y int) {
	s.minPos.y = y
}
func (s *Sky) setMaxPosX(x int) {
	s.maxPos.x = x
}
func (s *Sky) setMaxPosY(y int) {
	s.maxPos.y = y
}

type Star struct {
	position *Pos
	velocity *Pos
}

func NewStar(inp string) *Star {
	inp = inp[10:]
	pts := strings.Split(inp, ">")
	posStr := pts[0]
	velStr := pts[1][11:len(pts[1])]
	return &Star{
		position: NewPos(posStr),
		velocity: NewPos(velStr),
	}
}

func (s *Star) tick() {
	s.position.x += s.velocity.x
	s.position.y += s.velocity.y
}

type Pos struct {
	x, y int
}

func NewPos(inp string) *Pos {
	pts := strings.Split(inp, ", ")
	return &Pos{
		x: Atoi(strings.TrimSpace(pts[0])),
		y: Atoi(strings.TrimSpace(pts[1])),
	}
}

func (p *Pos) equals(o *Pos) bool {
	return p.x == o.x && p.y == o.y
}

func StdinToStringSlice() []string {
	var input []string
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		input = append(input, scanner.Text())
	}
	return input
}

func Atoi(i string) int {
	var ret int
	var err error
	if ret, err = strconv.Atoi(i); err != nil {
		fmt.Println(err.Error())
		log.Fatal("Invalid Atoi" + i)
	}
	return ret
}
