<commit_before>package seq

import (
	"fmt"
	"math"
	"strconv"
)

// HMM states in the Plan7 architecture.
const (
	Match HMMState = iota
	Deletion
	Insertion
	Begin
	End
)

type HMMState int

type HMM struct {
	// An ordered list of HMM nodes.
	Nodes []HMMNode

	// The alphabet as defined by an ordering of residues.
	// Indices in this slice correspond to indices in match/insertion emissions.
	Alphabet []Residue

	// NULL model. (Amino acid background frequencies.)
	// HMMER hmm files don't have this, but HHsuite hhm files do.
	// In the case of HHsuite, the NULL model is used for insertion emissions
	// in every node.
	Null EProbs
}

type HMMNode struct {
	HMM                 *HMM
	NodeNum             int
	Transitions         TProbs
	InsEmit             EProbs
	MatEmit             EProbs
	NeffM, NeffI, NeffD Prob
}

// EProbs represents emission probabilities, as log-odds scores.
type EProbs map[Residue]Prob

// NewEProbs creates a new EProbs map from the given alphabet. Keys of the map
// are residues defined in the alphabet, and values are defaulted to the
// minimum probability.
func NewEProbs(alphabet []Residue) EProbs {
	ep := make(EProbs, len(alphabet))
	for _, residue := range alphabet {
		ep[residue] = MinProb
	}
	return ep
}

// Returns the emission probability for a particular residue.
func (ep EProbs) EmitProb(r Residue) Prob {
	return ep[r]
}

// TProbs represents transition probabilities, as log-odds scores.
// Note that ID and DI are omitted (Plan7).
type TProbs struct {
	MM, MI, MD, IM, II, DM, DD Prob
}

// Prob represents a transition or emission probability.
type Prob float64

var invalidProb = Prob(math.NaN())

// The value representing a minimum emission/transition probability.
// Remember, max in log space is minimum probability.
var MinProb = Prob(math.MaxFloat64)

// NewProb creates a new probability value from a string (usually read from
// an hmm or hhm file). If the string is equivalent to the special value "*",
// then the probability returned is guaranteed to be minimal. Otherwise, the
// string is parsed as a float, and an error returned if parsing fails.
func NewProb(fstr string) (Prob, error) {
	if fstr == "*" {
		return MinProb, nil
	}

	f, err := strconv.ParseFloat(fstr, 64)
	if err != nil {
		return invalidProb,
			fmt.Errorf("Could not convert '%s' to a log probability: %s",
				fstr, err)
	}
	return Prob(f), nil
}

// IsMin returns true if the probability is minimal.
func (p Prob) IsMin() bool {
	return p == MinProb
}

// NewHMM creates a new HMM from a list of nodes, an ordered alphabet and a
// set of null probabilities (which may be nil).
func NewHMM(nodes []HMMNode, alphabet []Residue, null EProbs) *HMM {
	return &HMM{
		Nodes:    nodes,
		Alphabet: alphabet,
		Null:     null,
	}
}
<commit_msg>Reading and writing HHM files.<commit_after>package seq

import (
	"fmt"
	"math"
	"strconv"
)

// HMM states in the Plan7 architecture.
const (
	Match HMMState = iota
	Deletion
	Insertion
	Begin
	End
)

type HMMState int

type HMM struct {
	// An ordered list of HMM nodes.
	Nodes []HMMNode

	// The alphabet as defined by an ordering of residues.
	// Indices in this slice correspond to indices in match/insertion emissions.
	Alphabet []Residue

	// NULL model. (Amino acid background frequencies.)
	// HMMER hmm files don't have this, but HHsuite hhm files do.
	// In the case of HHsuite, the NULL model is used for insertion emissions
	// in every node.
	Null EProbs
}

type HMMNode struct {
	HMM                 *HMM
	Residue             Residue
	NodeNum             int
	InsEmit             EProbs
	MatEmit             EProbs
	Transitions         TProbs
	NeffM, NeffI, NeffD Prob
}

// EProbs represents emission probabilities, as log-odds scores.
type EProbs map[Residue]Prob

// NewEProbs creates a new EProbs map from the given alphabet. Keys of the map
// are residues defined in the alphabet, and values are defaulted to the
// minimum probability.
func NewEProbs(alphabet []Residue) EProbs {
	ep := make(EProbs, len(alphabet))
	for _, residue := range alphabet {
		ep[residue] = MinProb
	}
	return ep
}

// Returns the emission probability for a particular residue.
func (ep EProbs) EmitProb(r Residue) Prob {
	return ep[r]
}

// TProbs represents transition probabilities, as log-odds scores.
// Note that ID and DI are omitted (Plan7).
type TProbs struct {
	MM, MI, MD, IM, II, DM, DD Prob
}

// Prob represents a transition or emission probability.
type Prob float64

var invalidProb = Prob(math.NaN())

// The value representing a minimum emission/transition probability.
// Remember, max in log space is minimum probability.
var MinProb = Prob(math.MaxFloat64)

// NewProb creates a new probability value from a string (usually read from
// an hmm or hhm file). If the string is equivalent to the special value "*",
// then the probability returned is guaranteed to be minimal. Otherwise, the
// string is parsed as a float, and an error returned if parsing fails.
func NewProb(fstr string) (Prob, error) {
	if fstr == "*" {
		return MinProb, nil
	}

	f, err := strconv.ParseFloat(fstr, 64)
	if err != nil {
		return invalidProb,
			fmt.Errorf("Could not convert '%s' to a log probability: %s",
				fstr, err)
	}
	return Prob(f), nil
}

// IsMin returns true if the probability is minimal.
func (p Prob) IsMin() bool {
	return p == MinProb
}

// NewHMM creates a new HMM from a list of nodes, an ordered alphabet and a
// set of null probabilities (which may be nil).
func NewHMM(nodes []HMMNode, alphabet []Residue, null EProbs) *HMM {
	return &HMM{
		Nodes:    nodes,
		Alphabet: alphabet,
		Null:     null,
	}
}
<|endoftext|>


<commit_before>package game

import (
	"fmt"
	"log"
	"math"
	"sort"
	"strconv"
	"time"

	termbox "github.com/nsf/termbox-go"
)

type Direction int

const (
	UP Direction = iota
	DOWN
	LEFT
	RIGHT
)

const backgroundColor = termbox.ColorBlack
const foregroundColor = termbox.ColorWhite
const chaliceForegroundColor = termbox.ColorYellow
const monsterForegroundColor = termbox.ColorRed

const WALL rune = '#'
const GUY rune = '@'
const FLOOR rune = '.'
const ENEMY rune = 'x'
const FLOOR_PREV rune = '>'
const FLOOR_NEXT rune = '<'
const CHALICE rune = '*'

const numLevels int = 3

type Game struct {
	levels           []*Level
	currLevel        *Level
	currLevelPos     int
	player           *Player
	messages         []string
	clearMessageChan chan struct{}
	renderedOnce     bool
	IsGameOver       bool
	HasChalice       bool
}

func NewGame() *Game {
	levels := make([]*Level, numLevels)
	for i := 0; i < numLevels; i++ {
		levels[i] = NewLevel(i)
	}

	return &Game{
		levels:           levels,
		currLevelPos:     0,
		currLevel:        levels[0],
		player:           NewPlayer(levels[0].prevFloorX, levels[0].prevFloorY),
		messages:         make([]string, 0, 2),
		clearMessageChan: make(chan struct{}),
		renderedOnce:     false,
		IsGameOver:       false,
		HasChalice:       false,
	}
}

func (g *Game) Render() {
	g.StopMessageChan()
	termbox.Clear(backgroundColor, backgroundColor)

	//Display dungeon tiles
	for y, line := range g.currLevel.cells {
		for x, cell := range line {
			if cell.visible {
				if cell.content != CHALICE {
					termbox.SetCell(x, y, cell.content, foregroundColor, backgroundColor)
				} else {
					termbox.SetCell(x, y, cell.content, chaliceForegroundColor, backgroundColor)
				}
			}
		}
	}

	//Add monsters on top of cells
	for _, m := range g.currLevel.monsters {
		c := g.currLevel.cells.get(m.x, m.y)
		if c.visible {
			termbox.SetCell(m.x, m.y, m.symbol, monsterForegroundColor, backgroundColor)
		}
	}

	termbox.SetCell(g.player.x, g.player.y, g.player.content, foregroundColor, backgroundColor)

	//Render menu
	menuRow := 20
	nextLevelExp := strconv.Itoa(g.player.nextLevelEXP)
	if g.player.nextLevelEXP == math.MaxInt32 {
		nextLevelExp = "MAX"
	}
	menuString := fmt.Sprintf("HP: %d/%d EXP: %d/%s LVL: %d: ", g.player.hp, g.player.maxHP, g.player.exp, nextLevelExp, g.player.level)
	for i := 0; i < len(menuString); i++ {
		termbox.SetCell(i, menuRow, rune(menuString[i]), foregroundColor, backgroundColor)
	}

	//Render the message logs
	messageRow := 22
	nextMessageToShow := 0
	g.renderedOnce = true

	displayMessages := func() (done bool) {
		//First clear any old messages
		for i := 0; i < 2; i++ {
			for j := 0; j <= maxX; j++ {
				termbox.SetCell(j, messageRow+i, ' ', foregroundColor, backgroundColor)
			}
		}

		//We only support showing up to two messages at a time
		for i := 0; i < 2; i++ {
			if nextMessageToShow >= len(g.messages) {
				if i == 0 { //If i > 0, then we need to come back in again to clear the last message
					return true
				}
				return false
			}
			for j, c := range g.messages[nextMessageToShow] {
				termbox.SetCell(j, messageRow+i, c, foregroundColor, backgroundColor)
			}
			nextMessageToShow++
		}
		return false
	}

	displayMessages()
	termbox.Flush()
	go func() {
		//Ticker displays next messages after a delay.
		messageTicker := time.NewTicker(time.Millisecond * 1500)
		for {
			select {
			case <-messageTicker.C:
				if displayMessages() {
					messageTicker.Stop()
				}
				termbox.Flush()
			case <-g.clearMessageChan:
				//Stop showing more messages and clean up resources (by returning out of go routine)
				//This channel is sent data each time we render the next frame
				messageTicker.Stop()
				return
			}
		}
	}()
}

func (g *Game) UpdateFOV() {
	x, y := g.player.x, g.player.y
	r := g.currLevel.roomContainsPoint(x, y)
	if r != nil {
		for x = r.x1; x <= r.x2; x++ {
			for y = r.y1; y <= r.y2; y++ {
				g.currLevel.cells[y][x].visible = true
			}
		}
	} else {
		g.currLevel.cells[y][x].visible = true
		g.currLevel.cells[y][x+1].visible = true
		g.currLevel.cells[y][x-1].visible = true
		g.currLevel.cells[y+1][x].visible = true
		g.currLevel.cells[y-1][x].visible = true
	}
}

func (g *Game) GetPlayerPos() (x, y int) {
	return g.player.x, g.player.y
}

//Return value is if the move requested counts as a player action.
//Moving into a wall does not count as an action
func (g *Game) MovePlayer(dir Direction) bool {
	x := 0
	y := 0
	switch dir {
	case UP:
		y = -1
	case DOWN:
		y = 1
	case LEFT:
		x = -1
	case RIGHT:
		x = 1
	}
	newX := g.player.x + x
	newY := g.player.y + y

	if g.currLevel.cells.get(newX, newY).content == WALL {
		return false
	}

	for i, m := range g.currLevel.monsters {
		if m.x == newX && m.y == newY {
			damage := calculateDamage(g.player.strength, m.defense)
			m.hp -= damage
			if m.hp <= 0 {
				g.messages = append(g.messages, fmt.Sprintf("You killed %s.", m.name))

				//Just remove the monster from the array, it shouldn't re-render
				g.currLevel.monsters = append(g.currLevel.monsters[:i], g.currLevel.monsters[i+1:]...)

				g.player.exp += m.exp
				if g.player.ProcessLevelUp() {
					g.messages = append(g.messages, fmt.Sprintf("You reached level %d!", g.player.level))
				}
			} else {
				g.messages = append(g.messages, fmt.Sprintf("You attacked %s for %d damage.", m.name, damage))
			}
			return true
		}
	}

	g.player.x = newX
	g.player.y = newY

	return true
}

func (g *Game) moveMonster(m *Monster) {
	x, y := g.determineMonsterMoveNewPos(m.x, m.y)

	if g.player.x == x && g.player.y == y {
		damage := calculateDamage(m.strength, g.player.defense)
		g.player.hp -= damage
		if g.player.hp <= 0 {
			g.IsGameOver = true
		} else {
			g.messages = append(g.messages, fmt.Sprintf("%s has attacked you for %d damage.", m.name, damage))
		}
	} else {
		m.x = x
		m.y = y
	}
}

func (g *Game) OnDungeonExit() bool {
	return g.currLevelPos <= 0 && g.player.x == g.currLevel.prevFloorX && g.player.y == g.currLevel.prevFloorY
}

func (g *Game) OnChalice() bool {
	return g.currLevel.cells.get(g.player.x, g.player.y).content == CHALICE
}

func (g *Game) TakeChalice() {
	g.currLevel.cells.get(g.player.x, g.player.y).content = FLOOR
	g.HasChalice = true
	g.messages = append(g.messages, "You took the chalice of riches.")
}

