<commit_before>package peer

import (
	"image"
	"image/color"
	"image/draw"
	"log"
	"sync"
	"time"

	"github.com/golang/freetype/truetype"
	"github.com/pankona/gomo-simra/simra/config"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/goregular"
	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/asset"
	"golang.org/x/mobile/exp/app/debug"
	"golang.org/x/mobile/exp/f32"
	"golang.org/x/mobile/exp/gl/glutil"
	"golang.org/x/mobile/exp/sprite"
	"golang.org/x/mobile/exp/sprite/clock"
	"golang.org/x/mobile/exp/sprite/glsprite"
	"golang.org/x/mobile/gl"
)

// GLer interface represents interface of GL
type GLer interface {
	// Initialize initializes GLPeer.
	// This function must be called inadvance of using GLPeer
	Initialize(glctx *GLContext)
	// LoadTexture return texture that is loaded by the information of arguments.
	// Loaded texture can assign using AddSprite function.
	LoadTexture(assetName string, rect image.Rectangle) sprite.SubTex
	// MakeTextureByText createst and return texture by speicied text
	// Loaded texture can assign using AddSprite function.
	// TODO: font parameterize
	MakeTextureByText(text string, fontsize float64, fontcolor color.RGBA, rect image.Rectangle) sprite.SubTex
	// Finalize finalizes GLPeer.
	// This is called at termination of application.
	Finalize()
	// Update updates screen.
	// This is called 60 times per 1 sec.
	Update(sc SpriteContainerer, i interface{})
	// Reset resets current gl context.
	// All sprites are also cleaned.
	// This is called at changing of scene, and
	// this function is for clean previous scene.
	Reset()
	// NewTexture returns a new Texture instance
	NewTexture(s sprite.SubTex) *Texture
	// ReleaseTexture releases specified texture
	ReleaseTexture(t *Texture)
	// NewNode returns new node
	NewNode(fn arrangerFunc) *sprite.Node
	// AppendChild adds specified node as a child
	AppendChild(n *sprite.Node)
	// RemoveChild removes specified node
	RemoveChild(n *sprite.Node)
	// SetSubTex registers subtexture to specified node
	SetSubTex(n *sprite.Node, subTex *sprite.SubTex)
}

// GLPeer represents gl context.
// Singleton.
type GLPeer struct {
	glctx     gl.Context
	startTime time.Time
	images    *glutil.Images
	fps       *debug.FPS
	eng       sprite.Engine
	scene     *sprite.Node
	mu        sync.Mutex
}

// NewGLPeer returns a instance of GLPeer
func NewGLPeer() GLer {
	return &GLPeer{}
}

type GLContext struct {
	glcontext gl.Context
}

// Initialize initializes GLPeer.
// This function must be called inadvance of using GLPeer
// FIXME:
func (glpeer *GLPeer) Initialize(glc *GLContext) {
	glctx := glc.glcontext
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.glctx = glctx
	glpeer.startTime = time.Now()

	// transparency of png
	glpeer.glctx.Enable(gl.BLEND)
	glpeer.glctx.BlendEquation(gl.FUNC_ADD)
	glpeer.glctx.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	glpeer.images = glutil.NewImages(glctx)
	glpeer.fps = debug.NewFPS(glpeer.images)
	glpeer.initEng()

	LogDebug("OUT")
}

func (glpeer *GLPeer) initEng() {
	if glpeer.eng != nil {
		glpeer.eng.Release()
	}
	glpeer.eng = glsprite.Engine(glpeer.images)
	glpeer.scene = &sprite.Node{}
	glpeer.eng.Register(glpeer.scene)
	glpeer.eng.SetTransform(glpeer.scene, f32.Affine{
		{1, 0, 0},
		{0, 1, 0},
	})
}

type arrangerFunc func(e sprite.Engine, n *sprite.Node, t clock.Time)

func (a arrangerFunc) Arrange(e sprite.Engine, n *sprite.Node, t clock.Time) { a(e, n, t) }

// NewNode returns new node
func (glpeer *GLPeer) NewNode(fn arrangerFunc) *sprite.Node {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	n := &sprite.Node{Arranger: fn}
	glpeer.eng.Register(n)
	glpeer.scene.AppendChild(n)
	return n
}

// AppendChild adds specified node as a child
func (glpeer *GLPeer) AppendChild(n *sprite.Node) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.scene.AppendChild(n)
}

// RemoveChild removes specified node
func (glpeer *GLPeer) RemoveChild(n *sprite.Node) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.scene.RemoveChild(n)
}

// LoadTexture return texture that is loaded by the information of arguments.
// Loaded texture can assign using AddSprite function.
func (glpeer *GLPeer) LoadTexture(assetName string, rect image.Rectangle) sprite.SubTex {
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	a, err := asset.Open(assetName)
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		closeErr := a.Close()
		if closeErr != nil {
			log.Println(closeErr)
		}
	}()

	img, _, err := image.Decode(a)
	if err != nil {
		log.Fatal(err)
	}
	t, err := glpeer.eng.LoadTexture(img)
	if err != nil {
		log.Fatal(err)
	}

	LogDebug("OUT")
	return sprite.SubTex{T: t, R: rect}
}

// MakeTextureByText createst and return texture by speicied text
// Loaded texture can assign using AddSprite function.
// TODO: font parameterize
func (glpeer *GLPeer) MakeTextureByText(text string, fontsize float64, fontcolor color.RGBA, rect image.Rectangle) sprite.SubTex {
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	dpi := float64(72)
	width := rect.Dx()
	height := rect.Dy()
	img := image.NewRGBA(image.Rect(0, 0, width, height))

	fg, bg := image.NewUniform(fontcolor), image.Transparent
	draw.Draw(img, img.Bounds(), bg, image.Point{}, draw.Src)

	// Draw the text.
	h := font.HintingNone

	gofont, _ := truetype.Parse(goregular.TTF)

	d := &font.Drawer{
		Dst: img,
		Src: fg,
		Face: truetype.NewFace(gofont, &truetype.Options{
			Size:    fontsize,
			DPI:     dpi,
			Hinting: h,
		}),
	}

	textWidth := d.MeasureString(text)

	d.Dot = fixed.Point26_6{
		X: fixed.I(width/2) - textWidth/2,
		Y: fixed.I(int(fontsize * dpi / 72)),
	}
	d.DrawString(text)

	t, err := glpeer.eng.LoadTexture(img)
	if err != nil {
		log.Fatal(err)
	}

	LogDebug("OUT")
	return sprite.SubTex{T: t, R: rect}
}

// Finalize finalizes GLPeer.
// This is called at termination of application.
func (glpeer *GLPeer) Finalize() {
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	glpeer.eng.Release()
	glpeer.fps.Release()
	glpeer.images.Release()
	glpeer.glctx = nil
	LogDebug("OUT")
}

// Update updates screen.
// This is called 60 times per 1 sec.
// FIXME:
func (glpeer *GLPeer) Update(sc SpriteContainerer, i interface{}) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	if glpeer.glctx == nil {
		return
	}
	glpeer.glctx.ClearColor(0, 0, 0, 1) // black background
	glpeer.glctx.Clear(gl.COLOR_BUFFER_BIT)
	now := clock.Time(time.Since(glpeer.startTime) * 60 / time.Second)

	glpeer.apply(sc)

	glpeer.eng.Render(glpeer.scene, now, screensize.sz)
	if config.DEBUG {
		glpeer.fps.Draw(screensize.sz)
	}

	// app.Publish() calls glctx.Flush, it should be called within this mutex locking.
	i.(func() app.PublishResult)()
}

// Reset resets current gl context.
// All sprites are also cleaned.
// This is called at changing of scene, and
// this function is for clean previous scene.
func (glpeer *GLPeer) Reset() {
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.initEng()
	LogDebug("OUT")
}

// SetSubTex registers subtexture to specified node
func (glpeer *GLPeer) SetSubTex(n *sprite.Node, subTex *sprite.SubTex) {
	glpeer.eng.SetSubTex(n, *subTex)
}

func (glpeer *GLPeer) apply(sc SpriteContainerer) {
	snpairs := sc.GetSpriteNodePairs()
	snpairs.Range(func(k, v interface{}) bool {
		sn := v.(*spriteNodePair)
		if sn.sprite == nil || !sn.inuse {
			return true
		}
		s := sn.sprite

		affine := &f32.Affine{
			{1, 0, 0},
			{0, 1, 0},
		}
		affine.Translate(affine,
			(float32)(s.X)*screensize.scale-(float32)(s.W)/2*screensize.scale+screensize.marginWidth/2,
			(screensize.height-(float32)(s.Y))*screensize.scale-(float32)(s.H)/2*screensize.scale+screensize.marginHeight/2)
		if s.R != 0 {
			affine.Translate(affine,
				0.5*(float32)(s.W)*screensize.scale,
				0.5*(float32)(s.H)*screensize.scale)
			affine.Rotate(affine, s.R)
			affine.Translate(affine,
				-0.5*(float32)(s.W)*screensize.scale,
				-0.5*(float32)(s.H)*screensize.scale)
		}
		affine.Scale(affine,
			(float32)(s.W)*screensize.scale,
			(float32)(s.H)*screensize.scale)
		glpeer.eng.SetTransform(sn.node, *affine)
		return true
	})
}

// Texture represents a texture object that contains subTex
type Texture struct {
	glPeer *GLPeer
	subTex sprite.SubTex
}

// NewTexture returns a new Texture instance
func (glpeer *GLPeer) NewTexture(s sprite.SubTex) *Texture {
	return &Texture{
		glPeer: glpeer,
		subTex: s,
	}
}

// ReleaseTexture releases specified texture
func (glpeer *GLPeer) ReleaseTexture(t *Texture) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	t.subTex.T.Release()
}
<commit_msg>add missing comment for exported function<commit_after>package peer

import (
	"image"
	"image/color"
	"image/draw"
	"log"
	"sync"
	"time"

	"github.com/golang/freetype/truetype"
	"github.com/pankona/gomo-simra/simra/config"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/goregular"
	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/asset"
	"golang.org/x/mobile/exp/app/debug"
	"golang.org/x/mobile/exp/f32"
	"golang.org/x/mobile/exp/gl/glutil"
	"golang.org/x/mobile/exp/sprite"
	"golang.org/x/mobile/exp/sprite/clock"
	"golang.org/x/mobile/exp/sprite/glsprite"
	"golang.org/x/mobile/gl"
)

// GLer interface represents interface of GL
type GLer interface {
	// Initialize initializes GLPeer.
	// This function must be called inadvance of using GLPeer
	Initialize(glctx *GLContext)
	// LoadTexture return texture that is loaded by the information of arguments.
	// Loaded texture can assign using AddSprite function.
	LoadTexture(assetName string, rect image.Rectangle) sprite.SubTex
	// MakeTextureByText createst and return texture by speicied text
	// Loaded texture can assign using AddSprite function.
	// TODO: font parameterize
	MakeTextureByText(text string, fontsize float64, fontcolor color.RGBA, rect image.Rectangle) sprite.SubTex
	// Finalize finalizes GLPeer.
	// This is called at termination of application.
	Finalize()
	// Update updates screen.
	// This is called 60 times per 1 sec.
	Update(sc SpriteContainerer, i interface{})
	// Reset resets current gl context.
	// All sprites are also cleaned.
	// This is called at changing of scene, and
	// this function is for clean previous scene.
	Reset()
	// NewTexture returns a new Texture instance
	NewTexture(s sprite.SubTex) *Texture
	// ReleaseTexture releases specified texture
	ReleaseTexture(t *Texture)
	// NewNode returns new node
	NewNode(fn arrangerFunc) *sprite.Node
	// AppendChild adds specified node as a child
	AppendChild(n *sprite.Node)
	// RemoveChild removes specified node
	RemoveChild(n *sprite.Node)
	// SetSubTex registers subtexture to specified node
	SetSubTex(n *sprite.Node, subTex *sprite.SubTex)
}

