package main

import (
	"fmt"
	"strings"

	h "git.bullercodeworks.com/brian/adventofcode/helpers"
)

func main() {
	inp := h.StdinToStringSlice()
	part1(inp)
	part2(inp)
}

func part1(inp []string) {
	s := NewSubMk1(0, 0, 0)
	s.Follow(inp)
	fmt.Println(s)
}

func part2(inp []string) {
	s := NewSubMk2(0, 0, 0)
	s.Follow(inp)
	fmt.Println(s)
}

type Sub interface {
	Follow(inp []string)
	Forward(d int)
	Down(d int)
	Up(d int)
}

type SubMk1 struct {
	Horizontal, Depth, Aim int
}

func NewSubMk1(horizontal, depth, aim int) Sub {
	return &SubMk1{
		Horizontal: horizontal,
		Depth:      depth,
		Aim:        aim,
	}
}

func (s SubMk1) String() string {
	return fmt.Sprintf("Horizontal: %d; Depth: %d; Aim: %d; M: %d", s.Horizontal, s.Depth, s.Aim, s.Horizontal*s.Depth)
}

func (s *SubMk1) Follow(inp []string) {
	for _, v := range inp {
		pts := strings.Fields(v)
		if len(pts) < 2 {
			fmt.Println("Error following instruction:", v)
			return
		}
		switch pts[0] {
		case "forward":
			s.Forward(h.Atoi(pts[1]))
		case "down":
			s.Down(h.Atoi(pts[1]))
		case "up":
			s.Up(h.Atoi(pts[1]))
		}
	}
}

func (s *SubMk1) Forward(d int) {
	s.Horizontal += d
}

func (s *SubMk1) Down(d int) {
	s.Depth += d
}

func (s *SubMk1) Up(d int) {
	s.Depth -= d
}

type SubMk2 struct {
	Horizontal, Depth, Aim int
}

func NewSubMk2(horizontal, depth, aim int) Sub {
	return &SubMk2{
		Horizontal: horizontal,
		Depth:      depth,
		Aim:        aim,
	}
}

func (s SubMk2) String() string {
	return fmt.Sprintf("Horizontal: %d; Depth: %d; Aim: %d; M: %d", s.Horizontal, s.Depth, s.Aim, s.Horizontal*s.Depth)
}

func (s *SubMk2) Follow(inp []string) {
	for _, v := range inp {
		pts := strings.Fields(v)
		if len(pts) < 2 {
			fmt.Println("Error following instruction:", v)
			return
		}
		switch pts[0] {
		case "forward":
			s.Forward(h.Atoi(pts[1]))
		case "down":
			s.Down(h.Atoi(pts[1]))
		case "up":
			s.Up(h.Atoi(pts[1]))
		}
	}
}

func (s *SubMk2) Forward(x int) {
	s.Horizontal += x
	s.Depth += s.Aim * x
}

func (s *SubMk2) Down(x int) {
	s.Aim += x
}

func (s *SubMk2) Up(x int) {
	s.Aim -= x
}