func (g *Game) ChangeFloor() bool {
	if g.player.x == g.currLevel.prevFloorX && g.player.y == g.currLevel.prevFloorY {
		if g.currLevelPos <= 0 {
			//TODO - show dialog asking if player wants to leave. Special case to handle this (custom return value enum?)
			return false
		}
		g.currLevelPos--
		g.currLevel = g.levels[g.currLevelPos]
		g.player.x = g.currLevel.nextFloorX
		g.player.y = g.currLevel.nextFloorY
		return true
	} else if g.player.x == g.currLevel.nextFloorX && g.player.y == g.currLevel.nextFloorY {
		if g.currLevelPos >= len(g.levels)-1 {
			//Should never happen
			//TODO: do not render the next floor on the top most floor
			return false
		}
		g.currLevelPos++
		g.currLevel = g.levels[g.currLevelPos]
		g.player.x = g.currLevel.prevFloorX
		g.player.y = g.currLevel.prevFloorY
		return true
	}
	return false
}

func (g *Game) HealPlayerFromActions() {
	g.player.HealFromActions()
}

func (g *Game) UpdateMonsters() {
	//Order monsters so the nearest ones moves first
	//This prevents closer monsters from blocking the ones behind them by not moving first
	sort.Slice(g.currLevel.monsters, func(i, j int) bool {
		return tileDistance(g.currLevel.monsters[i].x, g.currLevel.monsters[i].y, g.player.x, g.player.y) <
			tileDistance(g.currLevel.monsters[j].x, g.currLevel.monsters[j].y, g.player.x, g.player.y)
	})

	for _, m := range g.currLevel.monsters {
		if g.currLevel.cells.get(m.x, m.y).visible {
			m.active = true
		}

		if m.active {
			g.moveMonster(m)
		}
	}
}

//Given the input (x, y), what is the best way to move towards the player
func (g *Game) determineMonsterMoveNewPos(x, y int) (int, int) {
	nearestToPlayer, distance := g.nearestAdjacentPlayerSpot(x, y)
	log.Printf("Player: (%d, %d); Nearest to player (%d, %d); Monster: (%d, %d); Distance: %d",
		g.player.x, g.player.y,
		nearestToPlayer[0], nearestToPlayer[1],
		x, y, distance)

	//We are already beside player
	if distance == 0 {
		return g.player.x, g.player.y
	}

	//We could not find a nearest spot
	if nearestToPlayer[0] < 0 {
		return x, y
	}

	potentialMovements := [][]int{
		[]int{x - 1, y},
		[]int{x + 1, y},
		[]int{x, y - 1},
		[]int{x, y + 1},
	}

	//Can we move closer to that spot?
	for _, movement := range potentialMovements {
		if g.monsterCanMoveTo(movement[0], movement[1]) &&
			tileDistance(movement[0], movement[1], nearestToPlayer[0], nearestToPlayer[1]) < distance {
			return movement[0], movement[1]
		}
	}

	//No better move, stay still
	return x, y
}

func (g *Game) nearestAdjacentPlayerSpot(x, y int) (nearestSpot []int, distance int) {
	nearestSpot = []int{-1, -1}
	distance = math.MaxInt32

	potentialSpots := [][]int{
		[]int{g.player.x - 1, g.player.y},
		[]int{g.player.x + 1, g.player.y},
		[]int{g.player.x, g.player.y - 1},
		[]int{g.player.x, g.player.y + 1},
	}

	for _, spot := range potentialSpots {
		//Already adjacent spot, just return this spot and don't move
		if x == spot[0] && y == spot[1] {
			nearestSpot = spot
			distance = 0
			break
		}

		if !g.monsterCanMoveTo(spot[0], spot[1]) {
			log.Printf("Cannot move to spot\n")
			continue
		}

		currDist := tileDistance(x, y, spot[0], spot[1])
		log.Printf("Distance to spot: %d\n", currDist)
		if currDist < distance {
			distance = currDist
			nearestSpot = spot
		}
	}
	return
}

func (g *Game) monsterCanMoveTo(x, y int) bool {
	c := g.currLevel.cells.get(x, y)
	if c.content == WALL {
		return false
	}

	for _, m := range g.currLevel.monsters {
		if m.x == x && m.y == y {
			return false
		}
	}
	return true
}

func (g *Game) ClearMessages() {
	g.messages = g.messages[:0]
}

func (g *Game) StopMessageChan() {
	if g.renderedOnce {
		g.clearMessageChan <- struct{}{}
	}
}
<commit_msg>Remove unneeded logs and TODO comments<commit_after>package game

import (
	"fmt"
	"math"
	"sort"
	"strconv"
	"time"

	termbox "github.com/nsf/termbox-go"
)

type Direction int

const (
	UP Direction = iota
	DOWN
	LEFT
	RIGHT
)

const backgroundColor = termbox.ColorBlack
const foregroundColor = termbox.ColorWhite
const chaliceForegroundColor = termbox.ColorYellow
const monsterForegroundColor = termbox.ColorRed

const WALL rune = '#'
const GUY rune = '@'
const FLOOR rune = '.'
const ENEMY rune = 'x'
const FLOOR_PREV rune = '>'
const FLOOR_NEXT rune = '<'
const CHALICE rune = '*'

const numLevels int = 3

type Game struct {
	levels           []*Level
	currLevel        *Level
	currLevelPos     int
	player           *Player
	messages         []string
	clearMessageChan chan struct{}
	renderedOnce     bool
	IsGameOver       bool
	HasChalice       bool
}

func NewGame() *Game {
	levels := make([]*Level, numLevels)
	for i := 0; i < numLevels; i++ {
		levels[i] = NewLevel(i)
	}

	return &Game{
		levels:           levels,
		currLevelPos:     0,
		currLevel:        levels[0],
		player:           NewPlayer(levels[0].prevFloorX, levels[0].prevFloorY),
		messages:         make([]string, 0, 2),
		clearMessageChan: make(chan struct{}),
		renderedOnce:     false,
		IsGameOver:       false,
		HasChalice:       false,
	}
}

func (g *Game) Render() {
	g.StopMessageChan()
	termbox.Clear(backgroundColor, backgroundColor)

	//Display dungeon tiles
	for y, line := range g.currLevel.cells {
		for x, cell := range line {
			if cell.visible {
				if cell.content != CHALICE {
					termbox.SetCell(x, y, cell.content, foregroundColor, backgroundColor)
				} else {
					termbox.SetCell(x, y, cell.content, chaliceForegroundColor, backgroundColor)
				}
			}
		}
	}

	//Add monsters on top of cells
	for _, m := range g.currLevel.monsters {
		c := g.currLevel.cells.get(m.x, m.y)
		if c.visible {
			termbox.SetCell(m.x, m.y, m.symbol, monsterForegroundColor, backgroundColor)
		}
	}

	termbox.SetCell(g.player.x, g.player.y, g.player.content, foregroundColor, backgroundColor)

	//Render menu
	menuRow := 20
	nextLevelExp := strconv.Itoa(g.player.nextLevelEXP)
	if g.player.nextLevelEXP == math.MaxInt32 {
		nextLevelExp = "MAX"
	}
	menuString := fmt.Sprintf("HP: %d/%d EXP: %d/%s LVL: %d: ", g.player.hp, g.player.maxHP, g.player.exp, nextLevelExp, g.player.level)
	for i := 0; i < len(menuString); i++ {
		termbox.SetCell(i, menuRow, rune(menuString[i]), foregroundColor, backgroundColor)
	}

	//Render the message logs
	messageRow := 22
	nextMessageToShow := 0
	g.renderedOnce = true

	displayMessages := func() (done bool) {
		//First clear any old messages
		for i := 0; i < 2; i++ {
			for j := 0; j <= maxX; j++ {
				termbox.SetCell(j, messageRow+i, ' ', foregroundColor, backgroundColor)
			}
		}

		//We only support showing up to two messages at a time
		for i := 0; i < 2; i++ {
			if nextMessageToShow >= len(g.messages) {
				if i == 0 { //If i > 0, then we need to come back in again to clear the last message
					return true
				}
				return false
			}
			for j, c := range g.messages[nextMessageToShow] {
				termbox.SetCell(j, messageRow+i, c, foregroundColor, backgroundColor)
			}
			nextMessageToShow++
		}
		return false
	}

	displayMessages()
	termbox.Flush()
	go func() {
		//Ticker displays next messages after a delay.
		messageTicker := time.NewTicker(time.Millisecond * 1500)
		for {
			select {
			case <-messageTicker.C:
				if displayMessages() {
					messageTicker.Stop()
				}
				termbox.Flush()
			case <-g.clearMessageChan:
				//Stop showing more messages and clean up resources (by returning out of go routine)
				//This channel is sent data each time we render the next frame
				messageTicker.Stop()
				return
			}
		}
	}()
}

func (g *Game) UpdateFOV() {
	x, y := g.player.x, g.player.y
	r := g.currLevel.roomContainsPoint(x, y)
	if r != nil {
		for x = r.x1; x <= r.x2; x++ {
			for y = r.y1; y <= r.y2; y++ {
				g.currLevel.cells[y][x].visible = true
			}
		}
	} else {
		g.currLevel.cells[y][x].visible = true
		g.currLevel.cells[y][x+1].visible = true
		g.currLevel.cells[y][x-1].visible = true
		g.currLevel.cells[y+1][x].visible = true
		g.currLevel.cells[y-1][x].visible = true
	}
}

func (g *Game) GetPlayerPos() (x, y int) {
	return g.player.x, g.player.y
}

//Return value is if the move requested counts as a player action.
//Moving into a wall does not count as an action
func (g *Game) MovePlayer(dir Direction) bool {
	x := 0
	y := 0
	switch dir {
	case UP:
		y = -1
	case DOWN:
		y = 1
	case LEFT:
		x = -1
	case RIGHT:
		x = 1
	}
	newX := g.player.x + x
	newY := g.player.y + y

	if g.currLevel.cells.get(newX, newY).content == WALL {
		return false
	}

	for i, m := range g.currLevel.monsters {
		if m.x == newX && m.y == newY {
			damage := calculateDamage(g.player.strength, m.defense)
			m.hp -= damage
			if m.hp <= 0 {
				g.messages = append(g.messages, fmt.Sprintf("You killed %s.", m.name))

				//Just remove the monster from the array, it shouldn't re-render
				g.currLevel.monsters = append(g.currLevel.monsters[:i], g.currLevel.monsters[i+1:]...)

				g.player.exp += m.exp
				if g.player.ProcessLevelUp() {
					g.messages = append(g.messages, fmt.Sprintf("You reached level %d!", g.player.level))
				}
			} else {
				g.messages = append(g.messages, fmt.Sprintf("You attacked %s for %d damage.", m.name, damage))
			}
			return true
		}
	}

	g.player.x = newX
	g.player.y = newY

	return true
}

func (g *Game) moveMonster(m *Monster) {
	x, y := g.determineMonsterMoveNewPos(m.x, m.y)

	if g.player.x == x && g.player.y == y {
		damage := calculateDamage(m.strength, g.player.defense)
		g.player.hp -= damage
		if g.player.hp <= 0 {
			g.IsGameOver = true
		} else {
			g.messages = append(g.messages, fmt.Sprintf("%s has attacked you for %d damage.", m.name, damage))
		}
	} else {
		m.x = x
		m.y = y
	}
}

func (g *Game) OnDungeonExit() bool {
	return g.currLevelPos <= 0 && g.player.x == g.currLevel.prevFloorX && g.player.y == g.currLevel.prevFloorY
}

func (g *Game) OnChalice() bool {
	return g.currLevel.cells.get(g.player.x, g.player.y).content == CHALICE
}

func (g *Game) TakeChalice() {
	g.currLevel.cells.get(g.player.x, g.player.y).content = FLOOR
	g.HasChalice = true
	g.messages = append(g.messages, "You took the chalice of riches.")
}

func (g *Game) ChangeFloor() bool {
	if g.player.x == g.currLevel.prevFloorX && g.player.y == g.currLevel.prevFloorY {
		if g.currLevelPos <= 0 {
			return false
		}
		g.currLevelPos--
		g.currLevel = g.levels[g.currLevelPos]
		g.player.x = g.currLevel.nextFloorX
		g.player.y = g.currLevel.nextFloorY
		return true
	} else if g.player.x == g.currLevel.nextFloorX && g.player.y == g.currLevel.nextFloorY {
		if g.currLevelPos >= len(g.levels)-1 {
			//Should never happen
			return false
		}
		g.currLevelPos++
		g.currLevel = g.levels[g.currLevelPos]
		g.player.x = g.currLevel.prevFloorX
		g.player.y = g.currLevel.prevFloorY
		return true
	}
	return false
}

func (g *Game) HealPlayerFromActions() {
	g.player.HealFromActions()
}

func (g *Game) UpdateMonsters() {
	//Order monsters so the nearest ones moves first
	//This prevents closer monsters from blocking the ones behind them by not moving first
	sort.Slice(g.currLevel.monsters, func(i, j int) bool {
		return tileDistance(g.currLevel.monsters[i].x, g.currLevel.monsters[i].y, g.player.x, g.player.y) <
			tileDistance(g.currLevel.monsters[j].x, g.currLevel.monsters[j].y, g.player.x, g.player.y)
	})

	for _, m := range g.currLevel.monsters {
		if g.currLevel.cells.get(m.x, m.y).visible {
			m.active = true
		}

		if m.active {
			g.moveMonster(m)
		}
	}
}