// GLPeer represents gl context.
// Singleton.
type GLPeer struct {
	glctx     gl.Context
	startTime time.Time
	images    *glutil.Images
	fps       *debug.FPS
	eng       sprite.Engine
	scene     *sprite.Node
	mu        sync.Mutex
}

// NewGLPeer returns a instance of GLPeer
func NewGLPeer() GLer {
	return &GLPeer{}
}

// GLContext is a wrapper of gl.Context
type GLContext struct {
	glcontext gl.Context
}

// Initialize initializes GLPeer.
// This function must be called inadvance of using GLPeer
// FIXME:
func (glpeer *GLPeer) Initialize(glc *GLContext) {
	glctx := glc.glcontext
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.glctx = glctx
	glpeer.startTime = time.Now()

	// transparency of png
	glpeer.glctx.Enable(gl.BLEND)
	glpeer.glctx.BlendEquation(gl.FUNC_ADD)
	glpeer.glctx.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	glpeer.images = glutil.NewImages(glctx)
	glpeer.fps = debug.NewFPS(glpeer.images)
	glpeer.initEng()

	LogDebug("OUT")
}

func (glpeer *GLPeer) initEng() {
	if glpeer.eng != nil {
		glpeer.eng.Release()
	}
	glpeer.eng = glsprite.Engine(glpeer.images)
	glpeer.scene = &sprite.Node{}
	glpeer.eng.Register(glpeer.scene)
	glpeer.eng.SetTransform(glpeer.scene, f32.Affine{
		{1, 0, 0},
		{0, 1, 0},
	})
}

type arrangerFunc func(e sprite.Engine, n *sprite.Node, t clock.Time)

func (a arrangerFunc) Arrange(e sprite.Engine, n *sprite.Node, t clock.Time) { a(e, n, t) }

// NewNode returns new node
func (glpeer *GLPeer) NewNode(fn arrangerFunc) *sprite.Node {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	n := &sprite.Node{Arranger: fn}
	glpeer.eng.Register(n)
	glpeer.scene.AppendChild(n)
	return n
}

// AppendChild adds specified node as a child
func (glpeer *GLPeer) AppendChild(n *sprite.Node) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.scene.AppendChild(n)
}

// RemoveChild removes specified node
func (glpeer *GLPeer) RemoveChild(n *sprite.Node) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.scene.RemoveChild(n)
}

// LoadTexture return texture that is loaded by the information of arguments.
// Loaded texture can assign using AddSprite function.
func (glpeer *GLPeer) LoadTexture(assetName string, rect image.Rectangle) sprite.SubTex {
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	a, err := asset.Open(assetName)
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		closeErr := a.Close()
		if closeErr != nil {
			log.Println(closeErr)
		}
	}()

	img, _, err := image.Decode(a)
	if err != nil {
		log.Fatal(err)
	}
	t, err := glpeer.eng.LoadTexture(img)
	if err != nil {
		log.Fatal(err)
	}

	LogDebug("OUT")
	return sprite.SubTex{T: t, R: rect}
}

// MakeTextureByText createst and return texture by speicied text
// Loaded texture can assign using AddSprite function.
// TODO: font parameterize
func (glpeer *GLPeer) MakeTextureByText(text string, fontsize float64, fontcolor color.RGBA, rect image.Rectangle) sprite.SubTex {
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	dpi := float64(72)
	width := rect.Dx()
	height := rect.Dy()
	img := image.NewRGBA(image.Rect(0, 0, width, height))

	fg, bg := image.NewUniform(fontcolor), image.Transparent
	draw.Draw(img, img.Bounds(), bg, image.Point{}, draw.Src)

	// Draw the text.
	h := font.HintingNone

	gofont, _ := truetype.Parse(goregular.TTF)

	d := &font.Drawer{
		Dst: img,
		Src: fg,
		Face: truetype.NewFace(gofont, &truetype.Options{
			Size:    fontsize,
			DPI:     dpi,
			Hinting: h,
		}),
	}

	textWidth := d.MeasureString(text)

	d.Dot = fixed.Point26_6{
		X: fixed.I(width/2) - textWidth/2,
		Y: fixed.I(int(fontsize * dpi / 72)),
	}
	d.DrawString(text)

	t, err := glpeer.eng.LoadTexture(img)
	if err != nil {
		log.Fatal(err)
	}

	LogDebug("OUT")
	return sprite.SubTex{T: t, R: rect}
}

// Finalize finalizes GLPeer.
// This is called at termination of application.
func (glpeer *GLPeer) Finalize() {
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	glpeer.eng.Release()
	glpeer.fps.Release()
	glpeer.images.Release()
	glpeer.glctx = nil
	LogDebug("OUT")
}

// Update updates screen.
// This is called 60 times per 1 sec.
// FIXME:
func (glpeer *GLPeer) Update(sc SpriteContainerer, i interface{}) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	if glpeer.glctx == nil {
		return
	}
	glpeer.glctx.ClearColor(0, 0, 0, 1) // black background
	glpeer.glctx.Clear(gl.COLOR_BUFFER_BIT)
	now := clock.Time(time.Since(glpeer.startTime) * 60 / time.Second)

	glpeer.apply(sc)

	glpeer.eng.Render(glpeer.scene, now, screensize.sz)
	if config.DEBUG {
		glpeer.fps.Draw(screensize.sz)
	}

	// app.Publish() calls glctx.Flush, it should be called within this mutex locking.
	i.(func() app.PublishResult)()
}

// Reset resets current gl context.
// All sprites are also cleaned.
// This is called at changing of scene, and
// this function is for clean previous scene.
func (glpeer *GLPeer) Reset() {
	LogDebug("IN")
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.initEng()
	LogDebug("OUT")
}

// SetSubTex registers subtexture to specified node
func (glpeer *GLPeer) SetSubTex(n *sprite.Node, subTex *sprite.SubTex) {
	glpeer.eng.SetSubTex(n, *subTex)
}

func (glpeer *GLPeer) apply(sc SpriteContainerer) {
	snpairs := sc.GetSpriteNodePairs()
	snpairs.Range(func(k, v interface{}) bool {
		sn := v.(*spriteNodePair)
		if sn.sprite == nil || !sn.inuse {
			return true
		}
		s := sn.sprite

		affine := &f32.Affine{
			{1, 0, 0},
			{0, 1, 0},
		}
		affine.Translate(affine,
			(float32)(s.X)*screensize.scale-(float32)(s.W)/2*screensize.scale+screensize.marginWidth/2,
			(screensize.height-(float32)(s.Y))*screensize.scale-(float32)(s.H)/2*screensize.scale+screensize.marginHeight/2)
		if s.R != 0 {
			affine.Translate(affine,
				0.5*(float32)(s.W)*screensize.scale,
				0.5*(float32)(s.H)*screensize.scale)
			affine.Rotate(affine, s.R)
			affine.Translate(affine,
				-0.5*(float32)(s.W)*screensize.scale,
				-0.5*(float32)(s.H)*screensize.scale)
		}
		affine.Scale(affine,
			(float32)(s.W)*screensize.scale,
			(float32)(s.H)*screensize.scale)
		glpeer.eng.SetTransform(sn.node, *affine)
		return true
	})
}

// Texture represents a texture object that contains subTex
type Texture struct {
	glPeer *GLPeer
	subTex sprite.SubTex
}

// NewTexture returns a new Texture instance
func (glpeer *GLPeer) NewTexture(s sprite.SubTex) *Texture {
	return &Texture{
		glPeer: glpeer,
		subTex: s,
	}
}

// ReleaseTexture releases specified texture
func (glpeer *GLPeer) ReleaseTexture(t *Texture) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	t.subTex.T.Release()
}
<|endoftext|>


<commit_before>/*-
 * Copyright (c) 2016, 1&1 Internet SE
 * All rights reserved
 */

package stmt

const LoadPermissions = `
SELECT permission_id,
       permission_name
FROM   soma.permissions;`

const AddPermissionCategory = `
INSERT INTO soma.permission_types (
            permission_type,
            created_by
)
SELECT $1::varchar,
       $2::uuid
WHERE NOT EXISTS (
      SELECT permission_type
      FROM   soma.permission_types
      WHERE  permission_type = $1::varchar
);`

const DeletePermissionCategory = `
DELETE FROM soma.permission_types
WHERE permission_type = $1::varchar;`

const ListPermissionCategory = `
SELECT spt.permission_type
FROM   soma.permission_types spt:`

const ShowPermissionCategory = `
SELECT spt.permission_type,
       iu.user_uid,
       spt.created_by
FROM   soma.permission_types spt
JOIN   inventory.users iu
ON     spt.created_by = iu.user_id
WHERE  spt.permission_type = $1::varchar;`

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Supervisor/ShowPermissionCategory: select correct column<commit_after>/*-
 * Copyright (c) 2016, 1&1 Internet SE
 * All rights reserved
 */

package stmt

const LoadPermissions = `
SELECT permission_id,
       permission_name
FROM   soma.permissions;`

const AddPermissionCategory = `
INSERT INTO soma.permission_types (
            permission_type,
            created_by
)
SELECT $1::varchar,
       $2::uuid
WHERE NOT EXISTS (
      SELECT permission_type
      FROM   soma.permission_types
      WHERE  permission_type = $1::varchar
);`

const DeletePermissionCategory = `
DELETE FROM soma.permission_types
WHERE permission_type = $1::varchar;`

const ListPermissionCategory = `
SELECT spt.permission_type
FROM   soma.permission_types spt:`

const ShowPermissionCategory = `
SELECT spt.permission_type,
       iu.user_uid,
       spt.created_at
FROM   soma.permission_types spt
JOIN   inventory.users iu
ON     spt.created_by = iu.user_id
WHERE  spt.permission_type = $1::varchar;`

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"bytes"
	"math"
	"time"
	"io/ioutil"
	"net/http"

	"os"
	"image"
	"image/png"
	"image/draw"
    "image/color"
)

// Create a struct to deal with pixel
type Pixel struct {
    Point image.Point
    Color color.Color
}

// Decode image.Image's pixel data into []*Pixel
func DecodePixelsFromImage(img image.Image, offsetX, offsetY int) []*Pixel {
    pixels := []*Pixel{}
    for y := 0; y <= img.Bounds().Max.Y; y++ {
        for x := 0; x <= img.Bounds().Max.X; x++ {
            p := &Pixel{
                Point: image.Point{x + offsetX, y + offsetY},
                Color: img.At(x, y),
            }
            pixels = append(pixels, p)
        }
    }
    return pixels
}

var client = &http.Client{
	Timeout: time.Second * 5,
}

var tiles_map map[int][]image.Image

func init() {
	tiles_map = make(map[int][]image.Image)
}

// degTorad converts degree to radians.
func degTorad(deg float64) float64 {
	return deg * math.Pi / 180;
}

// deg2num converts latlng to tile number
func deg2num(lat_deg float64, lon_deg float64, zoom int) (int, int) {
    lat_rad := degTorad(lat_deg)
    n := math.Pow(2.0, float64(zoom))
    xtile := int((lon_deg + 180.0) / 360.0 * n)
    ytile := int((1.0 - math.Log(math.Tan(lat_rad) + (1 / math.Cos(lat_rad))) / math.Pi) / 2.0 * n)
    return xtile, ytile
}

// xyz
type xyz struct {
	x int
	y int
	z int
}

// getTileNames
func getTileNames(minlat, maxlat, minlng, maxlng float64, z int) []xyz {
	tiles := []xyz{}
	// // upper right
	// ur_tile_x, ur_tile_y := deg2num(float64(70), float64(16), z)
	// // lower left
	// ll_tile_x, ll_tile_y := deg2num(float64(35), float64(0), z)
	// upper right
	ur_tile_x, ur_tile_y := deg2num(maxlat, maxlng, z)
	// lower left
	ll_tile_x, ll_tile_y := deg2num(minlat, minlng, z)

	for x := ll_tile_x-1; x < ur_tile_x+1; x++ {
		if x < 0 { x++ }
		for y := ur_tile_y-1; y < ll_tile_y+1; y++ {
			if y < 0 { y++ }
			tiles = append(tiles, xyz{x,y,z})
		}
	}
	return tiles
}

