package main

import (
	"AoC2020/helper"
	"fmt"
	"os"
	"strconv"
)

var Pointing rune

func main() {
	args := os.Args[1:]
	input, err := helper.GetInput(args[0])
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("%v",input)
	runPart2(input)
}

func run(input []string) {
	Pointing = 'E'
	position := [2]int{0,0}
	for _, val := range input {
		fmt.Println(val)
		position = move(position,val)
		fmt.Printf("Positon %v, Direction %v \n", position, string(Pointing))
	}
}

func runPart2(input []string) {
	positionBoat := [2]int{0,0}
	positionWayPoint := [2]int{10,1}
	for _, val := range input {
		fmt.Println(val)
		positionBoat, positionWayPoint = movePart2(positionBoat,positionWayPoint,val)
		fmt.Printf("PositonBoat: %v, PositionWaypoint %v \n", positionBoat, positionWayPoint)
	}
}

func move(position [2]int, command string) [2]int{
	direction := rune(command[0])
	distance, _ := strconv.Atoi(command[1:])
	if direction == 'L' || direction == 'R' {
		Pointing = turnByDegree(Pointing,direction,distance)
		return position
	}
	if direction == 'F'{
		newCommand := string(Pointing) + command[1:]
		return move(position,newCommand)
	}
	return moveCompass(position,direction,distance)
}

func movePart2(positionBoat [2]int, positionWayPoint [2]int, command string) ([2]int, [2]int){
	direction := rune(command[0])
	if direction == 'F'{
		distance, _ := strconv.Atoi(command[1:])
		var newPositionBoat [2]int
		newPositionBoat[0] = positionBoat[0] + distance * positionWayPoint[0]
		newPositionBoat[1] = positionBoat[1] + distance * positionWayPoint[1]
		positionBoat = newPositionBoat
	}else {
		newPositionWaypoint := moveWayPoint(positionWayPoint,command)
		positionWayPoint = newPositionWaypoint
	}
	return positionBoat, positionWayPoint
}

func moveCompass(position [2]int, direction rune, distance int) [2]int {
	switch direction {
	case 'N':return [2]int{position[0], position[1] + distance}
	case 'S':return [2]int{position[0], position[1] - distance}
	case 'E':return [2]int{position[0] + distance, position[1]}
	case 'W':return [2]int{position[0] - distance, position[1]}
	}
	return position
}

func moveWayPoint(position [2]int, command string) [2]int {
	direction := rune(command[0])
	distance, _ := strconv.Atoi(command[1:])
	if direction == 'L' || direction == 'R' {
		return turnWayPoint(position,direction,distance)
	}
	return moveCompass(position,direction,distance)
}

func turnWayPoint(position [2]int,turn rune, degree int) [2]int {
	p0 := position[0]
	p1 := position[1]
	var newDs = make([]rune,2)
	if p0 > 0 {
		newDs[0] = turnByDegree('E', turn, degree)
	}else {
		newDs[0] = turnByDegree('W', turn, degree)
	}

	if p1 > 0 {
		newDs[1] = turnByDegree('N', turn, degree)
	}else {
		newDs[1] = turnByDegree('S', turn, degree)
	}
	var newPosition [2]int

	for i,val := range newDs {
		switch val {
		case 'N': newPosition[1] = Abs(position[i])
		case 'S': newPosition[1] = -Abs(position[i])
		case 'E': newPosition[0] = Abs(position[i])
		case 'W': newPosition[0] = -Abs(position[i])
		}
	}
	return newPosition
}

func turnByDegree(pointing rune, turn rune, degree int) rune {
	directions := []rune{'N','E','S','W'}
	move := degree / 90
	if turn == 'L' {
		move = -1 * move
	}
	for i, val := range directions {
		if val == pointing {
			newDirectionPointer := (i + move) % len(directions)
			if newDirectionPointer < 0 {
				newDirectionPointer = len(directions) + newDirectionPointer
			}
			return directions[newDirectionPointer]
		}
	}
	return pointing
}

func Abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}