//Given the input (x, y), what is the best way to move towards the player
func (g *Game) determineMonsterMoveNewPos(x, y int) (int, int) {
	nearestToPlayer, distance := g.nearestAdjacentPlayerSpot(x, y)

	//We are already beside player
	if distance == 0 {
		return g.player.x, g.player.y
	}

	//We could not find a nearest spot
	if nearestToPlayer[0] < 0 {
		return x, y
	}

	potentialMovements := [][]int{
		[]int{x - 1, y},
		[]int{x + 1, y},
		[]int{x, y - 1},
		[]int{x, y + 1},
	}

	//Can we move closer to that spot?
	for _, movement := range potentialMovements {
		if g.monsterCanMoveTo(movement[0], movement[1]) &&
			tileDistance(movement[0], movement[1], nearestToPlayer[0], nearestToPlayer[1]) < distance {
			return movement[0], movement[1]
		}
	}

	//No better move, stay still
	return x, y
}

func (g *Game) nearestAdjacentPlayerSpot(x, y int) (nearestSpot []int, distance int) {
	nearestSpot = []int{-1, -1}
	distance = math.MaxInt32

	potentialSpots := [][]int{
		[]int{g.player.x - 1, g.player.y},
		[]int{g.player.x + 1, g.player.y},
		[]int{g.player.x, g.player.y - 1},
		[]int{g.player.x, g.player.y + 1},
	}

	for _, spot := range potentialSpots {
		//Already adjacent spot, just return this spot and don't move
		if x == spot[0] && y == spot[1] {
			nearestSpot = spot
			distance = 0
			break
		}

		if !g.monsterCanMoveTo(spot[0], spot[1]) {
			continue
		}

		currDist := tileDistance(x, y, spot[0], spot[1])
		if currDist < distance {
			distance = currDist
			nearestSpot = spot
		}
	}
	return
}

func (g *Game) monsterCanMoveTo(x, y int) bool {
	c := g.currLevel.cells.get(x, y)
	if c.content == WALL {
		return false
	}

	for _, m := range g.currLevel.monsters {
		if m.x == x && m.y == y {
			return false
		}
	}
	return true
}

func (g *Game) ClearMessages() {
	g.messages = g.messages[:0]
}

func (g *Game) StopMessageChan() {
	if g.renderedOnce {
		g.clearMessageChan <- struct{}{}
	}
}
<|endoftext|>


<commit_before>package game

import (
  "fmt"
)

type Game struct {
  Name string
  SetupRules []SetupRule
}

func NewGame(name string, rules []SetupRule) *Game {
  return &Game{
    Name: name,
    SetupRules: rules,
  }
}

func (game *Game) PrintSetupRules() {
  for _,r := range game.SetupRules {
    fmt.Printf("%s\t%s\n", r.Description, r.Arity)
  }
}
<commit_msg>Add id and make fields work with Ember default JSON serialization<commit_after>package game

import (
  "fmt"
)

type Game struct {
  Id uint `json:"id"`
  Name string `json:"name"`
  SetupRules []SetupRule
}

func NewGame(name string, rules []SetupRule) *Game {
  return &Game{
    Name: name,
    SetupRules: rules,
  }
}

func (game *Game) PrintSetupRules() {
  for _,r := range game.SetupRules {
    fmt.Printf("%s\t%s\n", r.Description, r.Arity)
  }
}
<|endoftext|>


<commit_before>// Copyright 2015 The http2amqp Authors. All rights reserved.  Use of this
// source code is governed by a MIT-style license that can be found in the
// LICENSE file.

package queries_service

import (
	"encoding/json"
	"errors"
	"time"

	"github.com/aleasoluciones/simpleamqp"
)

type Result interface{}

type QueriesService interface {
	Query(topic string, criteria Criteria) (Result, error)
}

func NewQueriesService(amqpPublisher simpleamqp.AMQPPublisher, amqpConsumer simpleamqp.AMQPConsumer, timeout time.Duration) QueriesService {
	service := queriesService{
		amqpConsumer:  amqpConsumer,
		amqpPublisher: amqpPublisher,
		queryTimeout:  timeout,

		queries:   make(chan query),
		responses: make(chan responseMessage),
	}

	go service.dispatch()
	go service.receiveResponses()

	return &service
}

type queriesService struct {
	amqpConsumer  simpleamqp.AMQPConsumer
	amqpPublisher simpleamqp.AMQPPublisher
	queryTimeout  time.Duration

	queries   chan query
	responses chan responseMessage
}

type query struct {
	Id              int
	RoutingKey      string
	CriteriaMessage Criteria
	Responses       chan responseMessage
}
type amqpQueryMessage struct {
	Id              int      `json:"id"`
	CriteriaMessage Criteria `json:"criteria"`
}

type responseMessage struct {
	Id      int
	Message interface{}
}

func (service *queriesService) receiveResponses() {
	amqpResponses := service.amqpConsumer.Receive("an exchange", []string{"a topic"}, "no se", simpleamqp.QueueOptions{}, 1*time.Minute)

	var deserialized map[string]interface{}

	for message := range amqpResponses {
		_ = json.Unmarshal([]byte(message.Body), &deserialized)

		if deserialized["id"] != nil {
			service.responses <- responseMessage{
				Id:      int(deserialized["id"].(float64)),
				Message: deserialized["content"],
			}
		}
	}
}

func (service *queriesService) dispatch() {
	var responses chan responseMessage
	var found bool

	queryResponses := map[int]chan responseMessage{}
	id := 0

	for {
		select {
		case query := <-service.queries:
			query.Id = id
			queryResponses[id] = query.Responses
			messageToSend := amqpQueryMessage{Id: id, CriteriaMessage: query.CriteriaMessage}
			messageToSendJson, _ := json.Marshal(messageToSend)
			service.amqpPublisher.Publish(query.RoutingKey, messageToSendJson)
			id += 1
		case response := <-service.responses:
			responses, found = queryResponses[response.Id]
			if found {
				responses <- response
			}
		}
	}
}

type Criteria map[string]string

func (service *queriesService) Query(topic string, criteria Criteria) (Result, error) {
	responses := make(chan responseMessage)
	service.queries <- query{
		RoutingKey:      topic,
		CriteriaMessage: criteria,
		Responses:       responses,
	}

	afterTimeoutTicker := time.NewTicker(service.queryTimeout)
	defer afterTimeoutTicker.Stop()
	afterTimeout := afterTimeoutTicker.C

	select {
	case response := <-responses:
		return response.Message, nil
	case <-afterTimeout:
		return nil, errors.New("Timeout")
	}
}
<commit_msg>Refactor<commit_after>// Copyright 2015 The http2amqp Authors. All rights reserved.  Use of this
// source code is governed by a MIT-style license that can be found in the
// LICENSE file.

package queries_service

import (
	"encoding/json"
	"errors"
	"time"

	"github.com/aleasoluciones/simpleamqp"
)

type Result interface{}

type QueriesService interface {
	Query(topic string, criteria Criteria) (Result, error)
}

func NewQueriesService(amqpPublisher simpleamqp.AMQPPublisher, amqpConsumer simpleamqp.AMQPConsumer, timeout time.Duration) QueriesService {
	service := queriesService{
		amqpConsumer:  amqpConsumer,
		amqpPublisher: amqpPublisher,
		queryTimeout:  timeout,

		queries:   make(chan query),
		responses: make(chan responseMessage),
	}

	go service.dispatch()
	go service.receiveResponses()

	return &service
}

type queriesService struct {
	amqpConsumer  simpleamqp.AMQPConsumer
	amqpPublisher simpleamqp.AMQPPublisher
	queryTimeout  time.Duration

	queries   chan query
	responses chan responseMessage
}

type query struct {
	RoutingKey     string
	CriteriaValues Criteria
	Responses      chan responseMessage
}

type amqpQueryMessage struct {
	Id             int      `json:"id"`
	CriteriaValues Criteria `json:"criteria"`
}

type responseMessage struct {
	Id      int
	Message interface{}
}

func (service *queriesService) receiveResponses() {
	amqpResponses := service.amqpConsumer.Receive("an exchange", []string{"a topic"}, "no se", simpleamqp.QueueOptions{}, 1*time.Minute)

	var deserialized map[string]interface{}

	for message := range amqpResponses {
		_ = json.Unmarshal([]byte(message.Body), &deserialized)

		if deserialized["id"] != nil {
			service.responses <- responseMessage{
				Id:      int(deserialized["id"].(float64)),
				Message: deserialized["content"],
			}
		}
	}
}

func (service *queriesService) dispatch() {
	var responses chan responseMessage
	var found bool

	queryResponses := map[int]chan responseMessage{}
	id := 0

	for {
		select {
		case query := <-service.queries:
			queryResponses[id] = query.Responses
			service.publishQuery(id, query)
			id += 1
		case response := <-service.responses:
			responses, found = queryResponses[response.Id]
			if found {
				responses <- response
			}
		}
	}
}

func (service *queriesService) publishQuery(id int, query query) {
	messageToSendJson, _ := json.Marshal(amqpQueryMessage{
		Id:             id,
		CriteriaValues: query.CriteriaValues,
	})

	service.amqpPublisher.Publish(query.RoutingKey, messageToSendJson)
}

type Criteria map[string]string

func (service *queriesService) Query(topic string, criteria Criteria) (Result, error) {
	responses := make(chan responseMessage)
	service.queries <- query{
		RoutingKey:     topic,
		CriteriaValues: criteria,
		Responses:      responses,
	}

	afterTimeoutTicker := time.NewTicker(service.queryTimeout)
	defer afterTimeoutTicker.Stop()
	afterTimeout := afterTimeoutTicker.C

	select {
	case response := <-responses:
		return response.Message, nil
	case <-afterTimeout:
		return nil, errors.New("Timeout")
	}
}
<|endoftext|>


<commit_before>package gaws

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var notFoundError = AWSError{Type: "NotFound", Message: "Could not find something"}
var throttlingError = AWSError{Type: "Throttling", Message: "You have been throttled"}

func testHTTP200(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("OK"))
}

func testHTTP404(w http.ResponseWriter, r *http.Request) {
	b, _ := json.Marshal(notFoundError)

	w.WriteHeader(404)
	w.Write([]byte(b))
}

func testAWSThrottle(w http.ResponseWriter, r *http.Request) {
	b, _ := json.Marshal(throttlingError)

	w.WriteHeader(400)
	w.Write([]byte(b))
}

func TestSuccess(t *testing.T) {
	Convey("A successful request works", t, func() {
		ts := httptest.NewServer(http.HandlerFunc(testHTTP200))
		defer ts.Close()

		req, _ := http.NewRequest("GET", ts.URL, nil)

		_, err := SendAWSRequest(req)

		Convey("There should be no errors", func() {
			So(err, ShouldBeNil)
		})

	})
}

func TestFailNoRetry(t *testing.T) {
	Convey("Requests that return 404 do not retry", t, func() {

		ts := httptest.NewServer(http.HandlerFunc(testHTTP404))
		defer ts.Close()

		req, _ := http.NewRequest("GET", ts.URL, nil)

		_, err := SendAWSRequest(req)

		Convey("SendAWSRequest should return an error", func() {
			So(err, ShouldNotBeNil)
		})

		Convey("SendAWSRequest should return a not found error", func() {
			So(err.Error(), ShouldEqual, notFoundError.Error())
		})

	})
}

func TestThrottleRetry(t *testing.T) {
	Convey("Requests that return a 400 throttle error retry", t, func() {

		ts := httptest.NewServer(http.HandlerFunc(testAWSThrottle))
		defer ts.Close()

		req, _ := http.NewRequest("GET", ts.URL, nil)

		_, err := SendAWSRequest(req)

		Convey("SendAWSRequest should return an error", func() {
			So(err, ShouldNotBeNil)
		})

		Convey("SendAWSRequest should return an exceeded retries error", func() {
			So(err.Error(), ShouldEqual, exceededRetriesError.Error())
		})

	})
}
<commit_msg>adding non-json test, rewording tests<commit_after>package gaws

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var notFoundError = AWSError{Type: "NotFound", Message: "Could not find something"}
var throttlingError = AWSError{Type: "Throttling", Message: "You have been throttled"}

func testHTTP200(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("OK"))
}

func testHTTP404(w http.ResponseWriter, r *http.Request) {
	b, _ := json.Marshal(notFoundError)

	w.WriteHeader(404)
	w.Write([]byte(b))
}

func testHTTP404NonJson(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(404)
	w.Write([]byte("I am not JSON!"))
}

func testAWSThrottle(w http.ResponseWriter, r *http.Request) {
	b, _ := json.Marshal(throttlingError)

	w.WriteHeader(400)
	w.Write([]byte(b))
}

func TestSuccess(t *testing.T) {
	Convey("Given a request sent to a server that always returns 200s", t, func() {
		ts := httptest.NewServer(http.HandlerFunc(testHTTP200))
		defer ts.Close()

		req, _ := http.NewRequest("GET", ts.URL, nil)

		_, err := SendAWSRequest(req)

		Convey("SendAWSRequest will not return errors", func() {
			So(err, ShouldBeNil)
		})

	})
}

func TestFailBadJson(t *testing.T) {
	Convey("Given a server that returns 404 errors without JSON", t, func() {

		ts := httptest.NewServer(http.HandlerFunc(testHTTP404NonJson))
		defer ts.Close()

		req, _ := http.NewRequest("GET", ts.URL, nil)

		_, err := SendAWSRequest(req)

		Convey("SendAWSRequest should return an error", func() {
			So(err, ShouldNotBeNil)
		})

	})
}