// getTilePngFromUrl
func getTilePngBytesFromUrl(tile_url string) []byte {
	fmt.Println("GET", tile_url)

	// Just a simple GET request to the image URL
    // We get back a *Response, and an error
	res, err := client.Get(tile_url)
	if err != nil {
        fmt.Printf("Error http.Get -> %v\n", err)
		return []byte("")
    }

	// We read all the bytes of the image
    // Types: data []byte
    data, err := ioutil.ReadAll(res.Body)

	// You have to manually close the body, check docs
    // This is required if you want to use things like
    // Keep-Alive and other HTTP sorcery.
    defer res.Body.Close()

    if err != nil {
        fmt.Printf("Error ioutil.ReadAll -> %v\n", err)
		return []byte("")
    }

	// You can now save it to disk or whatever...
	// ioutil.WriteFile("TMP_TILE.png", data, 0644)
	// ioutil.WriteFile("TMP_TILE.png", data, 0666)

	return data
}

// BytesToPngImage
func BytesToPngImage(b []byte) image.Image {
	img, err := png.Decode(bytes.NewReader(b))
	if nil != err {
		panic(err)
	}
	return img
}

func mergePngTiles() {
	// Get bounds for new image
	size := 256
	cols := 0
	rows := 0
	for i := range tiles_map {
		cols += size
		rows = len(tiles_map[i]) * size
	}

	// collect pixel data from each image.
	// each image has a x-offset and Y-offset from the first.
	var pixelSum []*Pixel
	x := 0
	for i := range tiles_map {
		y := 0
		for j := range tiles_map[i] {
			pixels := DecodePixelsFromImage(tiles_map[i][j], x, y)
			pixelSum = append(pixelSum, pixels...)
			y += size
			fmt.Println(x,y)
		}
		x += size
	}

	// Set a new size for the new image equal to the max width
	// of bigger image and max height of two images combined
	newRect := image.Rectangle{
		Min: image.Point{X: 0, Y: 0},
		Max: image.Point{X: cols, Y: rows},
	}

	finImage := image.NewRGBA(newRect)
	// This is the cool part, all you have to do is loop through
	// each Pixel and set the image's color on the go
	for _, px := range pixelSum {
			finImage.Set(
				px.Point.X,
				px.Point.Y,
				px.Color,
			)
	}
	draw.Draw(finImage, finImage.Bounds(), finImage, image.Point{0, 0}, draw.Src)

	// Create a new file and write to it
	out, err := os.Create("./output.png")
	if err != nil {
		panic(err)
		os.Exit(1)
	}
	err = png.Encode(out, finImage)
	if err != nil {
		panic(err)
		os.Exit(1)
	}
}

func main() {
	base_url := "http://localhost:8080/tms/1.0/population"
	// base_url := "http://localhost:8080/tms/1.0/osm"

	minlat := float64(35)
	maxlat := float64(70)
	minlng := float64(0)
	maxlng := float64(16)
	zoom := 7

	tiles := getTileNames(minlat, maxlat, minlng, maxlng, zoom)

	for _, v := range tiles {
		tile_url := fmt.Sprintf("/%v/%v/%v.png", v.z, v.x, v.y)
		data := getTilePngBytesFromUrl(base_url+tile_url)

		img := BytesToPngImage(data)
		tiles_map[v.x] = append(tiles_map[v.x], img)

	}

	mergePngTiles()
}

/*

export GOPATH="`pwd`"

*/
<commit_msg>comment on stitch.go<commit_after>package main

import (
	"fmt"
	"bytes"
	"math"
	"time"
	"io/ioutil"
	"net/http"

	"os"
	"image"
	"image/png"
	"image/draw"
    "image/color"
)

// http://stackoverflow.com/questions/35964656/golang-how-to-concatenate-append-images-to-one-another

// Create a struct to deal with pixel
type Pixel struct {
    Point image.Point
    Color color.Color
}

// Decode image.Image's pixel data into []*Pixel
func DecodePixelsFromImage(img image.Image, offsetX, offsetY int) []*Pixel {
    pixels := []*Pixel{}
    for y := 0; y <= img.Bounds().Max.Y; y++ {
        for x := 0; x <= img.Bounds().Max.X; x++ {
            p := &Pixel{
                Point: image.Point{x + offsetX, y + offsetY},
                Color: img.At(x, y),
            }
            pixels = append(pixels, p)
        }
    }
    return pixels
}

var client = &http.Client{
	Timeout: time.Second * 5,
}

var tiles_map map[int][]image.Image

func init() {
	tiles_map = make(map[int][]image.Image)
}

// degTorad converts degree to radians.
func degTorad(deg float64) float64 {
	return deg * math.Pi / 180;
}

// deg2num converts latlng to tile number
func deg2num(lat_deg float64, lon_deg float64, zoom int) (int, int) {
    lat_rad := degTorad(lat_deg)
    n := math.Pow(2.0, float64(zoom))
    xtile := int((lon_deg + 180.0) / 360.0 * n)
    ytile := int((1.0 - math.Log(math.Tan(lat_rad) + (1 / math.Cos(lat_rad))) / math.Pi) / 2.0 * n)
    return xtile, ytile
}

// xyz
type xyz struct {
	x int
	y int
	z int
}

// getTileNames
func getTileNames(minlat, maxlat, minlng, maxlng float64, z int) []xyz {
	tiles := []xyz{}
	// // upper right
	// ur_tile_x, ur_tile_y := deg2num(float64(70), float64(16), z)
	// // lower left
	// ll_tile_x, ll_tile_y := deg2num(float64(35), float64(0), z)
	// upper right
	ur_tile_x, ur_tile_y := deg2num(maxlat, maxlng, z)
	// lower left
	ll_tile_x, ll_tile_y := deg2num(minlat, minlng, z)

	for x := ll_tile_x-1; x < ur_tile_x+1; x++ {
		if x < 0 { x++ }
		for y := ur_tile_y-1; y < ll_tile_y+1; y++ {
			if y < 0 { y++ }
			tiles = append(tiles, xyz{x,y,z})
		}
	}
	return tiles
}

// getTilePngFromUrl
func getTilePngBytesFromUrl(tile_url string) []byte {
	fmt.Println("GET", tile_url)

	// Just a simple GET request to the image URL
    // We get back a *Response, and an error
	res, err := client.Get(tile_url)
	if err != nil {
        fmt.Printf("Error http.Get -> %v\n", err)
		return []byte("")
    }

	// We read all the bytes of the image
    // Types: data []byte
    data, err := ioutil.ReadAll(res.Body)

	// You have to manually close the body, check docs
    // This is required if you want to use things like
    // Keep-Alive and other HTTP sorcery.
    defer res.Body.Close()

    if err != nil {
        fmt.Printf("Error ioutil.ReadAll -> %v\n", err)
		return []byte("")
    }

	// You can now save it to disk or whatever...
	// ioutil.WriteFile("TMP_TILE.png", data, 0644)
	// ioutil.WriteFile("TMP_TILE.png", data, 0666)

	return data
}

// BytesToPngImage
func BytesToPngImage(b []byte) image.Image {
	img, err := png.Decode(bytes.NewReader(b))
	if nil != err {
		panic(err)
	}
	return img
}

func mergePngTiles() {
	// Get bounds for new image
	size := 256
	cols := 0
	rows := 0
	for i := range tiles_map {
		cols += size
		rows = len(tiles_map[i]) * size
	}

	// collect pixel data from each image.
	// each image has a x-offset and Y-offset from the first.
	var pixelSum []*Pixel
	x := 0
	for i := range tiles_map {
		y := 0
		for j := range tiles_map[i] {
			pixels := DecodePixelsFromImage(tiles_map[i][j], x, y)
			pixelSum = append(pixelSum, pixels...)
			y += size
			fmt.Println(x,y)
		}
		x += size
	}

	// Set a new size for the new image equal to the max width
	// of bigger image and max height of two images combined
	newRect := image.Rectangle{
		Min: image.Point{X: 0, Y: 0},
		Max: image.Point{X: cols, Y: rows},
	}

	finImage := image.NewRGBA(newRect)
	// This is the cool part, all you have to do is loop through
	// each Pixel and set the image's color on the go
	for _, px := range pixelSum {
			finImage.Set(
				px.Point.X,
				px.Point.Y,
				px.Color,
			)
	}
	draw.Draw(finImage, finImage.Bounds(), finImage, image.Point{0, 0}, draw.Src)

	// Create a new file and write to it
	out, err := os.Create("./output.png")
	if err != nil {
		panic(err)
		os.Exit(1)
	}
	err = png.Encode(out, finImage)
	if err != nil {
		panic(err)
		os.Exit(1)
	}
}

func main() {
	base_url := "http://localhost:8080/tms/1.0/population"
	// base_url := "http://localhost:8080/tms/1.0/osm"

	minlat := float64(35)
	maxlat := float64(70)
	minlng := float64(0)
	maxlng := float64(16)
	zoom := 7

	tiles := getTileNames(minlat, maxlat, minlng, maxlng, zoom)

	for _, v := range tiles {
		tile_url := fmt.Sprintf("/%v/%v/%v.png", v.z, v.x, v.y)
		data := getTilePngBytesFromUrl(base_url+tile_url)

		img := BytesToPngImage(data)
		tiles_map[v.x] = append(tiles_map[v.x], img)

	}

	mergePngTiles()
}

/*

export GOPATH="`pwd`"

*/
<|endoftext|>


<commit_before>package streams

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"

	"github.com/FederationOfFathers/dashboard/clients/mixer"
	"github.com/FederationOfFathers/dashboard/db"
	"github.com/FederationOfFathers/dashboard/messaging"
	"go.uber.org/zap"
)

var bplog *zap.Logger

type Mixer mixer.Mixer

type mixerChannelResponse struct {
	Name          string `json:"name"`
	Token         string `json:"token"`
	ChannelOnline bool   `json:"online"`
	ChannelID     int64
	User          struct {
		AvatarUrl string `json:"avatarUrl"`
	} `json:"user"`
	Type struct {
		Name string `json:"name"`
	} `json:"type"`
}

type mixerManifestResponse struct {
	StartedAt string `json:"startedAt"`
}

