package main

import (
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"

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

func main() {
	inp := h.StdinToStringSlice()
	var passports []*Passport
	var p *Passport
	p, inp = GetFirstPassport(inp)
	for p != nil {
		passports = append(passports, p)
		p, inp = GetFirstPassport(inp)
	}
	var valid int
	for _, t := range passports {
		if t.IsValid() {
			valid++
		}
	}
	fmt.Printf("%d Valid Passports\n", valid)
}

type Passport struct {
	BirthYear  string
	IssueYear  string
	Expiration string
	Height     string
	HairColor  string
	EyeColor   string
	PassportID string
	CountryID  string
}

func GetFirstPassport(input []string) (*Passport, []string) {
	ret := Passport{}
	var k int
	for k = range input {
		if strings.TrimSpace(input[k]) == "" {
			break
		}
		pts := strings.Split(input[k], " ")
		for pk := range pts {
			keyval := strings.Split(pts[pk], ":")
			if len(keyval) == 2 {
				switch keyval[0] {
				case "byr":
					ret.BirthYear = keyval[1]
				case "iyr":
					ret.IssueYear = keyval[1]
				case "eyr":
					ret.Expiration = keyval[1]
				case "hgt":
					ret.Height = keyval[1]
				case "hcl":
					ret.HairColor = keyval[1]
				case "ecl":
					ret.EyeColor = keyval[1]
				case "pid":
					ret.PassportID = keyval[1]
				case "cid":
					ret.CountryID = keyval[1]
				}
			}
		}
	}
	if k == 0 {
		return nil, []string{}
	}
	return &ret, input[k+1:]
}

func (p *Passport) ValidBirthYear() bool {
	wrk, err := strconv.Atoi(p.BirthYear)
	return err == nil && wrk >= 1920 && wrk <= 2002
}

func (p *Passport) ValidIssueYear() bool {
	wrk, err := strconv.Atoi(p.IssueYear)
	return err == nil && wrk >= 2010 && wrk <= 2020
}

func (p *Passport) ValidExpiration() bool {
	wrk, err := strconv.Atoi(p.Expiration)
	return err == nil && wrk >= 2020 && wrk <= 2030
}

func (p *Passport) ValidHeight() bool {
	if strings.Contains(p.Height, "in") {
		wrk, err := strconv.Atoi(strings.TrimSuffix(p.Height, "in"))
		return err == nil && wrk >= 59 && wrk <= 76
	} else if strings.Contains(p.Height, "cm") {
		wrk, err := strconv.Atoi(strings.TrimSuffix(p.Height, "cm"))
		return err == nil && wrk >= 150 && wrk <= 193
	}
	return false
}

func (p *Passport) ValidHairColor() bool {
	if !strings.HasPrefix(p.HairColor, "#") {
		return false
	}
	_, err := hex.DecodeString(p.HairColor[1:])
	return err == nil
}

func (p *Passport) ValidEyeColor() bool {
	return p.EyeColor == "amb" || p.EyeColor == "blu" || p.EyeColor == "brn" || p.EyeColor == "gry" || p.EyeColor == "grn" || p.EyeColor == "hzl" || p.EyeColor == "oth"
}

func (p *Passport) ValidPassportID() bool {
	_, err := strconv.Atoi(p.PassportID)
	return err == nil && len(p.PassportID) == 9
}

func (p *Passport) IsValid() bool {
	return p.ValidBirthYear() && p.ValidIssueYear() && p.ValidExpiration() && p.ValidHeight() && p.ValidHairColor() && p.ValidEyeColor() && p.ValidPassportID()
}