func TestFailNoRetry(t *testing.T) {
	Convey("Given a server that returns 404 errors with proper JSON", t, func() {

		ts := httptest.NewServer(http.HandlerFunc(testHTTP404))
		defer ts.Close()

		req, _ := http.NewRequest("GET", ts.URL, nil)

		_, err := SendAWSRequest(req)

		Convey("SendAWSRequest should return an error", func() {
			So(err, ShouldNotBeNil)
		})

		Convey("SendAWSRequest should return a not found error (and not attempt to retry)", func() {
			So(err.Error(), ShouldEqual, notFoundError.Error())
		})

	})
}

func TestThrottleRetry(t *testing.T) {
	Convey("Given a server that only returns 400 errors with the Trottle type", t, func() {

		ts := httptest.NewServer(http.HandlerFunc(testAWSThrottle))
		defer ts.Close()

		req, _ := http.NewRequest("GET", ts.URL, nil)

		_, err := SendAWSRequest(req)

		Convey("SendAWSRequest should return an error", func() {
			So(err, ShouldNotBeNil)
		})

		Convey("SendAWSRequest should return an exceeded retries error", func() {
			So(err.Error(), ShouldEqual, exceededRetriesError.Error())
		})

	})
}
<|endoftext|>


<commit_before>package gcsresource

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"

	"golang.org/x/oauth2"
	oauthgoogle "golang.org/x/oauth2/google"
	"google.golang.org/api/googleapi"
	"google.golang.org/api/storage/v1"
	"gopkg.in/cheggaaa/pb.v1"
)

type GCSClient interface {
	BucketObjects(bucketName string, prefix string) ([]string, error)
	ObjectGenerations(bucketName string, objectPath string) ([]int64, error)
	DownloadFile(bucketName string, objectPath string, generation int64, localPath string) error
	UploadFile(bucketName string, objectPath string, objectContentType string, localPath string, predefinedACL string) (int64, error)
	URL(bucketName string, objectPath string, generation int64) (string, error)
	DeleteObject(bucketName string, objectPath string, generation int64) error
	GetBucketObjectInfo(bucketName, objectPath string) (*storage.Object, error)
}

type gcsclient struct {
	storageService *storage.Service
	progressOutput io.Writer
}

func NewGCSClient(
	progressOutput io.Writer,
	jsonKey string,
) (GCSClient, error) {
	var err error
	var storageClient *http.Client
	var userAgent = "gcs-resource/0.0.1"

	if jsonKey != "" {
		storageJwtConf, err := oauthgoogle.JWTConfigFromJSON([]byte(jsonKey), storage.DevstorageFullControlScope)
		if err != nil {
			return &gcsclient{}, err
		}
		storageClient = storageJwtConf.Client(oauth2.NoContext)
	} else {
		storageClient, err = oauthgoogle.DefaultClient(oauth2.NoContext, storage.DevstorageFullControlScope)
		if err != nil {
			return &gcsclient{}, err
		}
	}

	storageService, err := storage.New(storageClient)
	if err != nil {
		return &gcsclient{}, err
	}
	storageService.UserAgent = userAgent

	return &gcsclient{
		storageService: storageService,
		progressOutput: progressOutput,
	}, nil
}

func (gcsclient *gcsclient) BucketObjects(bucketName string, prefix string) ([]string, error) {
	bucketObjects, err := gcsclient.getBucketObjects(bucketName, prefix)
	if err != nil {
		return []string{}, err
	}

	return bucketObjects, nil
}

func (gcsclient *gcsclient) ObjectGenerations(bucketName string, objectPath string) ([]int64, error) {
	isBucketVersioned, err := gcsclient.getBucketVersioning(bucketName)
	if err != nil {
		return []int64{}, err
	}

	if !isBucketVersioned {
		return []int64{}, errors.New("bucket is not versioned")
	}

	objectGenerations, err := gcsclient.getObjectGenerations(bucketName, objectPath)
	if err != nil {
		return []int64{}, err
	}

	return objectGenerations, nil
}

func (gcsclient *gcsclient) DownloadFile(bucketName string, objectPath string, generation int64, localPath string) error {
	isBucketVersioned, err := gcsclient.getBucketVersioning(bucketName)
	if err != nil {
		return err
	}

	if !isBucketVersioned && generation != 0 {
		return errors.New("bucket is not versioned")
	}

	getCall := gcsclient.storageService.Objects.Get(bucketName, objectPath)
	if generation != 0 {
		getCall = getCall.Generation(generation)
	}

	object, err := getCall.Do()
	if err != nil {
		return err
	}

	localFile, err := os.Create(localPath)
	if err != nil {
		return err
	}
	defer localFile.Close()

	progress := gcsclient.newProgressBar(int64(object.Size))
	progress.Start()
	defer progress.Finish()

	response, err := getCall.Download()
	if err != nil {
		return err
	}
	defer response.Body.Close()

	reader := progress.NewProxyReader(response.Body)
	_, err = io.Copy(localFile, reader)
	if err != nil {
		return err
	}

	return nil
}

func (gcsclient *gcsclient) UploadFile(bucketName string, objectPath string, objectContentType string, localPath string, predefinedACL string) (int64, error) {
	isBucketVersioned, err := gcsclient.getBucketVersioning(bucketName)
	if err != nil {
		return 0, err
	}

	stat, err := os.Stat(localPath)
	if err != nil {
		return 0, err
	}

	localFile, err := os.Open(localPath)
	if err != nil {
		return 0, err
	}
	defer localFile.Close()

	progress := gcsclient.newProgressBar(stat.Size())
	progress.Start()
	defer progress.Finish()

	object := &storage.Object{
		Name:        objectPath,
		ContentType: objectContentType,
	}

	var mediaOptions []googleapi.MediaOption
	if objectContentType != "" {
		mediaOptions = append(mediaOptions, googleapi.ContentType(objectContentType))
	}

	insertCall := gcsclient.storageService.Objects.Insert(bucketName, object).Media(progress.NewProxyReader(localFile), mediaOptions...)
	if predefinedACL != "" {
		insertCall = insertCall.PredefinedAcl(predefinedACL)
	}

	uploadedObject, err := insertCall.Do()
	if err != nil {
		return 0, err
	}

	if isBucketVersioned {
		return uploadedObject.Generation, nil
	}

	return 0, nil
}

func (gcsclient *gcsclient) URL(bucketName string, objectPath string, generation int64) (string, error) {
	getCall := gcsclient.storageService.Objects.Get(bucketName, objectPath)
	if generation != 0 {
		getCall = getCall.Generation(generation)
	}

	_, err := getCall.Do()
	if err != nil {
		return "", err
	}

	var url string
	if generation != 0 {
		url = fmt.Sprintf("gs://%s/%s#%d", bucketName, objectPath, generation)
	} else {
		url = fmt.Sprintf("gs://%s/%s", bucketName, objectPath)
	}

	return url, nil
}

func (gcsclient *gcsclient) DeleteObject(bucketName string, objectPath string, generation int64) error {
	deleteCall := gcsclient.storageService.Objects.Delete(bucketName, objectPath)
	if generation != 0 {
		deleteCall = deleteCall.Generation(generation)
	}

	err := deleteCall.Do()
	if err != nil {
		return err
	}

	return nil
}

func (gcsclient *gcsclient) GetBucketObjectInfo(bucketName, objectPath string) (*storage.Object, error) {
	getCall := gcsclient.storageService.Objects.Get(bucketName, objectPath)
	object, err := getCall.Do()
	if err != nil {
		return nil, err
	}

	return object, nil
}

func (gcsclient *gcsclient) getBucketObjects(bucketName string, prefix string) ([]string, error) {
	var bucketObjects []string

	pageToken := ""
	for {
		listCall := gcsclient.storageService.Objects.List(bucketName)
		listCall = listCall.PageToken(pageToken)
		listCall = listCall.Prefix(prefix)
		listCall = listCall.Versions(false)

		objects, err := listCall.Do()
		if err != nil {
			return bucketObjects, err
		}

		for _, object := range objects.Items {
			bucketObjects = append(bucketObjects, object.Name)
		}

		if objects.NextPageToken != "" {
			pageToken = objects.NextPageToken
		} else {
			break
		}
	}

	return bucketObjects, nil
}

func (gcsclient *gcsclient) getBucketVersioning(bucketName string) (bool, error) {
	bucket, err := gcsclient.storageService.Buckets.Get(bucketName).Do()
	if err != nil {
		return false, err
	}

	if bucket.Versioning != nil {
		return bucket.Versioning.Enabled, nil
	}

	return false, nil
}

func (gcsclient *gcsclient) getObjectGenerations(bucketName string, objectPath string) ([]int64, error) {
	var objectGenerations []int64

	pageToken := ""
	for {
		listCall := gcsclient.storageService.Objects.List(bucketName)
		listCall = listCall.PageToken(pageToken)
		listCall = listCall.Prefix(objectPath)
		listCall = listCall.Versions(true)

		objects, err := listCall.Do()
		if err != nil {
			return objectGenerations, err
		}

		for _, object := range objects.Items {
			if object.Name == objectPath {
				objectGenerations = append(objectGenerations, object.Generation)
			}
		}

		if objects.NextPageToken != "" {
			pageToken = objects.NextPageToken
		} else {
			break
		}
	}

	return objectGenerations, nil
}

func (gcsclient *gcsclient) newProgressBar(total int64) *pb.ProgressBar {
	progress := pb.New64(total)

	progress.Output = gcsclient.progressOutput
	progress.ShowSpeed = true
	progress.Units = pb.U_BYTES
	progress.NotPrint = true

	return progress.SetWidth(80)
}
<commit_msg>Add go:generate for counterfieter mock generation<commit_after>package gcsresource

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"

	"golang.org/x/oauth2"
	oauthgoogle "golang.org/x/oauth2/google"
	"google.golang.org/api/googleapi"
	"google.golang.org/api/storage/v1"
	"gopkg.in/cheggaaa/pb.v1"
)

//go:generate counterfeiter -o fakes/fake_gcsclient.go . GCSClient
type GCSClient interface {
	BucketObjects(bucketName string, prefix string) ([]string, error)
	ObjectGenerations(bucketName string, objectPath string) ([]int64, error)
	DownloadFile(bucketName string, objectPath string, generation int64, localPath string) error
	UploadFile(bucketName string, objectPath string, objectContentType string, localPath string, predefinedACL string) (int64, error)
	URL(bucketName string, objectPath string, generation int64) (string, error)
	DeleteObject(bucketName string, objectPath string, generation int64) error
	GetBucketObjectInfo(bucketName, objectPath string) (*storage.Object, error)
}

type gcsclient struct {
	storageService *storage.Service
	progressOutput io.Writer
}

func NewGCSClient(
	progressOutput io.Writer,
	jsonKey string,
) (GCSClient, error) {
	var err error
	var storageClient *http.Client
	var userAgent = "gcs-resource/0.0.1"

	if jsonKey != "" {
		storageJwtConf, err := oauthgoogle.JWTConfigFromJSON([]byte(jsonKey), storage.DevstorageFullControlScope)
		if err != nil {
			return &gcsclient{}, err
		}
		storageClient = storageJwtConf.Client(oauth2.NoContext)
	} else {
		storageClient, err = oauthgoogle.DefaultClient(oauth2.NoContext, storage.DevstorageFullControlScope)
		if err != nil {
			return &gcsclient{}, err
		}
	}

	storageService, err := storage.New(storageClient)
	if err != nil {
		return &gcsclient{}, err
	}
	storageService.UserAgent = userAgent

	return &gcsclient{
		storageService: storageService,
		progressOutput: progressOutput,
	}, nil
}

func (gcsclient *gcsclient) BucketObjects(bucketName string, prefix string) ([]string, error) {
	bucketObjects, err := gcsclient.getBucketObjects(bucketName, prefix)
	if err != nil {
		return []string{}, err
	}

	return bucketObjects, nil
}

func (gcsclient *gcsclient) ObjectGenerations(bucketName string, objectPath string) ([]int64, error) {
	isBucketVersioned, err := gcsclient.getBucketVersioning(bucketName)
	if err != nil {
		return []int64{}, err
	}

	if !isBucketVersioned {
		return []int64{}, errors.New("bucket is not versioned")
	}

	objectGenerations, err := gcsclient.getObjectGenerations(bucketName, objectPath)
	if err != nil {
		return []int64{}, err
	}

	return objectGenerations, nil
}

func (gcsclient *gcsclient) DownloadFile(bucketName string, objectPath string, generation int64, localPath string) error {
	isBucketVersioned, err := gcsclient.getBucketVersioning(bucketName)
	if err != nil {
		return err
	}

	if !isBucketVersioned && generation != 0 {
		return errors.New("bucket is not versioned")
	}

	getCall := gcsclient.storageService.Objects.Get(bucketName, objectPath)
	if generation != 0 {
		getCall = getCall.Generation(generation)
	}

	object, err := getCall.Do()
	if err != nil {
		return err
	}

	localFile, err := os.Create(localPath)
	if err != nil {
		return err
	}
	defer localFile.Close()

	progress := gcsclient.newProgressBar(int64(object.Size))
	progress.Start()
	defer progress.Finish()

	response, err := getCall.Download()
	if err != nil {
		return err
	}
	defer response.Body.Close()

	reader := progress.NewProxyReader(response.Body)
	_, err = io.Copy(localFile, reader)
	if err != nil {
		return err
	}

	return nil
}