func (b *Mixer) Update() error {
	b.Online = false
	b.Game = ""
	b.StartedAt = ""
	var c = new(mixerChannelResponse)
	var cURL = fmt.Sprintf("https://mixer.com/api/v1/channels/%s", b.BeamUsername)
	bplog.Info("fetching channel", zap.String("url", cURL))
	chResponse, err := http.Get(cURL)
	if err != nil {
		return err
	}
	defer chResponse.Body.Close()
	if chResponse.StatusCode == 404 {
		bplog.Info(fmt.Sprintf("channel %s is not valid (404)", cURL))
		return nil
	} else if chResponse.StatusCode != 200 {
		return fmt.Errorf("got HTTP %d '%s' for '%s'", chResponse.StatusCode, chResponse.Status, cURL)
	}
	if err := json.NewDecoder(chResponse.Body).Decode(&c); err != nil {
		return err
	}
	if !c.ChannelOnline {
		return nil
	}
	b.Online = c.ChannelOnline
	b.ChannelID = c.ChannelID
	b.Game = c.Type.Name
	b.Title = c.Name
	b.BeamUsername = c.Token
	b.AvatarUrl = c.User.AvatarUrl
	var m = new(mixerManifestResponse)
	var mURL = fmt.Sprintf("https://mixer.com/api/v1/channels/%d/manifest.light2", b.ChannelID)
	bplog.Info("fetching manifest", zap.String("url", mURL))
	rsp, err := http.Get(mURL)
	if err != nil {
		return err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode == 404 {
		// Stream went offline
		return nil
	}
	if rsp.StatusCode != 200 {
		return fmt.Errorf("got HTTP %d '%s' for '%s'", chResponse.StatusCode, chResponse.Status, mURL)
	}
	if err := json.NewDecoder(rsp.Body).Decode(&m); err != nil {
		return err
	}
	b.StartedAt = m.StartedAt
	b.StartedTime, err = time.Parse(time.RFC3339Nano, m.StartedAt)
	if err != nil {
		return err
	}
	return nil
}

func mindMixer() {
	bplog = Logger.With(zap.String("service", "mixer"))
	bplog.Debug("begin minding")
	for _, stream := range Streams {
		if stream.Beam == "" {
			bplog.Debug("not a mixer.com stream", zap.Int("id", stream.ID), zap.Int("member_id", stream.MemberID))
			continue
		}
		bplog.Debug("minding mixer.com stream", zap.String("mixer id", stream.Beam))
		updateMixer(stream)
	}
	bplog.Debug("end minding")
}

func updateMixer(s *db.Stream) {
	m := Mixer{
		BeamUsername: s.Beam,
	}
	err := m.Update()
	if err != nil {
		bplog.Error("Error updating mixer stream details", zap.Error(err))
		return
	}

	if !m.Online {
		var save bool
		if s.BeamStop < s.BeamStart {
			s.BeamStop = time.Now().Unix()
			save = true
		}
		if s.BeamStop < s.BeamStart {
			s.BeamStop = s.BeamStart + 1
			save = true
		}
		if save {
			stopError := s.Save()
			if stopError != nil {
				bplog.Error(fmt.Sprintf("Unable to save stop data: %v", stopError))
			}
		}
		return
	}

	var startedAt = m.StartedTime.Unix()
	if startedAt <= s.BeamStart && s.BeamGame == m.Game {
		// Continuation of known stream
		return
	}

	s.BeamStart = startedAt
	s.BeamGame = m.Game
	if s.BeamStop > s.BeamStart {
		s.BeamStop = s.BeamStart - 1
	}
	updateErr := s.Save()
	if updateErr != nil {
		bplog.Error(fmt.Sprintf("Unable to save stream data: %v", updateErr))
		return
	}

	messaging.SendMixerStreamMessage(mixer.Mixer(m))
}
<commit_msg>Using json.Unmarshal instead of json.Decode<commit_after>package streams

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/FederationOfFathers/dashboard/clients/mixer"
	"github.com/FederationOfFathers/dashboard/db"
	"github.com/FederationOfFathers/dashboard/messaging"
	"go.uber.org/zap"
)

var bplog *zap.Logger

type Mixer mixer.Mixer

type mixerChannelResponse struct {
	Name          string `json:"name"`
	Token         string `json:"token"`
	ChannelOnline bool   `json:"online"`
	ChannelID     int64
	User          struct {
		AvatarUrl string `json:"avatarUrl"`
	} `json:"user"`
	Type struct {
		Name string `json:"name"`
	} `json:"type"`
}

type mixerManifestResponse struct {
	StartedAt string `json:"startedAt"`
}

func (b *Mixer) Update() error {
	b.Online = false
	b.Game = ""
	b.StartedAt = ""
	var c = mixerChannelResponse{}
	var cURL = fmt.Sprintf("https://mixer.com/api/v1/channels/%s", b.BeamUsername)
	bplog.Debug("fetching channel", zap.String("url", cURL))
	chResponse, err := http.Get(cURL)
	if err != nil {
		return err
	}
	defer chResponse.Body.Close()
	if chResponse.StatusCode == 404 {
		bplog.Info(fmt.Sprintf("channel %s is not valid (404)", cURL))
		return nil
	} else if chResponse.StatusCode != 200 {
		return fmt.Errorf("got HTTP %d '%s' for '%s'", chResponse.StatusCode, chResponse.Status, cURL)
	}

	bodyContent, err := ioutil.ReadAll(chResponse.Body)
	if err != nil {
		bplog.Error("Unable to read body bytes", zap.Error(err))
	}

	if err := json.Unmarshal(bodyContent, &c); err != nil {
		return fmt.Errorf("Unable to decode JSON - %s", err.Error())
	}
	if !c.ChannelOnline {
		return nil
	}
	b.Online = c.ChannelOnline
	b.ChannelID = c.ChannelID
	b.Game = c.Type.Name
	b.Title = c.Name
	b.BeamUsername = c.Token
	b.AvatarUrl = c.User.AvatarUrl
	var m = mixerManifestResponse{}
	var mURL = fmt.Sprintf("https://mixer.com/api/v1/channels/%d/manifest.light2", b.ChannelID)
	bplog.Debug("fetching manifest", zap.String("url", mURL))
	rsp, err := http.Get(mURL)
	if err != nil {
		return err
	}
	defer rsp.Body.Close()
	if rsp.StatusCode == 404 {
		// Stream went offline
		return nil
	}
	if rsp.StatusCode != 200 {
		return fmt.Errorf("got HTTP %d '%s' for '%s'", chResponse.StatusCode, chResponse.Status, mURL)
	}
	if err := json.NewDecoder(rsp.Body).Decode(&m); err != nil {
		return err
	}
	b.StartedAt = m.StartedAt
	b.StartedTime, err = time.Parse(time.RFC3339Nano, m.StartedAt)
	if err != nil {
		return err
	}
	return nil
}

func mindMixer() {
	bplog = Logger.With(zap.String("service", "mixer"))
	bplog.Debug("begin minding")
	for _, stream := range Streams {
		if stream.Beam == "" {
			bplog.Debug("not a mixer.com stream", zap.Int("id", stream.ID), zap.Int("member_id", stream.MemberID))
			continue
		}
		bplog.Debug("minding mixer.com stream", zap.String("mixer id", stream.Beam))
		updateMixer(stream)
	}
	bplog.Debug("end minding")
}

func updateMixer(s *db.Stream) {
	m := Mixer{
		BeamUsername: s.Beam,
	}
	err := m.Update()
	if err != nil {
		bplog.Error("Error updating mixer stream details", zap.Error(err))
		return
	}

	if !m.Online {
		var save bool
		if s.BeamStop < s.BeamStart {
			s.BeamStop = time.Now().Unix()
			save = true
		}
		if s.BeamStop < s.BeamStart {
			s.BeamStop = s.BeamStart + 1
			save = true
		}
		if save {
			stopError := s.Save()
			if stopError != nil {
				bplog.Error(fmt.Sprintf("Unable to save stop data: %v", stopError))
			}
		}
		return
	}

	var startedAt = m.StartedTime.Unix()
	if startedAt <= s.BeamStart && s.BeamGame == m.Game {
		// Continuation of known stream
		return
	}

	s.BeamStart = startedAt
	s.BeamGame = m.Game
	if s.BeamStop > s.BeamStart {
		s.BeamStop = s.BeamStart - 1
	}
	updateErr := s.Save()
	if updateErr != nil {
		bplog.Error(fmt.Sprintf("Unable to save stream data: %v", updateErr))
		return
	}

	messaging.SendMixerStreamMessage(mixer.Mixer(m))
}
<|endoftext|>


<commit_before>package validator

import (
	"context"
	"reflect"
)

// StructLevelFunc accepts all values needed for struct level validation
type StructLevelFunc func(sl StructLevel)

// StructLevelFuncCtx accepts all values needed for struct level validation
// but also allows passing of contextual validation information vi context.Context.
type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)

// wrapStructLevelFunc wraps noramal StructLevelFunc makes it compatible with StructLevelFuncCtx
func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
	return func(ctx context.Context, sl StructLevel) {
		fn(sl)
	}
}

// StructLevel contains all the information and helper functions
// to validate a struct
type StructLevel interface {

	// returns the main validation object, in case one want to call validations internally.
	// this is so you don;t have to use anonymous functoins to get access to the validate
	// instance.
	Validator() *Validate

	// returns the top level struct, if any
	Top() reflect.Value

	// returns the current fields parent struct, if any
	Parent() reflect.Value

	// returns the current struct.
	Current() reflect.Value

	// ExtractType gets the actual underlying type of field value.
	// It will dive into pointers, customTypes and return you the
	// underlying value and it's kind.
	ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)

	// reports an error just by passing the field and tag information
	//
	// NOTES:
	//
	// fieldName and altName get appended to the existing namespace that
	// validator is on. eg. pass 'FirstName' or 'Names[0]' depending
	// on the nesting
	//
	// tag can be an existing validation tag or just something you make up
	// and process on the flip side it's up to you.
	ReportError(field interface{}, fieldName, structFieldName string, tag, param string)

	// reports an error just by passing ValidationErrors
	//
	// NOTES:
	//
	// relativeNamespace and relativeActualNamespace get appended to the
	// existing namespace that validator is on.
	// eg. pass 'User.FirstName' or 'Users[0].FirstName' depending
	// on the nesting. most of the time they will be blank, unless you validate
	// at a level lower the the current field depth
	ReportValidationErrors(relativeNamespace, relativeActualNamespace string, errs ValidationErrors)
}

var _ StructLevel = new(validate)

// Top returns the top level struct
//
// NOTE: this can be the same as the current struct being validated
// if not is a nested struct.
//
// this is only called when within Struct and Field Level validation and
// should not be relied upon for an acurate value otherwise.
func (v *validate) Top() reflect.Value {
	return v.top
}

// Parent returns the current structs parent
//
// NOTE: this can be the same as the current struct being validated
// if not is a nested struct.
//
// this is only called when within Struct and Field Level validation and
// should not be relied upon for an acurate value otherwise.
func (v *validate) Parent() reflect.Value {
	return v.slflParent
}

// Current returns the current struct.
func (v *validate) Current() reflect.Value {
	return v.slCurrent
}

// Validator returns the main validation object, in case one want to call validations internally.
func (v *validate) Validator() *Validate {
	return v.v
}

// ExtractType gets the actual underlying type of field value.
func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind, bool) {
	return v.extractTypeInternal(field, false)
}

// ReportError reports an error just by passing the field and tag information
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {

	fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)

	if len(structFieldName) == 0 {
		structFieldName = fieldName
	}

	v.str1 = string(append(v.ns, fieldName...))

	if v.v.hasTagNameFunc || fieldName != structFieldName {
		v.str2 = string(append(v.actualNs, structFieldName...))
	} else {
		v.str2 = v.str1
	}

	if kind == reflect.Invalid {

		v.errs = append(v.errs,
			&fieldError{
				v:              v.v,
				tag:            tag,
				actualTag:      tag,
				ns:             v.str1,
				structNs:       v.str2,
				fieldLen:       uint8(len(fieldName)),
				structfieldLen: uint8(len(structFieldName)),
				param:          param,
				kind:           kind,
			},
		)
		return
	}

	v.errs = append(v.errs,
		&fieldError{
			v:              v.v,
			tag:            tag,
			actualTag:      tag,
			ns:             v.str1,
			structNs:       v.str2,
			fieldLen:       uint8(len(fieldName)),
			structfieldLen: uint8(len(structFieldName)),
			value:          fv.Interface(),
			param:          param,
			kind:           kind,
			typ:            fv.Type(),
		},
	)
}

// ReportValidationErrors reports ValidationErrors obtained from running validations within the Struct Level validation.
//
// NOTE: this function prepends the current namespace to the relative ones.
func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {

	var err *fieldError

	for i := 0; i < len(errs); i++ {

		err = errs[i].(*fieldError)
		err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...))
		err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...))

		v.errs = append(v.errs, err)
	}
}
<commit_msg>Fix typos<commit_after>package validator

import (
	"context"
	"reflect"
)

// StructLevelFunc accepts all values needed for struct level validation
type StructLevelFunc func(sl StructLevel)

// StructLevelFuncCtx accepts all values needed for struct level validation
// but also allows passing of contextual validation information via context.Context.
type StructLevelFuncCtx func(ctx context.Context, sl StructLevel)

// wrapStructLevelFunc wraps normal StructLevelFunc makes it compatible with StructLevelFuncCtx
func wrapStructLevelFunc(fn StructLevelFunc) StructLevelFuncCtx {
	return func(ctx context.Context, sl StructLevel) {
		fn(sl)
	}
}

// StructLevel contains all the information and helper functions
// to validate a struct
type StructLevel interface {

	// returns the main validation object, in case one wants to call validations internally.
	// this is so you don't have to use anonymous functions to get access to the validate
	// instance.
	Validator() *Validate

	// returns the top level struct, if any
	Top() reflect.Value

	// returns the current fields parent struct, if any
	Parent() reflect.Value

	// returns the current struct.
	Current() reflect.Value

	// ExtractType gets the actual underlying type of field value.
	// It will dive into pointers, customTypes and return you the
	// underlying value and its kind.
	ExtractType(field reflect.Value) (value reflect.Value, kind reflect.Kind, nullable bool)

	// reports an error just by passing the field and tag information
	//
	// NOTES:
	//
	// fieldName and altName get appended to the existing namespace that
	// validator is on. e.g. pass 'FirstName' or 'Names[0]' depending
	// on the nesting
	//
	// tag can be an existing validation tag or just something you make up
	// and process on the flip side it's up to you.
	ReportError(field interface{}, fieldName, structFieldName string, tag, param string)

	// reports an error just by passing ValidationErrors
	//
	// NOTES:
	//
	// relativeNamespace and relativeActualNamespace get appended to the
	// existing namespace that validator is on.
	// e.g. pass 'User.FirstName' or 'Users[0].FirstName' depending
	// on the nesting. most of the time they will be blank, unless you validate
	// at a level lower the the current field depth
	ReportValidationErrors(relativeNamespace, relativeActualNamespace string, errs ValidationErrors)
}

var _ StructLevel = new(validate)

// Top returns the top level struct
//
// NOTE: this can be the same as the current struct being validated
// if not is a nested struct.
//
// this is only called when within Struct and Field Level validation and
// should not be relied upon for an acurate value otherwise.
func (v *validate) Top() reflect.Value {
	return v.top
}

// Parent returns the current structs parent
//
// NOTE: this can be the same as the current struct being validated
// if not is a nested struct.
//
// this is only called when within Struct and Field Level validation and
// should not be relied upon for an acurate value otherwise.
func (v *validate) Parent() reflect.Value {
	return v.slflParent
}

// Current returns the current struct.
func (v *validate) Current() reflect.Value {
	return v.slCurrent
}

// Validator returns the main validation object, in case one want to call validations internally.
func (v *validate) Validator() *Validate {
	return v.v
}

// ExtractType gets the actual underlying type of field value.
func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind, bool) {
	return v.extractTypeInternal(field, false)
}

// ReportError reports an error just by passing the field and tag information
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {

	fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)

	if len(structFieldName) == 0 {
		structFieldName = fieldName
	}

	v.str1 = string(append(v.ns, fieldName...))

	if v.v.hasTagNameFunc || fieldName != structFieldName {
		v.str2 = string(append(v.actualNs, structFieldName...))
	} else {
		v.str2 = v.str1
	}

	if kind == reflect.Invalid {

		v.errs = append(v.errs,
			&fieldError{
				v:              v.v,
				tag:            tag,
				actualTag:      tag,
				ns:             v.str1,
				structNs:       v.str2,
				fieldLen:       uint8(len(fieldName)),
				structfieldLen: uint8(len(structFieldName)),
				param:          param,
				kind:           kind,
			},
		)
		return
	}

	v.errs = append(v.errs,
		&fieldError{
			v:              v.v,
			tag:            tag,
			actualTag:      tag,
			ns:             v.str1,
			structNs:       v.str2,
			fieldLen:       uint8(len(fieldName)),
			structfieldLen: uint8(len(structFieldName)),
			value:          fv.Interface(),
			param:          param,
			kind:           kind,
			typ:            fv.Type(),
		},
	)
}

// ReportValidationErrors reports ValidationErrors obtained from running validations within the Struct Level validation.
//
// NOTE: this function prepends the current namespace to the relative ones.
func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {

	var err *fieldError

	for i := 0; i < len(errs); i++ {

		err = errs[i].(*fieldError)
		err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...))
		err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...))

		v.errs = append(v.errs, err)
	}
}
<|endoftext|>


<commit_before>package subsets

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestThree(t *testing.T) {
	returned := make(map[string]bool)
	callback := func(indexes []int) {
		b := make([]byte, len(indexes))
		for i := range indexes {
			b[i] = byte('A' + indexes[i])
		}
		returned[string(b)] = true
	}
	Enumerate(3, callback)
	expected := map[string]bool{
		"A":   true,
		"B":   true,
		"C":   true,
		"AB":  true,
		"AC":  true,
		"BC":  true,
		"ABC": true,
	}
	assert.Equal(t, expected, returned)
}

func nullCallback(_ []int) {}
func BenchmarkThree(b *testing.B) {

	for i := 0; i < b.N; i++ {
		Enumerate(3, nullCallback)
	}
}
func BenchmarkSeven(b *testing.B) {

	for i := 0; i < b.N; i++ {
		Enumerate(7, nullCallback)
	}
}
func BenchmarkFourteen(b *testing.B) {

	for i := 0; i < b.N; i++ {
		Enumerate(14, nullCallback)
	}
}
func BenchmarkSixteen(b *testing.B) {

	for i := 0; i < b.N; i++ {
		Enumerate(16, nullCallback)
	}
}
<commit_msg>Benchmarks<commit_after>package subsets

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func nullCallback(_ []int) {}

type testCallback map[string]bool

func (t testCallback) callback(indexes []int) {
	b := make([]byte, len(indexes))
	for i := range indexes {
		b[i] = byte('A' + indexes[i])
	}
	t[string(b)] = true
}

func TestThree(t *testing.T) {
	callback := make(testCallback)
	Enumerate(3, callback.callback)
	expected := map[string]bool{
		"A":   true,
		"B":   true,
		"C":   true,
		"AB":  true,
		"AC":  true,
		"BC":  true,
		"ABC": true,
	}
	assert.Equal(t, expected, map[string]bool(callback))
}

func TestCompare(t *testing.T) {
	callback1 := make(testCallback)
	Enumerate(8, callback1.callback)
	callback2 := make(testCallback)
	traditionalEnumerate(8, callback2.callback)
	assert.Equal(t, callback2, callback1)
}

func BenchmarkThree(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Enumerate(3, nullCallback)
	}
}
func BenchmarkSeven(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Enumerate(7, nullCallback)
	}
}
func BenchmarkFourteen(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Enumerate(14, nullCallback)
	}
}
func BenchmarkSixteen(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Enumerate(16, nullCallback)
	}
}

func traditionalEnumerate(n int, callback Callback) {
	max := uint64(1<<uint(n)) - 1
	indexes := make([]int, n)
	for bits := uint64(1); bits <= max; bits++ {
		size := 0
		for bit := 0; bit <= n; bit++ {
			if (bits & (1 << uint(bit))) != 0 {
				indexes[size] = bit
				size++
			}
		}
		callback(indexes[:size])
	}
}

func BenchmarkThreeTrad(b *testing.B) {
	for i := 0; i < b.N; i++ {
		traditionalEnumerate(3, nullCallback)
	}
}
func BenchmarkSixteenTrad(b *testing.B) {
	for i := 0; i < b.N; i++ {
		traditionalEnumerate(16, nullCallback)
	}
}

func BenchmarkSixteenIsItReallyWorthIt(b *testing.B) {
	// Do a little bit of work for each iteration so I can get a feel for how significant
	// the cost saving is likely to be.
	var sink int
	callbackWithWork := func(indexes []int) {
		for i := range indexes {
			sink = sink<<7 + indexes[i]
			sink = sink<<4 + i
		}
	}
	for i := 0; i < b.N; i++ {
		Enumerate(16, callbackWithWork)
	}
}
<|endoftext|>


<commit_before>package svg

import (
	"github.com/tdewolff/minify"
	"github.com/tdewolff/parse"
	"github.com/tdewolff/strconv"
)

type PathData struct {
	x, y        float64
	coords      [][]byte
	coordFloats []float64

	altBuffer   []byte
	coordBuffer []byte
}

func ShortenPathData(b []byte, p *PathData) []byte {
	var x0, y0 float64
	var cmd byte

	p.x, p.y = 0.0, 0.0
	p.coords = p.coords[:0]
	p.coordFloats = p.coordFloats[:0]

	j := 0
	for i := 0; i < len(b); i++ {
		c := b[i]
		if c == ' ' || c == ',' || c == '\n' || c == '\r' || c == '\t' {
			continue
		} else if c >= 'A' && (cmd == 0 || cmd != c) { // any command
			if cmd != 0 {
				j += p.copyInstruction(b[j:], cmd)
				if cmd == 'M' || cmd == 'm' {
					x0 = p.x
					y0 = p.y
				} else if cmd == 'Z' || cmd == 'z' {
					p.x = x0
					p.y = y0
				}
			}
			cmd = c
			p.coords = p.coords[:0]
			p.coordFloats = p.coordFloats[:0]
		} else if n := parse.Number(b[i:]); n > 0 {
			f, _ := strconv.ParseFloat(b[i : i+n])
			p.coords = append(p.coords, b[i:i+n])
			p.coordFloats = append(p.coordFloats, f)
			i += n - 1
		}
	}
	j += p.copyInstruction(b[j:], cmd)
	return b[:j]
}

func (p *PathData) copyInstruction(b []byte, cmd byte) int {
	n := len(p.coords)
	isRelativeCmd := cmd >= 'a'

	// get new cursor coordinates
	ax, ay := p.x, p.y
	if n >= 2 && (cmd == 'M' || cmd == 'm' || cmd == 'L' || cmd == 'l' || cmd == 'C' || cmd == 'c' || cmd == 'S' || cmd == 's' || cmd == 'Q' || cmd == 'q' || cmd == 'T' || cmd == 't' || cmd == 'A' || cmd == 'a') {
		ax = p.coordFloats[n-2]
		ay = p.coordFloats[n-1]
	} else if n >= 1 && (cmd == 'H' || cmd == 'h' || cmd == 'V' || cmd == 'v') {
		if cmd == 'H' || cmd == 'h' {
			ax = p.coordFloats[n-1]
		} else {
			ay = p.coordFloats[n-1]
		}
	} else if cmd == 'Z' || cmd == 'z' {
		b[0] = 'z'
		return 1
	} else {
		return 0
	}

	// make a current and alternated path with absolute/relative altered
	b = p.shortenCurPosInstruction(b, cmd)
	if isRelativeCmd {
		p.altBuffer = p.shortenAltPosInstruction(p.altBuffer[:0], cmd-'a'+'A', p.x, p.y)
	} else {
		p.altBuffer = p.shortenAltPosInstruction(p.altBuffer[:0], cmd-'A'+'a', -p.x, -p.y)
	}

	// choose shortest, relative or absolute path?
	if len(p.altBuffer) < len(b) {
		copy(b, p.altBuffer)
		b = b[:len(p.altBuffer)]
	}

	// set new cursor coordinates
	if isRelativeCmd {
		p.x += ax
		p.y += ay
	} else {
		p.x = ax
		p.y = ay
	}
	return len(b)
}

func (p *PathData) shortenCurPosInstruction(b []byte, cmd byte) []byte {
	prevDigit := false
	prevDigitRequiresSpace := true

	b[0] = cmd
	j := 1
	for _, coord := range p.coords {
		coord := minify.Number(coord)
		if prevDigit && (coord[0] >= '0' && coord[0] <= '9' || coord[0] == '.' && prevDigitRequiresSpace) {
			b[j] = ' '
			j++
		}
		prevDigit = true
		prevDigitRequiresSpace = true
		for _, c := range coord {
			if c == '.' || c == 'e' || c == 'E' {
				prevDigitRequiresSpace = false
				break
			}
		}
		j += copy(b[j:], coord)
	}
	return b[:j]
}