func (gcsclient *gcsclient) UploadFile(bucketName string, objectPath string, objectContentType string, localPath string, predefinedACL string) (int64, error) {
	isBucketVersioned, err := gcsclient.getBucketVersioning(bucketName)
	if err != nil {
		return 0, err
	}

	stat, err := os.Stat(localPath)
	if err != nil {
		return 0, err
	}

	localFile, err := os.Open(localPath)
	if err != nil {
		return 0, err
	}
	defer localFile.Close()

	progress := gcsclient.newProgressBar(stat.Size())
	progress.Start()
	defer progress.Finish()

	object := &storage.Object{
		Name:        objectPath,
		ContentType: objectContentType,
	}

	var mediaOptions []googleapi.MediaOption
	if objectContentType != "" {
		mediaOptions = append(mediaOptions, googleapi.ContentType(objectContentType))
	}

	insertCall := gcsclient.storageService.Objects.Insert(bucketName, object).Media(progress.NewProxyReader(localFile), mediaOptions...)
	if predefinedACL != "" {
		insertCall = insertCall.PredefinedAcl(predefinedACL)
	}

	uploadedObject, err := insertCall.Do()
	if err != nil {
		return 0, err
	}

	if isBucketVersioned {
		return uploadedObject.Generation, nil
	}

	return 0, nil
}

func (gcsclient *gcsclient) URL(bucketName string, objectPath string, generation int64) (string, error) {
	getCall := gcsclient.storageService.Objects.Get(bucketName, objectPath)
	if generation != 0 {
		getCall = getCall.Generation(generation)
	}

	_, err := getCall.Do()
	if err != nil {
		return "", err
	}

	var url string
	if generation != 0 {
		url = fmt.Sprintf("gs://%s/%s#%d", bucketName, objectPath, generation)
	} else {
		url = fmt.Sprintf("gs://%s/%s", bucketName, objectPath)
	}

	return url, nil
}

func (gcsclient *gcsclient) DeleteObject(bucketName string, objectPath string, generation int64) error {
	deleteCall := gcsclient.storageService.Objects.Delete(bucketName, objectPath)
	if generation != 0 {
		deleteCall = deleteCall.Generation(generation)
	}

	err := deleteCall.Do()
	if err != nil {
		return err
	}

	return nil
}

func (gcsclient *gcsclient) GetBucketObjectInfo(bucketName, objectPath string) (*storage.Object, error) {
	getCall := gcsclient.storageService.Objects.Get(bucketName, objectPath)
	object, err := getCall.Do()
	if err != nil {
		return nil, err
	}

	return object, nil
}

func (gcsclient *gcsclient) getBucketObjects(bucketName string, prefix string) ([]string, error) {
	var bucketObjects []string

	pageToken := ""
	for {
		listCall := gcsclient.storageService.Objects.List(bucketName)
		listCall = listCall.PageToken(pageToken)
		listCall = listCall.Prefix(prefix)
		listCall = listCall.Versions(false)

		objects, err := listCall.Do()
		if err != nil {
			return bucketObjects, err
		}

		for _, object := range objects.Items {
			bucketObjects = append(bucketObjects, object.Name)
		}

		if objects.NextPageToken != "" {
			pageToken = objects.NextPageToken
		} else {
			break
		}
	}

	return bucketObjects, nil
}

func (gcsclient *gcsclient) getBucketVersioning(bucketName string) (bool, error) {
	bucket, err := gcsclient.storageService.Buckets.Get(bucketName).Do()
	if err != nil {
		return false, err
	}

	if bucket.Versioning != nil {
		return bucket.Versioning.Enabled, nil
	}

	return false, nil
}

func (gcsclient *gcsclient) getObjectGenerations(bucketName string, objectPath string) ([]int64, error) {
	var objectGenerations []int64

	pageToken := ""
	for {
		listCall := gcsclient.storageService.Objects.List(bucketName)
		listCall = listCall.PageToken(pageToken)
		listCall = listCall.Prefix(objectPath)
		listCall = listCall.Versions(true)

		objects, err := listCall.Do()
		if err != nil {
			return objectGenerations, err
		}

		for _, object := range objects.Items {
			if object.Name == objectPath {
				objectGenerations = append(objectGenerations, object.Generation)
			}
		}

		if objects.NextPageToken != "" {
			pageToken = objects.NextPageToken
		} else {
			break
		}
	}

	return objectGenerations, nil
}