func (p *PathData) shortenAltPosInstruction(b []byte, cmd byte, dx, dy float64) []byte {
	prevDigit := false
	prevDigitRequiresSpace := true

	b = append(b, cmd)
	for i, f := range p.coordFloats {
		if cmd == 'L' || cmd == 'l' || cmd == 'C' || cmd == 'c' || cmd == 'S' || cmd == 's' || cmd == 'Q' || cmd == 'q' || cmd == 'T' || cmd == 't' || cmd == 'M' || cmd == 'm' {
			if i%2 == 0 {
				f += dx
			} else {
				f += dy
			}
		} else if cmd == 'H' || cmd == 'h' {
			f += dx
		} else if cmd == 'V' || cmd == 'v' {
			f += dy
		} else if cmd == 'A' || cmd == 'a' {
			if i%7 == 5 {
				f += dx
			} else if i%7 == 6 {
				f += dy
			}
		} else {
			continue
		}
		p.coordBuffer = strconv.AppendFloat(p.coordBuffer[:0], f)

		coord := minify.Number(p.coordBuffer)
		if prevDigit && (coord[0] >= '0' && coord[0] <= '9' || coord[0] == '.' && prevDigitRequiresSpace) {
			b = append(b, ' ')
		}
		prevDigit = true
		prevDigitRequiresSpace = true
		for _, c := range coord {
			if c == '.' || c == 'e' || c == 'E' {
				prevDigitRequiresSpace = false
				break
			}
		}
		b = append(b, coord...)
	}
	return b
}
<commit_msg>Use fallback for AppendFloat<commit_after>package svg

import (
	strconvStdlib "strconv"

	"github.com/tdewolff/minify"
	"github.com/tdewolff/parse"
	"github.com/tdewolff/strconv"
)

type PathData struct {
	x, y        float64
	coords      [][]byte
	coordFloats []float64

	altBuffer   []byte
	coordBuffer []byte
}

func ShortenPathData(b []byte, p *PathData) []byte {
	var x0, y0 float64
	var cmd byte

	p.x, p.y = 0.0, 0.0
	p.coords = p.coords[:0]
	p.coordFloats = p.coordFloats[:0]

	j := 0
	for i := 0; i < len(b); i++ {
		c := b[i]
		if c == ' ' || c == ',' || c == '\n' || c == '\r' || c == '\t' {
			continue
		} else if c >= 'A' && (cmd == 0 || cmd != c) { // any command
			if cmd != 0 {
				j += p.copyInstruction(b[j:], cmd)
				if cmd == 'M' || cmd == 'm' {
					x0 = p.x
					y0 = p.y
				} else if cmd == 'Z' || cmd == 'z' {
					p.x = x0
					p.y = y0
				}
			}
			cmd = c
			p.coords = p.coords[:0]
			p.coordFloats = p.coordFloats[:0]
		} else if n := parse.Number(b[i:]); n > 0 {
			f, _ := strconv.ParseFloat(b[i : i+n])
			p.coords = append(p.coords, b[i:i+n])
			p.coordFloats = append(p.coordFloats, f)
			i += n - 1
		}
	}
	j += p.copyInstruction(b[j:], cmd)
	return b[:j]
}

func (p *PathData) copyInstruction(b []byte, cmd byte) int {
	n := len(p.coords)
	isRelativeCmd := cmd >= 'a'

	// get new cursor coordinates
	ax, ay := p.x, p.y
	if n >= 2 && (cmd == 'M' || cmd == 'm' || cmd == 'L' || cmd == 'l' || cmd == 'C' || cmd == 'c' || cmd == 'S' || cmd == 's' || cmd == 'Q' || cmd == 'q' || cmd == 'T' || cmd == 't' || cmd == 'A' || cmd == 'a') {
		ax = p.coordFloats[n-2]
		ay = p.coordFloats[n-1]
	} else if n >= 1 && (cmd == 'H' || cmd == 'h' || cmd == 'V' || cmd == 'v') {
		if cmd == 'H' || cmd == 'h' {
			ax = p.coordFloats[n-1]
		} else {
			ay = p.coordFloats[n-1]
		}
	} else if cmd == 'Z' || cmd == 'z' {
		b[0] = 'z'
		return 1
	} else {
		return 0
	}

	// make a current and alternated path with absolute/relative altered
	b = p.shortenCurPosInstruction(b, cmd)
	if isRelativeCmd {
		p.altBuffer = p.shortenAltPosInstruction(p.altBuffer[:0], cmd-'a'+'A', p.x, p.y)
	} else {
		p.altBuffer = p.shortenAltPosInstruction(p.altBuffer[:0], cmd-'A'+'a', -p.x, -p.y)
	}

	// choose shortest, relative or absolute path?
	if len(p.altBuffer) < len(b) {
		copy(b, p.altBuffer)
		b = b[:len(p.altBuffer)]
	}

	// set new cursor coordinates
	if isRelativeCmd {
		p.x += ax
		p.y += ay
	} else {
		p.x = ax
		p.y = ay
	}
	return len(b)
}

func (p *PathData) shortenCurPosInstruction(b []byte, cmd byte) []byte {
	prevDigit := false
	prevDigitRequiresSpace := true

	b[0] = cmd
	j := 1
	for _, coord := range p.coords {
		coord := minify.Number(coord)
		if prevDigit && (coord[0] >= '0' && coord[0] <= '9' || coord[0] == '.' && prevDigitRequiresSpace) {
			b[j] = ' '
			j++
		}
		prevDigit = true
		prevDigitRequiresSpace = true
		for _, c := range coord {
			if c == '.' || c == 'e' || c == 'E' {
				prevDigitRequiresSpace = false
				break
			}
		}
		j += copy(b[j:], coord)
	}
	return b[:j]
}

func (p *PathData) shortenAltPosInstruction(b []byte, cmd byte, dx, dy float64) []byte {
	prevDigit := false
	prevDigitRequiresSpace := true

	b = append(b, cmd)
	for i, f := range p.coordFloats {
		if cmd == 'L' || cmd == 'l' || cmd == 'C' || cmd == 'c' || cmd == 'S' || cmd == 's' || cmd == 'Q' || cmd == 'q' || cmd == 'T' || cmd == 't' || cmd == 'M' || cmd == 'm' {
			if i%2 == 0 {
				f += dx
			} else {
				f += dy
			}
		} else if cmd == 'H' || cmd == 'h' {
			f += dx
		} else if cmd == 'V' || cmd == 'v' {
			f += dy
		} else if cmd == 'A' || cmd == 'a' {
			if i%7 == 5 {
				f += dx
			} else if i%7 == 6 {
				f += dy
			}
		} else {
			continue
		}

		coord, ok := strconv.AppendFloat(p.coordBuffer[:0], f, 6)
		if !ok {
			p.coordBuffer = strconvStdlib.AppendFloat(p.coordBuffer[:0], f, 'g', 6, 64)
			coord = minify.Number(p.coordBuffer)
		}

		if prevDigit && (coord[0] >= '0' && coord[0] <= '9' || coord[0] == '.' && prevDigitRequiresSpace) {
			b = append(b, ' ')
		}
		prevDigit = true
		prevDigitRequiresSpace = true
		for _, c := range coord {
			if c == '.' || c == 'e' || c == 'E' {
				prevDigitRequiresSpace = false
				break
			}
		}
		b = append(b, coord...)
	}
	return b
}
<|endoftext|>


<commit_before>package swgohgg

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"log"
	"strconv"
	"strings"
)

type Mod struct {
	ID       string
	Level    int
	Rarity   int
	Shape    string
	BonusSet string

	PrimStat ModStat
	SecStat  []ModStat

	UsingIn string
}

func (m *Mod) String() string {
	if m == nil {
		return "nil mod"
	}
	str := fmt.Sprintf("%s %-9s L%-2d %d* %v %v", m.ShapeIcon(), statAbbrev(m.BonusSet), m.Level, m.Rarity, m.PrimStat, m.SecStat)
	if m.UsingIn != "" {
		str += " (" + m.UsingIn + ")"
	}
	return str
}

func (m *Mod) ShapeIcon() string {
	switch m.Shape {
	case "Transmitter":
		return "◻"
	case "Processor":
		return "◇"
	case "Holo-Array":
		return "△"
	case "Data-Bus":
		return "○"
	case "Receiver":
		return "◹"
	case "Multiplexer":
		return "+"
	default:
		return m.Shape
	}
}

func (m *Mod) ShapeName() string {
	switch m.Shape {
	case "Transmitter":
		return "Square"
	case "Processor":
		return "Diamond"
	case "Holo-Array":
		return "Triangle"
	case "Data-Bus":
		return "Circle"
	case "Receiver":
		return "Arrow"
	case "Multiplexer":
		return "Cross"
	default:
		return m.Shape
	}
}

type ModStat struct {
	Stat      string
	Value     float64
	IsPercent bool
}

func (ms ModStat) String() string {
	if ms.IsPercent {
		return fmt.Sprintf("%.02f%% %s", ms.Value, ms.StatShortName())
	}
	return fmt.Sprintf("%.02f %s", ms.Value, ms.StatShortName())
}

func (ms ModStat) StatShortName() string {
	return statAbbrev(ms.Stat)
}

func statAbbrev(stat string) string {
	switch stat {
	case "Critical Chance":
		return "Crit Chan"
	case "Critical Damage":
		return "Crit Dam"
	case "Critical Avoidance":
		return "Crit Avoi"
	case "Protection":
		return "Prot"
	default:
		return stat
	}
}

type ModFilter struct {
	Char string
}

func (f *ModFilter) Match(mod *Mod) bool {
	if f.Char == "" {
		return true
	}
	return f.Char == mod.UsingIn
}

type ModCollection []*Mod

func (c *Client) Mods(filter ModFilter) (mods ModCollection, err error) {
	page := 1
	for {
		url := fmt.Sprintf("https://swgoh.gg/u/%s/mods/?page=%d", c.profile, page)
		resp, err := c.hc.Get(url)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		doc, err := goquery.NewDocumentFromReader(resp.Body)
		if err != nil {
			return nil, err
		}
		count := 0
		doc.Find(".collection-mod").Each(func(i int, s *goquery.Selection) {
			mod := parseMod(s)
			if filter.Match(mod) {
				mods = append(mods, mod)
			}
			count++
		})
		if count < 60 {
			break
		}
		page++
	}
	return mods, nil
}

func parseMod(s *goquery.Selection) *Mod {
	var err error
	mod := &Mod{}
	mod.ID = s.AttrOr("data-id", "")
	mod.Level, err = strconv.Atoi(s.Find(".statmod-level").Text())
	if err != nil {
		log.Println("Error: %v", err)
	}
	mod.Rarity = s.Find(".statmod-pip").Length()
	shortname := strings.Fields(s.Find(".statmod-img").AttrOr("alt", "!Unkown!"))
	switch len(shortname) {
	case 4:
		mod.BonusSet = shortname[2]
		mod.Shape = shortname[3]
	case 5:
		mod.BonusSet = shortname[2] + " " + shortname[3]
		mod.Shape = shortname[4]
	default:
		mod.BonusSet = "?"
		mod.Shape = "?"
	}

	// Primary stat
	mod.PrimStat = parseStat(s.Find(".statmod-stats-1 .statmod-stat"))
	// Secondary stats
	s.Find(".statmod-stats-2 .statmod-stat").Each(func(i int, stat *goquery.Selection) {
		mod.SecStat = append(mod.SecStat, parseStat(stat))
	})

	mod.UsingIn = s.Find("img.char-portrait-img").AttrOr("alt", "")
	return mod
}

func parseStat(s *goquery.Selection) (stat ModStat) {
	stat.Stat = s.Find(".statmod-stat-label").Text()

	strvalue := s.Find(".statmod-stat-value").Text()
	strvalue = strings.Replace(strvalue, "%", "", -1)
	strvalue = strings.Replace(strvalue, "+", "", -1)

	var err error
	stat.Value, err = strconv.ParseFloat(strvalue, 64)
	if err != nil {
		log.Printf("parsestat: invalid value %s", s.Find(".statmod-stat-value").Text())
	}
	stat.IsPercent = strings.Contains(s.Find(".statmod-stat-value").Text(), "%")
	return stat
}
<commit_msg>Improved mod display and added some helper methods.<commit_after>package swgohgg

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"log"
	"strconv"
	"strings"
)

type Mod struct {
	ID       string
	Level    int
	Rarity   int
	Shape    string
	BonusSet string

	PrimStat ModStat
	SecStat  []ModStat

	UsingIn string
}

func (m *Mod) String() string {
	return m.Format(false)
}

func (m *Mod) Format(useEmoji bool) string {
	if m == nil {
		return "nil mod"
	}
	icon := m.ShapeIcon()
	if useEmoji {
		icon = m.ShapeEmoji()
	}
	str := fmt.Sprintf("%s %-9s L%-2d %d* %v %v", icon, m.BonusShortName(), m.Level, m.Rarity, m.PrimStat, m.SecStat)
	if m.UsingIn != "" {
		str += " (" + m.UsingIn + ")"
	}
	return str
}

func (m *Mod) BonusShortName() string {
	return statAbbrev(m.BonusSet)
}

func (m *Mod) ShapeEmoji() string {
	switch m.Shape {
	case "Transmitter":
		return "◼"
	case "Processor":
		return "♦"
	case "Holo-Array":
		return "⚠"
	case "Data-Bus":
		return "⚫"
	case "Receiver":
		return "↗"
	case "Multiplexer":
		return "➕"
	default:
		return m.Shape
	}
}

func (m *Mod) ShapeIcon() string {
	switch m.Shape {
	case "Transmitter":
		return "◻"
	case "Processor":
		return "◇"
	case "Holo-Array":
		return "△"
	case "Data-Bus":
		return "○"
	case "Receiver":
		return "◹"
	case "Multiplexer":
		return "+"
	default:
		return m.Shape
	}
}

func (m *Mod) ShapeName() string {
	switch m.Shape {
	case "Transmitter":
		return "Square"
	case "Processor":
		return "Diamond"
	case "Holo-Array":
		return "Triangle"
	case "Data-Bus":
		return "Circle"
	case "Receiver":
		return "Arrow"
	case "Multiplexer":
		return "Cross"
	default:
		return m.Shape
	}
}

func (m *Mod) HasStat(stat string) bool {
	if m.PrimStat.Stat == stat || m.PrimStat.StatShortName() == stat {
		return true
	}
	for _, sec := range m.SecStat {
		if sec.Stat == stat || sec.StatShortName() == stat {
			return true
		}
	}
	return false
}

type ModStat struct {
	Stat      string
	Value     float64
	IsPercent bool
}

func (ms ModStat) String() string {
	if ms.IsPercent {
		return fmt.Sprintf("%.02f%% %s", ms.Value, ms.StatShortName())
	}
	return fmt.Sprintf("%.02f %s", ms.Value, ms.StatShortName())
}

func (ms ModStat) StatShortName() string {
	return statAbbrev(ms.Stat)
}

func statAbbrev(stat string) string {
	switch stat {
	case "Critical Chance":
		return "Crit Chan"
	case "Critical Damage":
		return "Crit Dam"
	case "Critical Avoidance":
		return "Crit Avoi"
	case "Protection":
		return "Prot"
	default:
		return stat
	}
}

type ModFilter struct {
	Char string
}

func (f *ModFilter) Match(mod *Mod) bool {
	if f.Char == "" {
		return true
	}
	return f.Char == mod.UsingIn
}

type ModCollection []*Mod

func (c *Client) Mods(filter ModFilter) (mods ModCollection, err error) {
	page := 1
	for {
		url := fmt.Sprintf("https://swgoh.gg/u/%s/mods/?page=%d", c.profile, page)
		resp, err := c.hc.Get(url)
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		doc, err := goquery.NewDocumentFromReader(resp.Body)
		if err != nil {
			return nil, err
		}
		count := 0
		doc.Find(".collection-mod").Each(func(i int, s *goquery.Selection) {
			mod := parseMod(s)
			if filter.Match(mod) {
				mods = append(mods, mod)
			}
			count++
		})
		if count < 60 {
			break
		}
		page++
	}
	return mods, nil
}

func parseMod(s *goquery.Selection) *Mod {
	var err error
	mod := &Mod{}
	mod.ID = s.AttrOr("data-id", "")
	mod.Level, err = strconv.Atoi(s.Find(".statmod-level").Text())
	if err != nil {
		log.Println("Error: %v", err)
	}
	mod.Rarity = s.Find(".statmod-pip").Length()
	shortname := strings.Fields(s.Find(".statmod-img").AttrOr("alt", "!Unkown!"))
	switch len(shortname) {
	case 4:
		mod.BonusSet = shortname[2]
		mod.Shape = shortname[3]
	case 5:
		mod.BonusSet = shortname[2] + " " + shortname[3]
		mod.Shape = shortname[4]
	default:
		mod.BonusSet = "?"
		mod.Shape = "?"
	}

	// Primary stat
	mod.PrimStat = parseStat(s.Find(".statmod-stats-1 .statmod-stat"))
	// Secondary stats
	s.Find(".statmod-stats-2 .statmod-stat").Each(func(i int, stat *goquery.Selection) {
		mod.SecStat = append(mod.SecStat, parseStat(stat))
	})

	mod.UsingIn = s.Find("img.char-portrait-img").AttrOr("alt", "")
	return mod
}

func parseStat(s *goquery.Selection) (stat ModStat) {
	stat.Stat = s.Find(".statmod-stat-label").Text()

	strvalue := s.Find(".statmod-stat-value").Text()
	strvalue = strings.Replace(strvalue, "%", "", -1)
	strvalue = strings.Replace(strvalue, "+", "", -1)

	var err error
	stat.Value, err = strconv.ParseFloat(strvalue, 64)
	if err != nil {
		log.Printf("parsestat: invalid value %s", s.Find(".statmod-stat-value").Text())
	}
	stat.IsPercent = strings.Contains(s.Find(".statmod-stat-value").Text(), "%")
	return stat
}
<|endoftext|>


<commit_before>package microsoft

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/poorny/utils/cache"
)

const (
	datamarket        = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
	scope             = "http://api.microsofttranslator.com"
	translateURL      = "http://api.microsofttranslator.com/v2/Http.svc/Translate"
	translateArrayURL = "http://api.microsofttranslator.com/V2/Http.svc/TranslateArray"
	detectArrayURL    = "http://api.microsofttranslator.com/V2/Http.svc/DetectArray"
	grantType         = "client_credentials"
	xmlArrayTemplate  = `<TranslateArrayRequest>
						<AppId />
						<From>%s</From>
						<Options>
							<Category xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" ></Category>
							<ContentType xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2">text/plain</ContentType>
							<ReservedFlags xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" />
							<State xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></State>
							<Uri xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></Uri>
							<User xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></User>
						</Options>
						<Texts>
							%s
						</Texts>
						<To>%s</To>
					</TranslateArrayRequest>`
	templateToTranslate    = `<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">%s</string>`
	xmlDetectArrayTemplate = `<ArrayOfstring xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">%s</ArrayOfstring>`
)

type Access interface {
	GetAccessToken() TokenResponse
}

type Translator interface {
	Translate() (string, error)
	TranslateArray() ([]string, error)
	DetectTextArray() ([]string, error)
	CheckTimeout() bool
}

type AuthRequest struct {
	ClientID     string
	ClientSecret string
}

type TextTranslate struct {
	Text  string
	Texts []string
	From  string
	To    string
	Cache bool

	TokenResponse
}

type TokenResponse struct {
	AccessToken string    `json:"access_token"`
	TokenType   string    `json:"token_type,omitempty"`
	ExpiresIn   string    `json:"expires_in"`
	Scope       string    `json:"scope"`
	Timeout     time.Time `json:"timeout"`
}

type TranslateResponse struct {
	Resp []ArrayResp `xml:"TranslateArrayResponse"`
}

type ArrayResp struct {
	Text string `xml:"TranslatedText"`
}

func GetAccessToken(access Access) TokenResponse {
	return access.GetAccessToken()
}

func TranslateText(t Translator) (string, error) {
	if t.CheckTimeout() == false {
		return t.Translate()
	}
	return "", errors.New("Access token is invalid, please get new token")
}

func TranslateTexts(t Translator) ([]string, error) {
	if t.CheckTimeout() == false {
		return t.TranslateArray()
	}
	return []string{}, errors.New("Access token is invalid, please get new token")
}

func DetectText(t Translator) ([]string, error) {
	if t.CheckTimeout() == false {
		return t.DetectTextArray()
	}
	return []string{}, errors.New("Access token is invalid, please get new token")
}

func rdbCache() *redis.Redis {
	conn := redis.Connection{"tcp", ":6379", "7"}
	rdb, err := conn.Dial()
	if err != nil {
		log.Println("Fail to connect: ", err)
	}
	return rdb
}

// Make a POST request to `datamark` url for getting access token
func (a *AuthRequest) GetAccessToken() TokenResponse {
	client := &http.Client{}

	postValues := url.Values{}
	postValues.Add("client_id", a.ClientID)
	postValues.Add("client_secret", a.ClientSecret)
	postValues.Add("scope", scope)
	postValues.Add("grant_type", grantType)

	req, err := http.NewRequest("POST", datamarket, strings.NewReader(postValues.Encode()))
	if err != nil {
		log.Println(err)
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var tr TokenResponse
	err = json.Unmarshal(body, &tr)
	if err != nil {
		log.Println(err)
	}

	now := time.Now()
	expiresIn, err := strconv.ParseInt(tr.ExpiresIn, 10, 0)
	if err != nil {
		log.Println(err)
	}

	expTime := now.Add(time.Duration(expiresIn) * time.Second)
	// 10 min
	tr.Timeout = expTime
	return tr
}

// Return `t.Text` in `t.From` language translated for `t.To` language
func (t *TextTranslate) Translate() (string, error) {

	if t.Cache == true {
		rdb := rdbCache()
		exists, _ := rdb.HExists(t.Text, t.To)
		if exists == true {
			result, _ := rdb.HGet(t.Text, t.To)
			log.Printf("Getting from cache %s:%s", t.Text, result)
			rdb.Conn.Close()
			return result, nil
		}
	}

	textEncode := url.Values{}
	textEncode.Add("text", t.Text)
	text := textEncode.Encode()

	url := fmt.Sprintf("%s?%s&from=%s&to=%s", translateURL, text, t.From, t.To)

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println(err)
	}
	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	type Text struct {
		T string `xml:",chardata"`
	}

	var obj Text
	err = xml.Unmarshal(body, &obj)
	if t.Cache == true {
		rdb := rdbCache()
		rdb.HSet(t.Text, t.To, obj.T)
		log.Printf("Add to cache %s:[%s] -> %s", t.Text, t.To, obj.T)
		rdb.Conn.Close()
	}

	return obj.T, nil
}