func (gcsclient *gcsclient) newProgressBar(total int64) *pb.ProgressBar {
	progress := pb.New64(total)

	progress.Output = gcsclient.progressOutput
	progress.ShowSpeed = true
	progress.Units = pb.U_BYTES
	progress.NotPrint = true

	return progress.SetWidth(80)
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package gen

import (
	"bytes"
	"fmt"
	"go/token"
	"path/filepath"
	"strings"

	"github.com/thriftrw/thriftrw-go/compile"
)

// TODO: This should be moved into a separate program or template

// YARPC generates YARPC-specific code for a service.
//
// Each service gets a ${serviceName}server and ${serviceName}client package.
func YARPC(i thriftPackageImporter, s *compile.ServiceSpec) (map[string]*bytes.Buffer, error) {
	files := make(map[string]*bytes.Buffer)

	thriftPackage, err := i.Package(s.ThriftFile())
	if err != nil {
		return nil, err
	}

	serverPkgName := strings.ToLower(s.Name) + "server"
	serverImportPath := filepath.Join(thriftPackage, "yarpc", serverPkgName)
	serverFile, err := yarpcGenerator{
		NewGenerator(i, serverImportPath, serverPkgName), i,
	}.server(s)
	if err != nil {
		return nil, err
	}
	files[filepath.Join(serverPkgName, "server.go")] = serverFile

	clientPkgName := strings.ToLower(s.Name) + "client"
	clientImportPath := filepath.Join(thriftPackage, "yarpc", clientPkgName)
	clientFile, err := yarpcGenerator{
		NewGenerator(i, clientImportPath, clientPkgName), i,
	}.client(s)
	if err != nil {
		return nil, err
	}
	files[filepath.Join(clientPkgName, "client.go")] = clientFile

	return files, nil
}

type yarpcGenerator struct {
	g Generator
	i thriftPackageImporter
}

func (yg yarpcGenerator) server(s *compile.ServiceSpec) (*bytes.Buffer, error) {
	if err := yg.iface(s, true); err != nil {
		return nil, err
	}

	err := yg.g.DeclareFromTemplate(
		`
		<$thrift := import "github.com/yarpc/yarpc-go/encoding/thrift">
		<$protocol := import "github.com/thriftrw/thriftrw-go/protocol">
		<$wire := import "github.com/thriftrw/thriftrw-go/wire">

		func New(impl Interface) <$thrift>.Service {
			return service{handler{impl}}
		}

		type service struct{h handler}

		func (service) Name() string {
			 return "<.Name>"
		 }

		func (service) Protocol() <$protocol>.Protocol {
			return <$protocol>.Binary
		}

		func (s service) Handlers() map[string]<$thrift>.Handler {
			return map[string]<$thrift>.Handler{
				<range .Functions>
					"<.Name>": <$thrift>.HandlerFunc(s.h.<goCase .Name>),
				<end>
			}
		}

		type handler struct{impl Interface}

		<$service := .>
		<range .Functions>
			<$servicePackage := servicePackage $service>
			<$Args := printf "%s.%sArgs" $servicePackage (goCase .Name)>
			<$Helper := printf "%s.%sHelper" $servicePackage (goCase .Name)>

			<$vars := newNamespace>
			<$req := $vars.NewName "req">
			<$body := $vars.NewName "body">

			func (h handler) <goCase .Name>(
				<$req> *<$thrift>.Request,
				<$body> <$wire>.Value,
			) (<$wire>.Value, *<$thrift>.Response, error) {

				<$args := $vars.NewName "args">
				var <$args> <$Args>
				if err := <$args>.FromWire(<$body>); err != nil {
					return <$wire>.Value{}, nil, err
				}

				<$res := $vars.NewName "res">
				<$succ := $vars.NewName "success">
				<if .ResultSpec.ReturnType>
					<$succ>,
				<end>
				<$res>, err := h.impl.<goCase .Name>(
					<$req>,
					<range .ArgsSpec><$args>.<goCase .Name>,<end>
				)

				<$result := $vars.NewName "result">
				<$result>, err := <$Helper>.WrapResponse(
					<if .ResultSpec.ReturnType>
						<$succ>,
					<end>
					err)
				<$w := $vars.NewName "w">
				var <$w> <$wire>.Value
				if err == nil {
					<$w>, err = <$result>.ToWire()
				}
				return <$w>, <$res>, err
			}
		<end>
		`,
		s)

	if err != nil {
		return nil, err
	}

	buff := new(bytes.Buffer)
	if err := yg.g.Write(buff, token.NewFileSet()); err != nil {
		return nil, fmt.Errorf(
			"failed to write YARPC server for service %q: %v", s.Name, err)
	}

	return buff, nil
}

func (yg yarpcGenerator) client(s *compile.ServiceSpec) (*bytes.Buffer, error) {
	if err := yg.iface(s, false); err != nil {
		return nil, err
	}

	err := yg.g.DeclareFromTemplate(
		`
		<$transport := import "github.com/yarpc/yarpc-go/transport">
		<$thrift := import "github.com/yarpc/yarpc-go/encoding/thrift">
		<$protocol := import "github.com/thriftrw/thriftrw-go/protocol">

		func New(c <$transport>.Channel) Interface {
			return client{
				c: <$thrift>.New(<$thrift>.Config{
					Service: "<.Name>",
					Channel: c,
					Protocol: <$protocol>.Binary,
				}),
			}
		}

		type client struct{c <$thrift>.Client}

		<$service := .>
		<range .Functions>
			<$wire := import "github.com/thriftrw/thriftrw-go/wire">
			<$servicePackage := servicePackage $service>

			<$Result := printf "%s.%sResult" $servicePackage (goCase .Name)>
			<$Helper := printf "%s.%sHelper" $servicePackage (goCase .Name)>

			<$vars := newNamespace>
			func (c client) <goCase .Name>(
				<$vars.NewName "req"> *<$thrift>.Request,
				<range .ArgsSpec>
					<if .Required>
						<$vars.NewName .Name> <typeReference .Type>,
					<else>
						<$vars.NewName .Name> <typeReferencePtr .Type>,
					<end>
				<end>
			) (
				<if .ResultSpec.ReturnType>
					<$vars.NewName "success"> <typeReference .ResultSpec.ReturnType>,
				<end>
				<$vars.NewName "res"> *<$thrift>.Response,
				err error,
			 ) {
				<$req := $vars.Rotate "req">
				<$args := $vars.NewName "args">
				<$args> := <$servicePackage>.<goCase .Name>Helper.Args(
					<range .ArgsSpec>
						<$vars.Rotate .Name>,
					<end>
				)

				<$res := $vars.Rotate "res">
				<$body := $vars.NewName "body">

				<$w := $vars.NewName "w">
				var <$w> <$wire>.Value
				<$w>, err = <$args>.ToWire()
				if err != nil {
					return
				}

				var <$body> <$wire>.Value
				<$body>, <$res>, err = c.c.Call("<.Name>", <$req>, <$w>)
				if err != nil {
					return
				}

				<$result := $vars.NewName "result">
				var <$result> <$Result>
				if err = <$result>.FromWire(<$body>); err != nil {
					return
				}

				<if .ResultSpec.ReturnType>
					<$succ := $vars.Rotate "success">
					<$succ>, err = <$Helper>.UnwrapResponse(&<$result>)
				<else>
					err = <$Helper>.UnwrapResponse(&<$result>)
				<end>
				return
			}
		<end>
		`, s)

	if err != nil {
		return nil, err
	}

	buff := new(bytes.Buffer)
	if err := yg.g.Write(buff, token.NewFileSet()); err != nil {
		return nil, fmt.Errorf(
			"failed to write YARPC server for service %q: %v", s.Name, err)
	}

	return buff, nil
}

// iface generates the service interface for the service and the client.
func (yg yarpcGenerator) iface(s *compile.ServiceSpec, isServer bool) error {
	return yg.g.DeclareFromTemplate(
		`
		<$thrift := import "github.com/yarpc/yarpc-go/encoding/thrift">

		type Interface interface {
			<if .Parent>
				<if isServer>
					<import (serverPackage .Parent)>.Interface
				<else>
					<import (clientPackage .Parent)>.Interface
				<end>
			<end>

			<range .Functions>
				<$params := newNamespace>
				<goCase .Name>(
					<$params.NewName "req"> *<$thrift>.Request,
					<range .ArgsSpec>
						<if .Required>
							<$params.NewName .Name> <typeReference .Type>,
						<else>
							<$params.NewName .Name> <typeReferencePtr .Type>,
						<end>
					<end>
				) (
					<if .ResultSpec.ReturnType>
						<typeReference .ResultSpec.ReturnType>,
					<end>
					*<$thrift>.Response,
					 error,
				 )
			<end>
		}
		`,
		s,
		TemplateFunc("isServer", func() bool { return isServer }),
		TemplateFunc("clientPackage", func(service *compile.ServiceSpec) (string, error) {
			return yg.clientPackage(service)
		}),
		TemplateFunc("serverPackage", func(service *compile.ServiceSpec) (string, error) {
			return yg.serverPackage(service)
		}),
	)
}

func (yg yarpcGenerator) basePackage(s *compile.ServiceSpec) (string, error) {
	pkg, err := yg.i.Package(s.ThriftFile())
	if err != nil {
		return "", err
	}
	return filepath.Join(pkg, "yarpc"), nil
}

func (yg yarpcGenerator) serverPackage(s *compile.ServiceSpec) (string, error) {
	pkg, err := yg.basePackage(s)
	if err != nil {
		return "", err
	}
	return filepath.Join(pkg, strings.ToLower(s.Name)+"server"), nil
}

func (yg yarpcGenerator) clientPackage(s *compile.ServiceSpec) (string, error) {
	pkg, err := yg.basePackage(s)
	if err != nil {
		return "", err
	}
	return filepath.Join(pkg, strings.ToLower(s.Name)+"client"), nil
}
<commit_msg>yarpc: Plug in application errors API (#149)<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package gen

import (
	"bytes"
	"fmt"
	"go/token"
	"path/filepath"
	"strings"

	"github.com/thriftrw/thriftrw-go/compile"
)

// TODO: This should be moved into a separate program or template

// YARPC generates YARPC-specific code for a service.
//
// Each service gets a ${serviceName}server and ${serviceName}client package.
func YARPC(i thriftPackageImporter, s *compile.ServiceSpec) (map[string]*bytes.Buffer, error) {
	files := make(map[string]*bytes.Buffer)

	thriftPackage, err := i.Package(s.ThriftFile())
	if err != nil {
		return nil, err
	}

	serverPkgName := strings.ToLower(s.Name) + "server"
	serverImportPath := filepath.Join(thriftPackage, "yarpc", serverPkgName)
	serverFile, err := yarpcGenerator{
		NewGenerator(i, serverImportPath, serverPkgName), i,
	}.server(s)
	if err != nil {
		return nil, err
	}
	files[filepath.Join(serverPkgName, "server.go")] = serverFile

	clientPkgName := strings.ToLower(s.Name) + "client"
	clientImportPath := filepath.Join(thriftPackage, "yarpc", clientPkgName)
	clientFile, err := yarpcGenerator{
		NewGenerator(i, clientImportPath, clientPkgName), i,
	}.client(s)
	if err != nil {
		return nil, err
	}
	files[filepath.Join(clientPkgName, "client.go")] = clientFile

	return files, nil
}

type yarpcGenerator struct {
	g Generator
	i thriftPackageImporter
}

func (yg yarpcGenerator) server(s *compile.ServiceSpec) (*bytes.Buffer, error) {
	if err := yg.iface(s, true); err != nil {
		return nil, err
	}

	err := yg.g.DeclareFromTemplate(
		`
		<$thrift := import "github.com/yarpc/yarpc-go/encoding/thrift">
		<$protocol := import "github.com/thriftrw/thriftrw-go/protocol">
		<$wire := import "github.com/thriftrw/thriftrw-go/wire">

		func New(impl Interface) <$thrift>.Service {
			return service{handler{impl}}
		}

		type service struct{h handler}

		func (service) Name() string {
			 return "<.Name>"
		 }

		func (service) Protocol() <$protocol>.Protocol {
			return <$protocol>.Binary
		}

		func (s service) Handlers() map[string]<$thrift>.Handler {
			return map[string]<$thrift>.Handler{
				<range .Functions>
					"<.Name>": <$thrift>.HandlerFunc(s.h.<goCase .Name>),
				<end>
			}
		}

		type handler struct{impl Interface}

		<$service := .>
		<range .Functions>
			<$servicePackage := servicePackage $service>
			<$Args := printf "%s.%sArgs" $servicePackage (goCase .Name)>
			<$Helper := printf "%s.%sHelper" $servicePackage (goCase .Name)>

			<$vars := newNamespace>
			<$req := $vars.NewName "req">
			<$body := $vars.NewName "body">

			func (h handler) <goCase .Name>(
				<$req> *<$thrift>.Request,
				<$body> <$wire>.Value,
			) (<$thrift>.TResponse, error) {

				<$args := $vars.NewName "args">
				var <$args> <$Args>
				if err := <$args>.FromWire(<$body>); err != nil {
					return <$thrift>.TResponse{}, err
				}

				<$res := $vars.NewName "res">
				<$succ := $vars.NewName "success">
				<if .ResultSpec.ReturnType>
					<$succ>,
				<end>
				<$res>, err := h.impl.<goCase .Name>(
					<$req>,
					<range .ArgsSpec><$args>.<goCase .Name>,<end>
				)

				<$result := $vars.NewName "result">
				<$hadError := $vars.NewName "hadError">

				<$hadError> := err != nil
				<$result>, err := <$Helper>.WrapResponse(
					<if .ResultSpec.ReturnType>
						<$succ>,
					<end>
					err)

				<$response := $vars.NewName "response">
				var <$response> <$thrift>.TResponse
				if err == nil {
					<$response>.IsApplicationError = <$hadError>
					<$response>.Response = <$res>
					<$response>.Body, err = <$result>.ToWire()
				}
				return <$response>, err
			}
		<end>
		`,
		s)

	if err != nil {
		return nil, err
	}

	buff := new(bytes.Buffer)
	if err := yg.g.Write(buff, token.NewFileSet()); err != nil {
		return nil, fmt.Errorf(
			"failed to write YARPC server for service %q: %v", s.Name, err)
	}

	return buff, nil
}

func (yg yarpcGenerator) client(s *compile.ServiceSpec) (*bytes.Buffer, error) {
	if err := yg.iface(s, false); err != nil {
		return nil, err
	}

	err := yg.g.DeclareFromTemplate(
		`
		<$transport := import "github.com/yarpc/yarpc-go/transport">
		<$thrift := import "github.com/yarpc/yarpc-go/encoding/thrift">
		<$protocol := import "github.com/thriftrw/thriftrw-go/protocol">

		func New(c <$transport>.Channel) Interface {
			return client{
				c: <$thrift>.New(<$thrift>.Config{
					Service: "<.Name>",
					Channel: c,
					Protocol: <$protocol>.Binary,
				}),
			}
		}

		type client struct{c <$thrift>.Client}

		<$service := .>
		<range .Functions>
			<$wire := import "github.com/thriftrw/thriftrw-go/wire">
			<$servicePackage := servicePackage $service>

			<$Result := printf "%s.%sResult" $servicePackage (goCase .Name)>
			<$Helper := printf "%s.%sHelper" $servicePackage (goCase .Name)>

			<$vars := newNamespace>
			func (c client) <goCase .Name>(
				<$vars.NewName "req"> *<$thrift>.Request,
				<range .ArgsSpec>
					<if .Required>
						<$vars.NewName .Name> <typeReference .Type>,
					<else>
						<$vars.NewName .Name> <typeReferencePtr .Type>,
					<end>
				<end>
			) (
				<if .ResultSpec.ReturnType>
					<$vars.NewName "success"> <typeReference .ResultSpec.ReturnType>,
				<end>
				<$vars.NewName "res"> *<$thrift>.Response,
				err error,
			 ) {
				<$req := $vars.Rotate "req">
				<$args := $vars.NewName "args">
				<$args> := <$servicePackage>.<goCase .Name>Helper.Args(
					<range .ArgsSpec>
						<$vars.Rotate .Name>,
					<end>
				)

				<$res := $vars.Rotate "res">
				<$body := $vars.NewName "body">

				<$w := $vars.NewName "w">
				var <$w> <$wire>.Value
				<$w>, err = <$args>.ToWire()
				if err != nil {
					return
				}

				var <$body> <$wire>.Value
				<$body>, <$res>, err = c.c.Call("<.Name>", <$req>, <$w>)
				if err != nil {
					return
				}

				<$result := $vars.NewName "result">
				var <$result> <$Result>
				if err = <$result>.FromWire(<$body>); err != nil {
					return
				}

				<if .ResultSpec.ReturnType>
					<$succ := $vars.Rotate "success">
					<$succ>, err = <$Helper>.UnwrapResponse(&<$result>)
				<else>
					err = <$Helper>.UnwrapResponse(&<$result>)
				<end>
				return
			}
		<end>
		`, s)

	if err != nil {
		return nil, err
	}

	buff := new(bytes.Buffer)
	if err := yg.g.Write(buff, token.NewFileSet()); err != nil {
		return nil, fmt.Errorf(
			"failed to write YARPC server for service %q: %v", s.Name, err)
	}

	return buff, nil
}

// iface generates the service interface for the service and the client.
func (yg yarpcGenerator) iface(s *compile.ServiceSpec, isServer bool) error {
	return yg.g.DeclareFromTemplate(
		`
		<$thrift := import "github.com/yarpc/yarpc-go/encoding/thrift">

		type Interface interface {
			<if .Parent>
				<if isServer>
					<import (serverPackage .Parent)>.Interface
				<else>
					<import (clientPackage .Parent)>.Interface
				<end>
			<end>

			<range .Functions>
				<$params := newNamespace>
				<goCase .Name>(
					<$params.NewName "req"> *<$thrift>.Request,
					<range .ArgsSpec>
						<if .Required>
							<$params.NewName .Name> <typeReference .Type>,
						<else>
							<$params.NewName .Name> <typeReferencePtr .Type>,
						<end>
					<end>
				) (
					<if .ResultSpec.ReturnType>
						<typeReference .ResultSpec.ReturnType>,
					<end>
					*<$thrift>.Response,
					 error,
				 )
			<end>
		}
		`,
		s,
		TemplateFunc("isServer", func() bool { return isServer }),
		TemplateFunc("clientPackage", func(service *compile.ServiceSpec) (string, error) {
			return yg.clientPackage(service)
		}),
		TemplateFunc("serverPackage", func(service *compile.ServiceSpec) (string, error) {
			return yg.serverPackage(service)
		}),
	)
}

func (yg yarpcGenerator) basePackage(s *compile.ServiceSpec) (string, error) {
	pkg, err := yg.i.Package(s.ThriftFile())
	if err != nil {
		return "", err
	}
	return filepath.Join(pkg, "yarpc"), nil
}

func (yg yarpcGenerator) serverPackage(s *compile.ServiceSpec) (string, error) {
	pkg, err := yg.basePackage(s)
	if err != nil {
		return "", err
	}
	return filepath.Join(pkg, strings.ToLower(s.Name)+"server"), nil
}

func (yg yarpcGenerator) clientPackage(s *compile.ServiceSpec) (string, error) {
	pkg, err := yg.basePackage(s)
	if err != nil {
		return "", err
	}
	return filepath.Join(pkg, strings.ToLower(s.Name)+"client"), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"os"
	"github.com/gorilla/mux"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"github.com/unrolled/render"
	"golang.org/x/crypto/bcrypt"
)

var cfg Config

func sendError(rw http.ResponseWriter, status int, message string) {
	envelope := map[string]interface{}{
		"errors": [1]interface{}{
			map[string]interface{}{
				"status": fmt.Sprintf("%d",status),
				"title": message,
				"detail": message,
			},
		},
	}
	render.New().JSON(rw, status, envelope)
}

func sendData(rw http.ResponseWriter, status int, data interface{}) {
	envelope := map[string]interface{}{
		"data": data,
	}
	render.New().JSON(rw, status, envelope)
}

func main() {
	err := LoadConfigInto(&cfg, "config.gcfg")
	if err != nil {
		fmt.Println(err)
		// TODO: die
	}
	// fmt.Println("loaded config", cfg)

	// set up database connection
	db, err := sql.Open("mysql", cfg.Database.User + ":" + cfg.Database.Password + "@/" + cfg.Database.Database)
	if err != nil {
	    panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
	}
	defer db.Close()

	err = db.Ping()
	if err != nil {
	    panic(err.Error()) // proper error handling instead of panic in your app
	}

    // Heroku uses env var to specify port
    port := os.Getenv("PORT")
	if port == "" {
		port = cfg.Server.Port
	}

	// any requests on the regular HTTP port get automatically redirected to the secure home page
	// don't just redirect HTTP to HTTPS, because that doesn't train the user to not use HTTP on the first request
	// TODO: commented out so I can run Apache on 80 on my computer
	// TODO: this didn't work, maybe I have too many web servers running, also Chrome gets pretty confused with port numbers
	// go http.ListenAndServe(cfg.Server.Host + ":80", http.HandlerFunc(func (w http.ResponseWriter, req *http.Request) {
	// 	// TODO: secure app shouldn't have to be at the root of the domain
	// 	http.Redirect(w, req, "https://" + cfg.Server.Host + ":" + port, http.StatusMovedPermanently)
	// }))

    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)

    api := r.PathPrefix("/api").Subrouter()

    api.HandleFunc("/user", func (rw http.ResponseWriter, r *http.Request) {
    	r.ParseForm()

    	handle := r.PostFormValue("handle")
    	if len(handle) == 0 {
			fmt.Println("Missing handle.")
	    	sendError(rw, http.StatusBadRequest, "Missing handle.")
			return
    	}

	    // TODO: validate email
    	email := r.PostFormValue("email")
    	if len(email) == 0 {
			fmt.Println("Missing email.")
	    	sendError(rw, http.StatusBadRequest, "Missing email.")
			return
    	}

	    // TODO: check password strength
    	password := r.PostFormValue("password")
    	if len(password) == 0 {
			fmt.Println("Missing password.")
	    	sendError(rw, http.StatusBadRequest, "Missing password.")
			return
    	}

    	// look up handle to see if this user already exists
	    var u User
	    err = u.Fetch(db, handle)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

	    if u.UserId > 0 {
			fmt.Println("Handle already in use.")
	    	// TODO: we need to define a regular envelope format
	    	sendError(rw, http.StatusConflict, "That handle is already in use.")
			return
	    }

	    hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

	    u.UserId = -1
	    u.Handle = handle
	    u.Email = email
	    u.PasswordHash = string(hash)
		fmt.Println(u)

	    err = u.Save(db)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

		// TODO: auth
		sendData(rw, http.StatusCreated, u)
	}).Methods("POST")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("GET")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
    	// TODO: update user
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("PUT")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
    	// TODO: delete user
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("DELETE")

	http.ListenAndServeTLS(cfg.Server.Host + ":" + port, cfg.Server.Certificate, cfg.Server.Key, r)
}

func HomeHandler(rw http.ResponseWriter, r *http.Request) {
	rend := render.New()
	rend.HTML(rw, http.StatusOK, "login", nil)
}
<commit_msg>validate email address<commit_after>package main

import (
	"fmt"
	"net/http"
	"os"
	"github.com/gorilla/mux"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"github.com/unrolled/render"
	"golang.org/x/crypto/bcrypt"
	"net/mail"
)

var cfg Config

func sendError(rw http.ResponseWriter, status int, message string) {
	envelope := map[string]interface{}{
		"errors": [1]interface{}{
			map[string]interface{}{
				"status": fmt.Sprintf("%d",status),
				"title": message,
				"detail": message,
			},
		},
	}
	render.New().JSON(rw, status, envelope)
}

func sendData(rw http.ResponseWriter, status int, data interface{}) {
	envelope := map[string]interface{}{
		"data": data,
	}
	render.New().JSON(rw, status, envelope)
}

func main() {
	err := LoadConfigInto(&cfg, "config.gcfg")
	if err != nil {
		fmt.Println(err)
		// TODO: die
	}
	// fmt.Println("loaded config", cfg)

	// set up database connection
	db, err := sql.Open("mysql", cfg.Database.User + ":" + cfg.Database.Password + "@/" + cfg.Database.Database)
	if err != nil {
	    panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
	}
	defer db.Close()

	err = db.Ping()
	if err != nil {
	    panic(err.Error()) // proper error handling instead of panic in your app
	}

    // Heroku uses env var to specify port
    port := os.Getenv("PORT")
	if port == "" {
		port = cfg.Server.Port
	}

	// any requests on the regular HTTP port get automatically redirected to the secure home page
	// don't just redirect HTTP to HTTPS, because that doesn't train the user to not use HTTP on the first request
	// TODO: commented out so I can run Apache on 80 on my computer
	// TODO: this didn't work, maybe I have too many web servers running, also Chrome gets pretty confused with port numbers
	// go http.ListenAndServe(cfg.Server.Host + ":80", http.HandlerFunc(func (w http.ResponseWriter, req *http.Request) {
	// 	// TODO: secure app shouldn't have to be at the root of the domain
	// 	http.Redirect(w, req, "https://" + cfg.Server.Host + ":" + port, http.StatusMovedPermanently)
	// }))

    r := mux.NewRouter()
    r.HandleFunc("/", HomeHandler)

    api := r.PathPrefix("/api").Subrouter()

    api.HandleFunc("/user", func (rw http.ResponseWriter, r *http.Request) {
    	r.ParseForm()

    	handle := r.PostFormValue("handle")
    	if len(handle) == 0 {
			fmt.Println("Missing handle.")
	    	sendError(rw, http.StatusBadRequest, "Missing handle.")
			return
    	}

    	email, err := mail.ParseAddress(r.PostFormValue("email"))
    	if err != nil {
			fmt.Println(err)
	    	sendError(rw, http.StatusBadRequest, "Invalid email address.")
			return
    	}

	    // TODO: check password strength
    	password := r.PostFormValue("password")
    	if len(password) == 0 {
			fmt.Println("Missing password.")
	    	sendError(rw, http.StatusBadRequest, "Missing password.")
			return
    	}

    	// look up handle to see if this user already exists
	    var u User
	    err = u.Fetch(db, handle)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

	    if u.UserId > 0 {
			fmt.Println("Handle already in use.")
	    	sendError(rw, http.StatusConflict, "That handle is already in use.")
			return
	    }

	    hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

	    u.UserId = -1
	    u.Handle = handle
	    u.Email = email.Address
	    u.PasswordHash = string(hash)
		fmt.Println(u)

	    err = u.Save(db)
		if err != nil {
			fmt.Println(err)
			sendError(rw, http.StatusInternalServerError, err.Error())
			return
		}

		// TODO: auth
		sendData(rw, http.StatusCreated, u)
	}).Methods("POST")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("GET")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
    	// TODO: update user
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("PUT")

    api.HandleFunc("/user/{handle}", func (rw http.ResponseWriter, r *http.Request) {
    	// TODO: delete user
	    handle := mux.Vars(r)["handle"]

	    var u User
	    u.Fetch(db, handle)

		rend := render.New()
	    rend.JSON(rw, http.StatusOK, u)

	    fmt.Fprintln(rw, "showing user", handle)
	}).Methods("DELETE")

	http.ListenAndServeTLS(cfg.Server.Host + ":" + port, cfg.Server.Certificate, cfg.Server.Key, r)
}

func HomeHandler(rw http.ResponseWriter, r *http.Request) {
	rend := render.New()
	rend.HTML(rw, http.StatusOK, "login", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
	"encoding/json"
)

// http://www.faa.gov/nextgen/programs/adsb/wsa/media/GDL90_Public_ICD_RevA.PDF

const (
	stratuxVersion			= "v0.1"
	configLocation			= "stratux.conf"
	ipadAddr                = "192.168.10.255:4000" // Port 4000 for FreeFlight RANGR.
	maxDatagramSize         = 8192
	UPLINK_BLOCK_DATA_BITS  = 576
	UPLINK_BLOCK_BITS       = (UPLINK_BLOCK_DATA_BITS + 160)
	UPLINK_BLOCK_DATA_BYTES = (UPLINK_BLOCK_DATA_BITS / 8)
	UPLINK_BLOCK_BYTES      = (UPLINK_BLOCK_BITS / 8)

	UPLINK_FRAME_BLOCKS     = 6
	UPLINK_FRAME_DATA_BITS  = (UPLINK_FRAME_BLOCKS * UPLINK_BLOCK_DATA_BITS)
	UPLINK_FRAME_BITS       = (UPLINK_FRAME_BLOCKS * UPLINK_BLOCK_BITS)
	UPLINK_FRAME_DATA_BYTES = (UPLINK_FRAME_DATA_BITS / 8)
	UPLINK_FRAME_BYTES      = (UPLINK_FRAME_BITS / 8)

	// assume 6 byte frames: 2 header bytes, 4 byte payload
	// (TIS-B heartbeat with one address, or empty FIS-B APDU)
	UPLINK_MAX_INFO_FRAMES = (424 / 6)

	MSGTYPE_UPLINK       = 0x07
	MSGTYPE_BASIC_REPORT = 0x1E
	MSGTYPE_LONG_REPORT  = 0x1F

	MSGCLASS_UAT		 = 0
	MSGCLASS_ES			 = 1
)

var Crc16Table [256]uint16
var outConn *net.UDPConn

type msg struct {
	MessageClass	uint
	TimeReceived	time.Time
	Data			[]byte
}

var MsgLog []msg

// Construct the CRC table. Adapted from FAA ref above.
func crcInit() {
	var i uint16
	var bitctr uint16
	var crc uint16
	for i = 0; i < 256; i++ {
		crc = (i << 8)
		for bitctr = 0; bitctr < 8; bitctr++ {
			z := uint16(0)
			if (crc & 0x8000) != 0 {
				z = 0x1021
			}
			crc = (crc << 1) ^ z
		}
		Crc16Table[i] = crc
	}
}

// Compute CRC. Adapted from FAA ref above.
func crcCompute(data []byte) uint16 {
	ret := uint16(0)
	for i := 0; i < len(data); i++ {
		ret = Crc16Table[ret>>8] ^ (ret << 8) ^ uint16(data[i])
	}
	return ret
}

func prepareMessage(data []byte) []byte {
	tmp := []byte{0x7E} // Flag start.
	// Compute CRC before modifying the message.
	crc := crcCompute(data)

	// Copy the message over, escaping 0x7E (Flag Byte) and 0x7D (Control-Escape).
	for i := 0; i < len(data); i++ {
		mv := data[i]
		if (mv == 0x7E) || (mv == 0x7D) {
			mv = mv ^ 0x20
			tmp = append(tmp, 0x7D)
		}
		tmp = append(tmp, mv)
	}

	// Add the two CRC16 bytes.
	tmp = append(tmp, byte(crc&0xFF))
	tmp = append(tmp, byte(crc>>8))

	tmp = append(tmp, 0x7E) // Flag end.

	return tmp
}

func makeHeartbeat() []byte {
	msg := make([]byte, 7)
	// See p.10.
	msg[0] = 0x00 // Message type "Heartbeat".
	msg[1] = 0x01 // "UAT Initialized".
	nowUTC := time.Now().UTC()
	// Seconds since 0000Z.
	midnightUTC := time.Date(nowUTC.Year(), nowUTC.Month(), nowUTC.Day(), 0, 0, 0, 0, time.UTC)
	secondsSinceMidnightUTC := uint32(nowUTC.Sub(midnightUTC).Seconds())

	msg[2] = byte((secondsSinceMidnightUTC >> 16) << 7)
	msg[3] = byte((secondsSinceMidnightUTC & 0xFF))
	msg[4] = byte((secondsSinceMidnightUTC & 0xFFFF) >> 8)

	// TODO. Number of uplink messages. See p.12.
	// msg[5]
	// msg[6]

	return prepareMessage(msg)
}

func relayMessage(msgtype uint16, msg []byte) {
	ret := make([]byte, len(msg)+4)
	// See p.15.
	ret[0] = byte(msgtype) // Uplink message ID.
	ret[1] = 0x00          //TODO: Time.
	ret[2] = 0x00          //TODO: Time.
	ret[3] = 0x00          //TODO: Time.

	for i := 0; i < len(msg); i++ {
		ret[i+4] = msg[i]
	}

	outConn.Write(prepareMessage(ret))
}

func heartBeatSender() {
	for {
		outConn.Write(makeHeartbeat())
		time.Sleep(1 * time.Second)
	}
}

func updateStatus() {
	t := make([]msg, 0)
	m := len(MsgLog)
	UAT_messages_last_minute := uint(0)
	ES_messages_last_minute := uint(0)
	for i := 0; i < m; i++ {
		if time.Now().Sub(MsgLog[i].TimeReceived).Minutes() < 1 {
			t = append(t, MsgLog[i])
			if MsgLog[i].MessageClass == MSGCLASS_UAT {
				UAT_messages_last_minute++
			} else if MsgLog[i].MessageClass == MSGCLASS_ES {
				ES_messages_last_minute++
			}
		}
	}
	MsgLog = t
	globalStatus.UAT_messages_last_minute = UAT_messages_last_minute
	globalStatus.ES_messages_last_minute = ES_messages_last_minute
}

func parseInput(buf string) ([]byte, uint16) {
	x := strings.Split(buf, ";") // Discard everything after the first ';'.
	if len(x) == 0 {
		return nil, 0
	}
	s := x[0]
	if len(s) == 0 {
		return nil, 0
	}
	msgtype := uint16(0)

	s = s[1:]
	msglen := len(s) / 2

	if len(s)%2 != 0 { // Bad format.
		return nil, 0
	}

	if msglen == UPLINK_FRAME_DATA_BYTES {
		msgtype = MSGTYPE_UPLINK
	} else if msglen == 34 {
		msgtype = MSGTYPE_LONG_REPORT
	} else if msglen == 18 {
		msgtype = MSGTYPE_BASIC_REPORT
	} else {
		msgtype = 0
	}

	if msgtype == 0 {
		fmt.Printf("UNKNOWN MESSAGE TYPE: %s - msglen=%d\n", s, msglen)
	}

	// Now, begin converting the string into a byte array.
	frame := make([]byte, UPLINK_FRAME_DATA_BYTES)
	hex.Decode(frame, []byte(s))

	var thisMsg msg
	thisMsg.MessageClass = MSGCLASS_UAT
	thisMsg.TimeReceived = time.Now()
	thisMsg.Data = frame
	MsgLog = append(MsgLog, thisMsg)
	updateStatus()

	return frame, msgtype
}

type settings struct {
	UAT_Enabled					bool
	ES_Enabled					bool
}

type status struct {
	Version						string
	Devices						uint
	UAT_messages_last_minute	uint
	UAT_messages_max			uint
	ES_messages_last_minute		uint
	ES_messages_max				uint
	GPS_satellites_locked		uint
}

var globalSettings settings
var globalStatus status

func handleManagementConnection(conn net.Conn) {
	defer conn.Close()
	rw := bufio.NewReader(conn)
	for {
		s, err := rw.ReadString('\n')
		if err != nil {
			break
		}
		s = strings.Trim(s, "\r\n")
		if s == "STATUS" {
			resp, _ := json.Marshal(&globalStatus)
			conn.Write(resp)
		} else if s == "SETTINGS" {
			resp, _ := json.Marshal(&globalSettings)
			conn.Write(resp)
		} else if s == "QUIT" {
			break
		} else {
			// Assume settings.
			//TODO: Make this so that there is some positive way of doing this versus assuming that everything other than commands above are settings.
			var newSettings settings
			err := json.Unmarshal([]byte(s), &newSettings)
			if err != nil {
				fmt.Printf("%s - error: %s\n", s, err.Error())
			} else {
				fmt.Printf("new settings: %s\n", s)
				globalSettings = newSettings
				saveSettings()
			}
		}
	}
}

func managementInterface() {
	ln, err := net.Listen("tcp", "127.0.0.1:9110")
	if err != nil { //TODO
		fmt.Printf("couldn't open management port: %s\n", err.Error())
		return
	}
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil { //TODO
			continue
		}
		go handleManagementConnection(conn)
	}
}

func defaultSettings() {
	globalSettings.UAT_Enabled = true //TODO
	globalSettings.ES_Enabled = false //TODO
}

func readSettings() {
	fd, err := os.Open(configLocation)
	defer fd.Close()
	if err != nil {
		fmt.Printf("can't read settings %s: %s\n", configLocation, err.Error())
		defaultSettings()
		return
	}
	buf := make([]byte, 1024)
	count, err := fd.Read(buf)
	if err != nil {
		fmt.Printf("can't read settings %s: %s\n", configLocation, err.Error())
		defaultSettings()
		return
	}
	var newSettings settings
	err = json.Unmarshal(buf[0:count], &newSettings)
	if err != nil {
		fmt.Printf("can't read settings %s: %s\n", configLocation, err.Error())
		defaultSettings()
		return
	}
	globalSettings = newSettings
	fmt.Printf("read in settings.\n")
}

func saveSettings() {
	fd, err := os.OpenFile(configLocation, os.O_CREATE | os.O_WRONLY, os.FileMode(0644))
	defer fd.Close()
	if err != nil {
		fmt.Printf("can't save settings %s: %s\n", configLocation, err.Error())
		return
	}
	jsonSettings, _ := json.Marshal(&globalSettings)
	fd.Write(jsonSettings)
	fmt.Printf("wrote settings.\n")
}

func main() {
	MsgLog = make([]msg, 0)
	globalStatus.Version = stratuxVersion
	globalStatus.Devices = 123 //TODO
	globalStatus.UAT_messages_last_minute = 567 //TODO
	globalStatus.ES_messages_last_minute = 981 //TODO

	readSettings()

	crcInit() // Initialize CRC16 table.

	// Open UDP port to send out the messages.
	addr, err := net.ResolveUDPAddr("udp", ipadAddr)
	if err != nil {
		panic(err)
	}
	outConn, err = net.DialUDP("udp", nil, addr)
	if err != nil {
		panic("error creating UDP socket: " + err.Error())
	}

	// Start the heartbeat message loop in the background, once per second.
	go heartBeatSender()
	// Start the management interface.
	go managementInterface()

	reader := bufio.NewReader(os.Stdin)

	for {
		buf, _ := reader.ReadString('\n')
		o, msgtype := parseInput(buf)
		if o != nil && msgtype != 0 {
			relayMessage(msgtype, o)
		}
	}

}
<commit_msg>Message formatting error causing CRC mismatch in some messages.<commit_after>package main

import (
	"bufio"
	"encoding/hex"
	"fmt"
	"net"
	"os"
	"strings"
	"time"
	"encoding/json"
)

// http://www.faa.gov/nextgen/programs/adsb/wsa/media/GDL90_Public_ICD_RevA.PDF

const (
	stratuxVersion			= "v0.1"
	configLocation			= "stratux.conf"
	ipadAddr                = "192.168.10.255:4000" // Port 4000 for FreeFlight RANGR.
	maxDatagramSize         = 8192
	UPLINK_BLOCK_DATA_BITS  = 576
	UPLINK_BLOCK_BITS       = (UPLINK_BLOCK_DATA_BITS + 160)
	UPLINK_BLOCK_DATA_BYTES = (UPLINK_BLOCK_DATA_BITS / 8)
	UPLINK_BLOCK_BYTES      = (UPLINK_BLOCK_BITS / 8)

	UPLINK_FRAME_BLOCKS     = 6
	UPLINK_FRAME_DATA_BITS  = (UPLINK_FRAME_BLOCKS * UPLINK_BLOCK_DATA_BITS)
	UPLINK_FRAME_BITS       = (UPLINK_FRAME_BLOCKS * UPLINK_BLOCK_BITS)
	UPLINK_FRAME_DATA_BYTES = (UPLINK_FRAME_DATA_BITS / 8)
	UPLINK_FRAME_BYTES      = (UPLINK_FRAME_BITS / 8)

	// assume 6 byte frames: 2 header bytes, 4 byte payload
	// (TIS-B heartbeat with one address, or empty FIS-B APDU)
	UPLINK_MAX_INFO_FRAMES = (424 / 6)

	MSGTYPE_UPLINK       = 0x07
	MSGTYPE_BASIC_REPORT = 0x1E
	MSGTYPE_LONG_REPORT  = 0x1F

	MSGCLASS_UAT		 = 0
	MSGCLASS_ES			 = 1
)

var Crc16Table [256]uint16
var outConn *net.UDPConn

type msg struct {
	MessageClass	uint
	TimeReceived	time.Time
	Data			[]byte
}

var MsgLog []msg

// Construct the CRC table. Adapted from FAA ref above.
func crcInit() {
	var i uint16
	var bitctr uint16
	var crc uint16
	for i = 0; i < 256; i++ {
		crc = (i << 8)
		for bitctr = 0; bitctr < 8; bitctr++ {
			z := uint16(0)
			if (crc & 0x8000) != 0 {
				z = 0x1021
			}
			crc = (crc << 1) ^ z
		}
		Crc16Table[i] = crc
	}
}

// Compute CRC. Adapted from FAA ref above.
func crcCompute(data []byte) uint16 {
	ret := uint16(0)
	for i := 0; i < len(data); i++ {
		ret = Crc16Table[ret>>8] ^ (ret << 8) ^ uint16(data[i])
	}
	return ret
}

func prepareMessage(data []byte) []byte {
	// Compute CRC before modifying the message.
	crc := crcCompute(data)
	// Add the two CRC16 bytes before replacing control characters.
	data = append(data, byte(crc&0xFF))
	data = append(data, byte(crc>>8))

	tmp := []byte{0x7E} // Flag start.

	// Copy the message over, escaping 0x7E (Flag Byte) and 0x7D (Control-Escape).
	for i := 0; i < len(data); i++ {
		mv := data[i]
		if (mv == 0x7E) || (mv == 0x7D) {
			mv = mv ^ 0x20
			tmp = append(tmp, 0x7D)
		}
		tmp = append(tmp, mv)
	}

	tmp = append(tmp, 0x7E) // Flag end.

	return tmp
}

func makeHeartbeat() []byte {
	msg := make([]byte, 7)
	// See p.10.
	msg[0] = 0x00 // Message type "Heartbeat".
	msg[1] = 0x01 // "UAT Initialized".
	nowUTC := time.Now().UTC()
	// Seconds since 0000Z.
	midnightUTC := time.Date(nowUTC.Year(), nowUTC.Month(), nowUTC.Day(), 0, 0, 0, 0, time.UTC)
	secondsSinceMidnightUTC := uint32(nowUTC.Sub(midnightUTC).Seconds())

	msg[2] = byte((secondsSinceMidnightUTC >> 16) << 7)
	msg[3] = byte((secondsSinceMidnightUTC & 0xFF))
	msg[4] = byte((secondsSinceMidnightUTC & 0xFFFF) >> 8)

	// TODO. Number of uplink messages. See p.12.
	// msg[5]
	// msg[6]

	return prepareMessage(msg)
}

func relayMessage(msgtype uint16, msg []byte) {
	ret := make([]byte, len(msg)+4)
	// See p.15.
	ret[0] = byte(msgtype) // Uplink message ID.
	ret[1] = 0x00          //TODO: Time.
	ret[2] = 0x00          //TODO: Time.
	ret[3] = 0x00          //TODO: Time.

	for i := 0; i < len(msg); i++ {
		ret[i+4] = msg[i]
	}

	outConn.Write(prepareMessage(ret))
}

func heartBeatSender() {
	for {
		outConn.Write(makeHeartbeat())
		time.Sleep(1 * time.Second)
	}
}

func updateStatus() {
	t := make([]msg, 0)
	m := len(MsgLog)
	UAT_messages_last_minute := uint(0)
	ES_messages_last_minute := uint(0)
	for i := 0; i < m; i++ {
		if time.Now().Sub(MsgLog[i].TimeReceived).Minutes() < 1 {
			t = append(t, MsgLog[i])
			if MsgLog[i].MessageClass == MSGCLASS_UAT {
				UAT_messages_last_minute++
			} else if MsgLog[i].MessageClass == MSGCLASS_ES {
				ES_messages_last_minute++
			}
		}
	}
	MsgLog = t
	globalStatus.UAT_messages_last_minute = UAT_messages_last_minute
	globalStatus.ES_messages_last_minute = ES_messages_last_minute
}

func parseInput(buf string) ([]byte, uint16) {
	x := strings.Split(buf, ";") // Discard everything after the first ';'.
	if len(x) == 0 {
		return nil, 0
	}
	s := x[0]
	if len(s) == 0 {
		return nil, 0
	}
	msgtype := uint16(0)

	s = s[1:]
	msglen := len(s) / 2

	if len(s)%2 != 0 { // Bad format.
		return nil, 0
	}

	if msglen == UPLINK_FRAME_DATA_BYTES {
		msgtype = MSGTYPE_UPLINK
	} else if msglen == 34 {
		msgtype = MSGTYPE_LONG_REPORT
	} else if msglen == 18 {
		msgtype = MSGTYPE_BASIC_REPORT
	} else {
		msgtype = 0
	}

	if msgtype == 0 {
		fmt.Printf("UNKNOWN MESSAGE TYPE: %s - msglen=%d\n", s, msglen)
	}

	// Now, begin converting the string into a byte array.
	frame := make([]byte, UPLINK_FRAME_DATA_BYTES)
	hex.Decode(frame, []byte(s))

	var thisMsg msg
	thisMsg.MessageClass = MSGCLASS_UAT
	thisMsg.TimeReceived = time.Now()
	thisMsg.Data = frame
	MsgLog = append(MsgLog, thisMsg)
	updateStatus()

	return frame, msgtype
}

type settings struct {
	UAT_Enabled					bool
	ES_Enabled					bool
}

type status struct {
	Version						string
	Devices						uint
	UAT_messages_last_minute	uint
	UAT_messages_max			uint
	ES_messages_last_minute		uint
	ES_messages_max				uint
	GPS_satellites_locked		uint
}

var globalSettings settings
var globalStatus status

func handleManagementConnection(conn net.Conn) {
	defer conn.Close()
	rw := bufio.NewReader(conn)
	for {
		s, err := rw.ReadString('\n')
		if err != nil {
			break
		}
		s = strings.Trim(s, "\r\n")
		if s == "STATUS" {
			resp, _ := json.Marshal(&globalStatus)
			conn.Write(resp)
		} else if s == "SETTINGS" {
			resp, _ := json.Marshal(&globalSettings)
			conn.Write(resp)
		} else if s == "QUIT" {
			break
		} else {
			// Assume settings.
			//TODO: Make this so that there is some positive way of doing this versus assuming that everything other than commands above are settings.
			var newSettings settings
			err := json.Unmarshal([]byte(s), &newSettings)
			if err != nil {
				fmt.Printf("%s - error: %s\n", s, err.Error())
			} else {
				fmt.Printf("new settings: %s\n", s)
				globalSettings = newSettings
				saveSettings()
			}
		}
	}
}

func managementInterface() {
	ln, err := net.Listen("tcp", "127.0.0.1:9110")
	if err != nil { //TODO
		fmt.Printf("couldn't open management port: %s\n", err.Error())
		return
	}
	defer ln.Close()
	for {
		conn, err := ln.Accept()
		if err != nil { //TODO
			continue
		}
		go handleManagementConnection(conn)
	}
}

func defaultSettings() {
	globalSettings.UAT_Enabled = true //TODO
	globalSettings.ES_Enabled = false //TODO
}

func readSettings() {
	fd, err := os.Open(configLocation)
	defer fd.Close()
	if err != nil {
		fmt.Printf("can't read settings %s: %s\n", configLocation, err.Error())
		defaultSettings()
		return
	}
	buf := make([]byte, 1024)
	count, err := fd.Read(buf)
	if err != nil {
		fmt.Printf("can't read settings %s: %s\n", configLocation, err.Error())
		defaultSettings()
		return
	}
	var newSettings settings
	err = json.Unmarshal(buf[0:count], &newSettings)
	if err != nil {
		fmt.Printf("can't read settings %s: %s\n", configLocation, err.Error())
		defaultSettings()
		return
	}
	globalSettings = newSettings
	fmt.Printf("read in settings.\n")
}

func saveSettings() {
	fd, err := os.OpenFile(configLocation, os.O_CREATE | os.O_WRONLY, os.FileMode(0644))
	defer fd.Close()
	if err != nil {
		fmt.Printf("can't save settings %s: %s\n", configLocation, err.Error())
		return
	}
	jsonSettings, _ := json.Marshal(&globalSettings)
	fd.Write(jsonSettings)
	fmt.Printf("wrote settings.\n")
}

func main() {
	MsgLog = make([]msg, 0)
	globalStatus.Version = stratuxVersion
	globalStatus.Devices = 123 //TODO
	globalStatus.UAT_messages_last_minute = 567 //TODO
	globalStatus.ES_messages_last_minute = 981 //TODO

	readSettings()

	crcInit() // Initialize CRC16 table.

	// Open UDP port to send out the messages.
	addr, err := net.ResolveUDPAddr("udp", ipadAddr)
	if err != nil {
		panic(err)
	}
	outConn, err = net.DialUDP("udp", nil, addr)
	if err != nil {
		panic("error creating UDP socket: " + err.Error())
	}

	// Start the heartbeat message loop in the background, once per second.
	go heartBeatSender()
	// Start the management interface.
	go managementInterface()

	reader := bufio.NewReader(os.Stdin)

	for {
		buf, _ := reader.ReadString('\n')
		o, msgtype := parseInput(buf)
		if o != nil && msgtype != 0 {
			relayMessage(msgtype, o)
		}
	}

}
<|endoftext|>