// Return `t.Texts` array in `t.From` language translated for `t.To` language
func (t *TextTranslate) TranslateArray() ([]string, error) {
	toTranslate := make([]string, len(t.Texts))
	response := []string{}

	// Simulate possible indexes of array response from microsoft.
	notCached := make(map[string]int)
	count := 0

	if t.Cache == true {
		rdb := rdbCache()
		for _, tx := range t.Texts {
			exs, _ := rdb.HExists(tx, t.To)

			if exs == false {
				notCached[tx] = count
				ts := fmt.Sprintf(templateToTranslate, tx)
				toTranslate = append(toTranslate, ts)
				count++
			}
		}
		rdb.Conn.Close()
	} else {
		for _, text := range t.Texts {
			tx := fmt.Sprintf(templateToTranslate, text)
			toTranslate = append(toTranslate, tx)
		}
	}

	// If do not need to do translation
	if len(notCached) == 0 {
		rdb := rdbCache()
		for _, tx := range t.Texts {
			res, _ := rdb.HGet(tx, t.To)
			if res != "" {
				response = append(response, res)
			}
		}
		rdb.Conn.Close()
		return response, nil
	}

	textToTranslate := strings.Join(toTranslate, "\n")
	bodyReq := fmt.Sprintf(xmlArrayTemplate, t.From, textToTranslate, t.To)

	client := &http.Client{}
	req, err := http.NewRequest("POST", translateArrayURL, strings.NewReader(bodyReq))
	if err != nil {
		log.Println(err)
	}

	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)
	req.Header.Add("Content-Type", "text/xml")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var obj TranslateResponse
	err = xml.Unmarshal(body, &obj)
	if err != nil {
		log.Println(err)
	}

	if t.Cache == true && len(obj.Resp) > 0 {
		rdb := rdbCache()
		for key, indx := range notCached {
			tx := obj.Resp[indx].Text
			log.Printf("Add to cache %s: [%s] %s", key, t.To, tx)

			err = rdb.HSet(key, t.To, tx)
			if err != nil {
				log.Println(err)
			}
		}

		for _, tx := range t.Texts {
			res, _ := rdb.HGet(tx, t.To)
			if res != "" {
				response = append(response, res)
			}
		}
		rdb.Conn.Close()
	}
	return response, nil
}

// Detects the language of the text passed in the array `t.Texts`
func (t *TextTranslate) DetectTextArray() ([]string, error) {
	response := []string{}
	toTranslate := make([]string, len(t.Texts))

	for _, text := range t.Texts {
		textEncode := url.Values{}
		textEncode.Add("text", text)
		text := textEncode.Encode()

		tx := fmt.Sprintf(templateToTranslate, text)
		toTranslate = append(toTranslate, tx)
	}
	textToTranslate := strings.Join(toTranslate, "\n")
	bodyReq := fmt.Sprintf(xmlDetectArrayTemplate, textToTranslate)

	client := &http.Client{}
	req, err := http.NewRequest("POST", detectArrayURL, strings.NewReader(bodyReq))
	if err != nil {
		log.Println(err)
	}

	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)
	req.Header.Add("Content-Type", "text/xml")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	type LangArray struct {
		Array []string `xml:"string"`
	}

	var obj LangArray
	err = xml.Unmarshal(body, &obj)
	if err != nil {
		log.Println(err)
	}

	response = append(response, obj.Array...)
	return response, nil
}

// Verify if access token is valid
func (t *TokenResponse) CheckTimeout() bool {
	if time.Since(t.Timeout) > 0 {
		return true
	}
	return false
}
<commit_msg>add log<commit_after>package microsoft

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/poorny/utils/cache"
)

const (
	datamarket        = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"
	scope             = "http://api.microsofttranslator.com"
	translateURL      = "http://api.microsofttranslator.com/v2/Http.svc/Translate"
	translateArrayURL = "http://api.microsofttranslator.com/V2/Http.svc/TranslateArray"
	detectArrayURL    = "http://api.microsofttranslator.com/V2/Http.svc/DetectArray"
	grantType         = "client_credentials"
	xmlArrayTemplate  = `<TranslateArrayRequest>
						<AppId />
						<From>%s</From>
						<Options>
							<Category xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" ></Category>
							<ContentType xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2">text/plain</ContentType>
							<ReservedFlags xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2" />
							<State xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></State>
							<Uri xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></Uri>
							<User xmlns="http://schemas.datacontract.org/2004/07/Microsoft.MT.Web.Service.V2"></User>
						</Options>
						<Texts>
							%s
						</Texts>
						<To>%s</To>
					</TranslateArrayRequest>`
	templateToTranslate    = `<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">%s</string>`
	xmlDetectArrayTemplate = `<ArrayOfstring xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">%s</ArrayOfstring>`
)

type Access interface {
	GetAccessToken() TokenResponse
}

type Translator interface {
	Translate() (string, error)
	TranslateArray() ([]string, error)
	DetectTextArray() ([]string, error)
	CheckTimeout() bool
}

type AuthRequest struct {
	ClientID     string
	ClientSecret string
}

type TextTranslate struct {
	Text  string
	Texts []string
	From  string
	To    string
	Cache bool

	TokenResponse
}

type TokenResponse struct {
	AccessToken string    `json:"access_token"`
	TokenType   string    `json:"token_type,omitempty"`
	ExpiresIn   string    `json:"expires_in"`
	Scope       string    `json:"scope"`
	Timeout     time.Time `json:"timeout"`
}

type TranslateResponse struct {
	Resp []ArrayResp `xml:"TranslateArrayResponse"`
}

type ArrayResp struct {
	Text string `xml:"TranslatedText"`
}

func GetAccessToken(access Access) TokenResponse {
	return access.GetAccessToken()
}

func TranslateText(t Translator) (string, error) {
	if t.CheckTimeout() == false {
		return t.Translate()
	}
	return "", errors.New("Access token is invalid, please get new token")
}

func TranslateTexts(t Translator) ([]string, error) {
	if t.CheckTimeout() == false {
		return t.TranslateArray()
	}
	return []string{}, errors.New("Access token is invalid, please get new token")
}

func DetectText(t Translator) ([]string, error) {
	if t.CheckTimeout() == false {
		return t.DetectTextArray()
	}
	return []string{}, errors.New("Access token is invalid, please get new token")
}

func rdbCache() *redis.Redis {
	conn := redis.Connection{"tcp", ":6379", "7"}
	rdb, err := conn.Dial()
	if err != nil {
		log.Println("Fail to connect: ", err)
	}
	return rdb
}

// Make a POST request to `datamark` url for getting access token
func (a *AuthRequest) GetAccessToken() TokenResponse {
	client := &http.Client{}

	postValues := url.Values{}
	postValues.Add("client_id", a.ClientID)
	postValues.Add("client_secret", a.ClientSecret)
	postValues.Add("scope", scope)
	postValues.Add("grant_type", grantType)

	req, err := http.NewRequest("POST", datamarket, strings.NewReader(postValues.Encode()))
	if err != nil {
		log.Println(err)
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var tr TokenResponse
	err = json.Unmarshal(body, &tr)
	if err != nil {
		log.Println(err)
	}

	now := time.Now()
	expiresIn, err := strconv.ParseInt(tr.ExpiresIn, 10, 0)
	if err != nil {
		log.Println(err)
	}

	expTime := now.Add(time.Duration(expiresIn) * time.Second)
	// 10 min
	tr.Timeout = expTime
	return tr
}

// Return `t.Text` in `t.From` language translated for `t.To` language
func (t *TextTranslate) Translate() (string, error) {

	if t.Cache == true {
		rdb := rdbCache()
		exists, _ := rdb.HExists(t.Text, t.To)
		if exists == true {
			result, _ := rdb.HGet(t.Text, t.To)
			log.Printf("Getting from cache %s:%s", t.Text, result)
			rdb.Conn.Close()
			return result, nil
		}
	}

	textEncode := url.Values{}
	textEncode.Add("text", t.Text)
	text := textEncode.Encode()

	url := fmt.Sprintf("%s?%s&from=%s&to=%s", translateURL, text, t.From, t.To)

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println(err)
	}
	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	type Text struct {
		T string `xml:",chardata"`
	}

	var obj Text
	err = xml.Unmarshal(body, &obj)
	if t.Cache == true {
		rdb := rdbCache()
		rdb.HSet(t.Text, t.To, obj.T)
		log.Printf("Add to cache %s:[%s] -> %s", t.Text, t.To, obj.T)
		rdb.Conn.Close()
	}

	return obj.T, nil
}

// Return `t.Texts` array in `t.From` language translated for `t.To` language
func (t *TextTranslate) TranslateArray() ([]string, error) {
	toTranslate := make([]string, len(t.Texts))
	response := []string{}

	// Simulate possible indexes of array response from microsoft.
	notCached := make(map[string]int)
	count := 0

	if t.Cache == true {
		rdb := rdbCache()
		for _, tx := range t.Texts {
			exs, _ := rdb.HExists(tx, t.To)

			if exs == false {
				notCached[tx] = count
				ts := fmt.Sprintf(templateToTranslate, tx)
				toTranslate = append(toTranslate, ts)
				count++
			}
		}
		rdb.Conn.Close()
	} else {
		for _, text := range t.Texts {
			tx := fmt.Sprintf(templateToTranslate, text)
			toTranslate = append(toTranslate, tx)
		}
	}

	// If do not need to do translation
	if len(notCached) == 0 {
		rdb := rdbCache()
		for _, tx := range t.Texts {
			res, _ := rdb.HGet(tx, t.To)
			log.Printf("Get from cache %s: [%s] %s", tx, t.To, res)
			if res != "" {
				response = append(response, res)
			}
		}
		rdb.Conn.Close()
		return response, nil
	}

	textToTranslate := strings.Join(toTranslate, "\n")
	bodyReq := fmt.Sprintf(xmlArrayTemplate, t.From, textToTranslate, t.To)

	client := &http.Client{}
	req, err := http.NewRequest("POST", translateArrayURL, strings.NewReader(bodyReq))
	if err != nil {
		log.Println(err)
	}

	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)
	req.Header.Add("Content-Type", "text/xml")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var obj TranslateResponse
	err = xml.Unmarshal(body, &obj)
	if err != nil {
		log.Println(err)
	}

	if t.Cache == true && len(obj.Resp) > 0 {
		rdb := rdbCache()
		for key, indx := range notCached {
			tx := obj.Resp[indx].Text
			log.Printf("Add to cache %s: [%s] %s", key, t.To, tx)

			err = rdb.HSet(key, t.To, tx)
			if err != nil {
				log.Println(err)
			}
		}

		for _, tx := range t.Texts {
			res, _ := rdb.HGet(tx, t.To)
			log.Printf("Get from cache %s: [%s] %s", tx, t.To, res)
			if res != "" {
				response = append(response, res)
			}
		}
		rdb.Conn.Close()
	}
	return response, nil
}

// Detects the language of the text passed in the array `t.Texts`
func (t *TextTranslate) DetectTextArray() ([]string, error) {
	response := []string{}
	toTranslate := make([]string, len(t.Texts))

	for _, text := range t.Texts {
		textEncode := url.Values{}
		textEncode.Add("text", text)
		text := textEncode.Encode()

		tx := fmt.Sprintf(templateToTranslate, text)
		toTranslate = append(toTranslate, tx)
	}
	textToTranslate := strings.Join(toTranslate, "\n")
	bodyReq := fmt.Sprintf(xmlDetectArrayTemplate, textToTranslate)

	client := &http.Client{}
	req, err := http.NewRequest("POST", detectArrayURL, strings.NewReader(bodyReq))
	if err != nil {
		log.Println(err)
	}

	authToken := fmt.Sprintf("Bearer %s", t.TokenResponse.AccessToken)
	req.Header.Add("Authorization", authToken)
	req.Header.Add("Content-Type", "text/xml")

	resp, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}
	type LangArray struct {
		Array []string `xml:"string"`
	}

	var obj LangArray
	err = xml.Unmarshal(body, &obj)
	if err != nil {
		log.Println(err)
	}

	response = append(response, obj.Array...)
	return response, nil
}

// Verify if access token is valid
func (t *TokenResponse) CheckTimeout() bool {
	if time.Since(t.Timeout) > 0 {
		return true
	}
	return false
}
<|endoftext|>