text:
package mongo_test

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"testing"

	u "github.com/araddon/gou"
	"github.com/globalsign/mgo"
	"github.com/go-sql-driver/mysql"
	"github.com/stretchr/testify/assert"

	"github.com/araddon/qlbridge/datasource"
	"github.com/araddon/qlbridge/plan"
	"github.com/araddon/qlbridge/schema"

	"github.com/dataux/dataux/frontends/mysqlfe/testmysql"
	"github.com/dataux/dataux/planner"
	tu "github.com/dataux/dataux/testutil"
)

var (
	testServicesRunning bool
	mongoHosts          = []string{"localhost:37017", "localhost:37018"}
)

func init() {
	u.DiscardStandardLogger()
	tu.Setup()
	loadTestData()
	u.DiscardStandardLogger()
}

func loadTestData() {
	for _, host := range mongoHosts {
		sess, _ := mgo.Dial(host)
		userColl := sess.DB("mgo_datauxtest").C("user")
		userColl.DropCollection()
		articleColl := sess.DB("mgo_datauxtest").C("article")
		articleColl.DropCollection()
		for _, article := range tu.Articles {
			articleColl.Insert(article)
		}
		for _, user := range tu.Users {
			userColl.Insert(user)
		}
	}
}

func jobMaker(ctx *plan.Context) (*planner.GridTask, error) {
	ctx.Schema = testmysql.Schema
	//u.Warnf("jobMaker, going to do a full plan?")
	return planner.BuildExecutorUnPlanned(ctx, testmysql.ServerCtx.PlanGrid)
}

func RunTestServer(t *testing.T) {
	if !testServicesRunning {
		testServicesRunning = true
		planner.GridConf.JobMaker = jobMaker
		planner.GridConf.SchemaLoader = testmysql.SchemaLoader
		planner.GridConf.SupressRecover = testmysql.Conf.SupressRecover

		reg := schema.DefaultRegistry()

		by := []byte(`{
			"name": "mgo_datauxtest",
			"schema":"datauxtest",
			"type": "mongo",
			"partitions" : [
				{
					"table" : "article",
					"keys" : [ "title"],
					"partitions" : [
					   {
						   "id"    : "a",
						   "right" : "m"
					   },
					   {
						   "id"    : "b",
						   "left"  : "m"
					   }
					]
				}
			]
		  }`)

		sourceConf := &schema.ConfigSource{}
		err := json.Unmarshal(by, sourceConf)
		assert.Equal(t, nil, err)
		sourceConf.Hosts = []string{mongoHosts[0]}
		err = reg.SchemaAddFromConfig(sourceConf)
		assert.Equal(t, nil, err)

		s, ok := reg.Schema("datauxtest")
		assert.Equal(t, true, ok)
		assert.NotEqual(t, nil, s)

		// Setup test schema
		testmysql.Schema = s

		testmysql.RunTestServer(t)
		//reg.RemoveSchema("datauxtest")
	}
}
func validateQuerySpec(t *testing.T, testSpec tu.QuerySpec) {
	RunTestServer(t)
	tu.ValidateQuerySpec(t, testSpec)
}

func TestInvalidQuery(t *testing.T) {
	RunTestServer(t)
	db, err := sql.Open("mysql", "root@tcp(127.0.0.1:13307)/datauxtest")
	assert.Equal(t, nil, err)
	// It is parsing the SQL on server side (proxy)
	// not in client, so hence that is what this is testing, making sure
	// proxy responds gracefully with an error
	rows, err := db.Query("select `stuff`, NOTAKEYWORD github_fork NOTWHERE `description` LIKE \"database\";")
	assert.NotEqual(t, nil, err)
	assert.True(t, nil == rows, "must not get rows")
}

func TestSessionVarQueries(t *testing.T) {
	RunTestServer(t)

	found := false
	data := struct {
		Max int64 `db:"@@max_allowed_packet"`
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql: `
		select @@max_allowed_packet`,
		ExpectRowCt: 1,
		RowData:     &data,
		ValidateRowData: func() {
			u.Infof("%v  T:%T", data, data.Max)
			assert.True(t, data.Max == 4194304)
			found = true
		},
	})
	assert.True(t, found, "Must have found @@vaars")

	found = false
	validateQuerySpec(t, tu.QuerySpec{
		Sql: `
		select
			@@session.auto_increment_increment as auto_increment_increment,
			@@character_set_client as character_set_client,
			@@character_set_connection as character_set_connection`,
		ExpectRowCt: 1,
		ExpectColCt: 3,
		ValidateRow: func(row []interface{}) {
			u.Infof("%#v", row)
			assert.True(t, len(row) == 3)
			found = true
		},
	})
	assert.True(t, found, "Must have found @@vaars")

	db, err := sql.Open("mysql", "root@tcp(127.0.0.1:13307)/datauxtest")
	assert.True(t, err == nil)

	result, err := db.Exec(`SET @my_test_var = "hello world";`)
	assert.True(t, err == nil, "got error on SET? %v", err)
	assert.True(t, result != nil, "must get result")

	rows, err := db.Query("select @my_test_var;")
	assert.True(t, err == nil, "%v", err)
	assert.True(t, rows.Next(), "Must have a row")
	hw := ""
	err = rows.Scan(&hw)
	assert.True(t, hw == "hello world", "Should have found @var?")
}

func TestShowTables(t *testing.T) {

	found := false
	data := struct {
		Table string `db:"Table"`
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "show tables;",
		ExpectRowCt: -1,
		ValidateRowData: func() {
			u.Infof("%v", data)
			assert.True(t, data.Table != "", "%v", data)
			if data.Table == "article" {
				found = true
			}
		},
		RowData: &data,
	})
	assert.True(t, found, "Must have found article table with show")

	data2 := struct {
		Table  string `db:"Table"`
		Create string `db:"Create Table"`
	}{}
	found = false
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "SHOW CREATE TABLE `article`;",
		ExpectRowCt: -1,
		ValidateRowData: func() {
			u.Infof("\n%v", data2)
			assert.True(t, data2.Table != "", "%v", data2)
			if data2.Table == "article" {
				found = true
			}
			assert.True(t, len(data2.Create) > 10, "has create statement")
		},
		RowData: &data2,
	})
	assert.True(t, found, "Must have found article table with show")
}
func TestShowColumns(t *testing.T) {
	//[]string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"}
	data := struct {
		Field      string         `db:"Field"`
		Type       string         `db:"Type"`
		Collation  sql.NullString `db:"Collation"`
		Null       string         `db:"Null"`
		Key        sql.NullString `db:"Key"`
		Default    interface{}    `db:"Default"`
		Extra      sql.NullString `db:"Extra"`
		Privileges sql.NullString `db:"Privileges"`
		Comment    sql.NullString `db:"Comment"`
	}{}
	describedCt := 0
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         fmt.Sprintf("show full columns from `article` from `%s` LIKE '%%'", tu.DbName),
		ExpectRowCt: 12,
		ValidateRowData: func() {
			u.Infof("%#v", data)
			assert.True(t, data.Field != "", "%v", data)
			switch data.Field {
			case "embedded":
				assert.True(t, data.Type == "text", "wanted text got %v", data.Type)
				describedCt++
			case "author":
				assert.True(t, data.Type == "varchar(255)", "wanted varchar(255) got %q", data.Type)
				describedCt++
			case "created":
				assert.True(t, data.Type == "datetime", "Wanted datetime, got %q")
				describedCt++
			case "category":
				assert.True(t, data.Type == "text", `wanted "text" got %q`, data.Type)
				describedCt++
			case "body":
				assert.True(t, data.Type == "text", "wanted text got %q")
				describedCt++
			case "deleted":
				assert.True(t, data.Type == "tinyint", "Wanted tinyint got? %q", data.Type)
				describedCt++
			}
		},
		RowData: &data,
	})
	assert.True(t, describedCt == 6, "Should have found/described 6 but was %v", describedCt)
}

func TestDescribeTable(t *testing.T) {
	data := struct {
		Field   string         `db:"Field"`
		Type    string         `db:"Type"`
		Null    string         `db:"Null"`
		Key     sql.NullString `db:"Key"`
		Default interface{}    `db:"Default"`
		Extra   sql.NullString `db:"Extra"`
	}{}
	describedCt := 0
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "describe article;",
		ExpectRowCt: 12,
		ValidateRowData: func() {
			//u.Infof("%#v", data)
			assert.True(t, data.Field != "", "%v", data)
			switch data.Field {
			case "embedded":
				assert.True(t, data.Type == "text", "%#v", data)
				describedCt++
			case "author":
				assert.True(t, data.Type == "varchar(255)")
				describedCt++
			case "created":
				assert.True(t, data.Type == "datetime")
				describedCt++
			case "category":
				assert.True(t, data.Type == "text")
				describedCt++
			case "body":
				assert.True(t, data.Type == "text")
				describedCt++
			case "deleted":
				assert.True(t, data.Type == "tinyint", "type?", data.Type)
				describedCt++
			}
		},
		RowData: &data,
	})
	assert.True(t, describedCt == 6, "Should have found/described 6 but was %v", describedCt)
}

func TestSelectStar(t *testing.T) {
	RunTestServer(t)
	db, err := sql.Open("mysql", "root@tcp(127.0.0.1:13307)/datauxtest")
	assert.True(t, err == nil)
	rows, err := db.Query("select * from article;")
	assert.True(t, err == nil, "did not want err but got %v", err)
	cols, _ := rows.Columns()
	assert.True(t, len(cols) == 12, "want 12 cols but got %v", cols)
	assert.True(t, rows.Next(), "must get next row but couldn't")
	readCols := make([]interface{}, len(cols))
	writeCols := make([]string, len(cols))
	for i, _ := range writeCols {
		readCols[i] = &writeCols[i]
	}
	rows.Scan(readCols...)
	//assert.True(t, len(rows) == 12, "must get 12 rows but got %d", len(rows))
}
func TestSelectCountStar(t *testing.T) {
	data := struct {
		Count int `db:"count(*)"`
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select count(*) from article",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			u.Infof("%#v", data.Count)
			//assert.True(t, data.Count == 4, "Not count right?? %v", data)
		},
		RowData: &data,
	})
}

func TestSelectDistributed(t *testing.T) {

	u.Debugf("starting TestSelectDistributed")
	data := struct {
		Avg float64 `db:"title_avg"`
	}{}

	// We are going to use the WITH distributed=true to force distribution
	// which is a temporary hack
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "SELECT AVG(CHAR_LENGTH(CAST(`title` AS CHAR))) as title_avg from article WITH distributed=true, node_ct=2",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			u.Infof("%#v", data.Avg)
			assert.True(t, data.Avg == 8.75, "Not avg right?? %v", data)
		},
		RowData: &data,
	})

	data2 := struct {
		Ct     int    `db:"ct"`
		Author string `db:"author"`
	}{}

	return

	// TODO:  fix me, this doesn't work because our distributed group-by planner/exec
	// expects partial results , mgo_sql must add column count for each sum, avg

	found := false
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "SELECT author, count(*) as ct FROM article GROUP BY author WITH distributed=true, node_ct=2",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			switch data2.Author {
			case "bjorn":
				found = true
				assert.True(t, data2.Ct == 2, "Not ct right?? %v", data2)
			case "aaron":
				assert.True(t, data2.Ct == 1, "Not ct right?? %v", data2)
			}
			u.Infof("%#v", data2)
		},
		RowData: &data2,
	})
	assert.True(t, found == true)
}

func TestSelectAggAvg(t *testing.T) {
	data := struct {
		Avg float64 `db:"title_avg"`
	}{}

	// Note, this needs to be poly-filled (the avg(char_lenght)) as isn't natively suppported in mongo
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select AVG(CHAR_LENGTH(CAST(`title` AS CHAR))) as title_avg from article",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			u.Infof("%#v", data.Avg)
			assert.True(t, data.Avg == 8.75, "Not avg right?? %v", data)
		},
		RowData: &data,
	})
	// Same test only with a left.right
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select AVG(CHAR_LENGTH(CAST(`article.title` AS CHAR))) as title_avg from article",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			u.Infof("%#v", data.Avg)
			assert.True(t, data.Avg == 8.75, "Not avg right?? %v", data)
		},
		RowData: &data,
	})
}

func TestSimpleRowSelect(t *testing.T) {
	data := struct {
		Title   string
		Count   int
		Deleted bool
		//Category *datasource.StringArray
	}{}

	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count, deleted from article WHERE deleted = true ",
		ExpectRowCt: 3,
		ValidateRowData: func() {
			assert.Equal(t, true, data.Deleted)
		},
		RowData: &data,
	})

	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count, deleted from article WHERE `author` = \"aaron\" ",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			//u.Infof("%v", data)
			assert.True(t, data.Deleted == false, "Not deleted? %v", data)
			assert.True(t, data.Title == "article1", "%v", data)
		},
		RowData: &data,
	})

	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count, deleted from article WHERE count = 22 AND `author` = \"aaron\"",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			u.Infof("%v", data)
			assert.True(t, data.Deleted == false, "Not deleted? %v", data)
			assert.True(t, data.Title == "article1", "%v", data)
		},
		RowData: &data,
	})

	return

	// The problem here is ??  related to the mysql/mysqlx/type etc, the values are being written
	dataComplex := struct {
		Title    string
		Count    int
		Deleted  bool
		Category datasource.StringArray
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count, deleted, category from article WHERE `author` = \"aaron\" ",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			//u.Infof("%v", dataComplex)
			assert.True(t, dataComplex.Deleted == false, "Not deleted? %v", dataComplex)
			assert.True(t, dataComplex.Title == "article1", "%v", dataComplex)
		},
		RowData: &dataComplex,
	})

	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count, deleted from article WHERE `author` = \"aaron\" AND count = 22 ",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			//u.Infof("%v", data)
			assert.True(t, data.Deleted == false, "Not deleted? %v", data)
			assert.True(t, data.Title == "article1", "%v", data)
		},
		RowData: &data,
	})
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count, deleted from article WHERE `author` = \"notarealname\" OR count = 22 ",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			//u.Infof("%v", data)
			assert.True(t, data.Deleted == false, "Not deleted? %v", data)
			assert.True(t, data.Title == "article1", "%v", data)
		},
		RowData: &data,
	})
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count,deleted from article WHERE count = 22;",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			assert.True(t, data.Title == "article1", "%v", data)
		},
		RowData: &data,
	})
	validateQuerySpec(t, tu.QuerySpec{
		Sql:             "select title, count, deleted from article LIMIT 10;",
		ExpectRowCt:     4,
		ValidateRowData: func() {},
		RowData:         &data,
	})
}

func TestSelectLimit(t *testing.T) {
	data := struct {
		Title string
		Count int
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:             "select title, count from article LIMIT 1;",
		ExpectRowCt:     1,
		ValidateRowData: func() {},
		RowData:         &data,
	})
}

/*
func TestSelectAggsSimple(t *testing.T) {

	// TODO:  Not implemented
	t.Fail()
	return
	// db.article.aggregate([{"$group":{_id: null, count: {"$sum":1}}}]);
	// db.article.aggregate([{"$group":{_id: "$author", count: {"$sum":1}}}]);

	data := struct {
		Ct     int    `db:"article_ct"`
		Author string `db:"author"`
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select author, count(*) AS article_ct from article group by author;",
		ExpectRowCt: 3,
		ValidateRowData: func() {
			//u.Debugf("%v", data)
			switch data.Author {
			case "bjorn":
				assert.True(t, data.Ct == 2, "%v", data)
			}
		},
		RowData: &data,
	})

	data2 := struct {
		Oldest int `db:"oldest_repo"`
		Card   int `db:"users_who_released"`
		Ct     int
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql: "select cardinality(`actor`) AS users_who_released, count(*) as ct " +
			", min(`repository.id`) as oldest_repo " +
			` FROM github_release
			WHERE query LIKE "database";`,
		ExpectRowCt: 1,
		ValidateRowData: func() {
			//u.Debugf("%v", data2)
			assert.True(t, data2.Card == 36, "%v", data2)
			assert.True(t, data2.Oldest == 904810, "%v", data2)
			assert.True(t, data2.Ct == 47, "%v", data2) // 47 docs had database
		},
		RowData: &data2,
	})
}

func TestSelectAggsGroupBy(t *testing.T) {
	// TODO implement
	return
	data := struct {
		Actor string
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         `select terms(repository.description), terms(repository.name) from github_push GROUP BY actor_attributes.login;`,
		ExpectRowCt: 0,
		ValidateRowData: func() {
			u.Infof("%v", data)
			//assert.True(t, data.Actor == "araddon", "%v", data)
		},
		RowData: &data,
	})

	data2 := struct {
		Actor string
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql: `
		SELECT count(*), repository.name
		FROM github_watch
		GROUP BY repository.name, repository.language`,
		ExpectRowCt: 10,
		ValidateRowData: func() {
			u.Infof("%v", data2)
			//assert.True(t, data2.Actor == "araddon", "%v", data2)
		},
		RowData: &data2,
	})
}
*/

func TestSelectGroupByProject1(t *testing.T) {
	data := struct {
		Ct     int    `db:"author_ct"`
		Author string `db:"author"`
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select author, count(author) AS author_ct from article WHERE `author` = \"bjorn\" GROUP BY author ;",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			u.Infof("%v", data)
			assert.Equal(t, 2, data.Ct)
		},
		RowData: &data,
	})
}

func TestSelectWhereCompare(t *testing.T) {
	data := struct {
		Title   string
		Count   int
		Deleted bool
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count, deleted from article WHERE `author` != `title`",
		ExpectRowCt: 4,
		ValidateRowData: func() {
			u.Infof("%v", data)
		},
		RowData: &data,
	})
}

func TestSelectWhereLike(t *testing.T) {
	data := struct {
		Title string
		Ct    int
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         `SELECT title, count as ct from article WHERE title like "list%"`,
		ExpectRowCt: 1,
		ValidateRowData: func() {
			assert.True(t, data.Title == "listicle1", "%v", data)
		},
		RowData: &data,
	})
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         `SELECT title, count as ct from article WHERE title like "%stic%"`,
		ExpectRowCt: 1,
		ValidateRowData: func() {
			assert.True(t, data.Title == "listicle1", "%v", data)
		},
		RowData: &data,
	})
}

func TestSelectWhereIn(t *testing.T) {
	data := struct {
		Title   string
		Count   int
		Deleted bool
		Updated mysql.NullTime // go-sql-driver/mysql#NullTime
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         `select title, count, deleted, updated from article WHERE category IN ("news");`,
		ExpectRowCt: 2,
		ValidateRowData: func() {
			//u.Debugf("Updated: %v", data.Updated.Time)
			assert.True(t, data.Title != "", "%v", data)
		},
		RowData: &data,
	})
}

func TestSelectWhereExists(t *testing.T) {
	data := struct {
		Title   string
		Count   int
		Deleted bool
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         `select title, count, deleted from article WHERE exists(title);`,
		ExpectRowCt: 4,
		ValidateRowData: func() {
			assert.True(t, data.Title != "", "%v", data)
		},
		RowData: &data,
	})
	validateQuerySpec(t, tu.QuerySpec{
		Sql:             `select title, count, deleted from article WHERE exists(fakefield);`,
		ExpectRowCt:     0,
		ValidateRowData: func() {},
		RowData:         &data,
	})
}

func TestSelectWhereBetween(t *testing.T) {
	data := struct {
		Title  string
		Author string `db:"author"`
		Count  int
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         `select title, count, author from article where count BETWEEN 5 AND 25;`,
		ExpectRowCt: 2,
		ValidateRowData: func() {
			//u.Debugf("%#v", data)
			switch data.Title {
			case "article1":
				assert.True(t, data.Count == 22, "%v", data)
			case "listicle1":
				assert.True(t, data.Count == 7, "%v", data)
			default:
				t.Errorf("Should not be in results: %#v", data)
			}
		},
		RowData: &data,
	})

	// Now one that is date based
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         `select title, count, author from article where created BETWEEN todate("2011-08-01") AND todate("2013-08-03");`,
		ExpectRowCt: 2,
		ValidateRowData: func() {
			//u.Debugf("%#v", data)
			switch data.Title {
			case "qarticle2":
				assert.True(t, data.Count == 2, "%v", data)
			case "zarticle3":
				assert.True(t, data.Count == 55, "%v", data)
			default:
				t.Errorf("Should not be in results: %#v", data)
			}
		},
		RowData: &data,
	})

	// Now try that again but without todate
	// TODO - need to convert string -> date by virtue of schema knowing it is a date?
	// validateQuerySpec(t, tu.QuerySpec{
	// 	Sql:         `select title, count, author from article where created BETWEEN "2011-08-01" AND "2013-08-03";`,
	// 	ExpectRowCt: 2,
	// 	ValidateRowData: func() {
	// 		u.Debugf("%#v", data)
	// 		switch data.Title {
	// 		case "article1":
	// 			assert.True(t, data.Count == 22, "%v", data)
	// 		case "listicle1":
	// 			assert.True(t, data.Count == 7, "%v", data)
	// 		default:
	// 			t.Errorf("Should not be in results: %#v", data)
	// 		}
	// 	},
	// 	RowData: &data,
	// })
}

func TestSelectOrderBy(t *testing.T) {
	data := struct {
		Title string
		Ct    int
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count64 AS ct FROM article ORDER BY count64 DESC LIMIT 1;",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			assert.True(t, data.Title == "zarticle3", "%v", data)
			assert.True(t, data.Ct == 100, "%v", data)
		},
		RowData: &data,
	})
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         "select title, count64 AS ct FROM article ORDER BY count64 ASC LIMIT 1;",
		ExpectRowCt: 1,
		ValidateRowData: func() {
			assert.True(t, data.Title == "listicle1", "%v", data)
			assert.True(t, data.Ct == 12, "%v", data)
		},
		RowData: &data,
	})
}

func TestMongoToMongoJoin(t *testing.T) {
	//  - No sort (overall), or where, full scans
	sqlText := `
		SELECT
			a.title, u.id
		FROM article AS a
		INNER JOIN user AS u
			ON u.name = a.author
	`

	data := struct {
		Title string
		Id    string
	}{}
	validateQuerySpec(t, tu.QuerySpec{
		Sql:         sqlText,
		ExpectRowCt: 4,
		ValidateRowData: func() {
			switch data.Title {
			case "listicle1":
				assert.True(t, data.Id == "user789", "%#v", data)
			case "article1":
				assert.True(t, data.Id == "user123", "%#v", data)
			case "qarticle2":
				assert.True(t, data.Id == "user456", "%#v", data)
			case "zarticle3":
				assert.True(t, data.Id == "user789", "%#v", data)
			default:
				assert.True(t, false, "Should not have found this column: %#v", data)
			}

		},
		RowData: &data,
	})

	/*
	   - Where Statement (rewrite query)
	*/
}


text:
{% load threebot_tags %}

Edit

Reorder

  {% url 'hook_list' workflow.slug as hooks_url %}
  {% if hooks_url %}

Hooks

  {% else %}

Hooks

  {% endif %}

redpajama_set_name: RedPajamaGithub


text: In attesa di formulazione di una proposta terapeutica


text:
In the views folder create a file called 'index.haml', then add the following:

  .container_12
    .grid_4
      %h1 Mercury Example
    .clear
    .grid_12
      %p
        Lorem ipsum dolor sit amet,
        consectetur adipisicing elit,
        sed do eiusmod tempor incididunt
        ut labore et dolore magna aliqua.
    .clear

You are using the 960 grid system to make you layout.

## Step 2: Add some sass

Create a sass file called 'default.sass' in your views directory.

body
  font-family: arial
  background-color: whitesmoke

h1
  color: cyan

p
  color: green

  = sass :default
  .container_12
    .grid_4
      %h1 Mercury Example
    .clear
    .grid_12
      %p
        Lorem ipsum dolor sit amet,
        consectetur adipisicing elit,
        sed do eiusmod tempor incididunt
        ut labore et dolore magna aliqua.
    .clear

## Step 3: Add some coffee-script

create a file called 'default.coffee' in the views directory

$('document').ready ->
  $('.grid_4').css 'background-color', 'gray'

And attach it to your index.haml file

  = sass :default
  = coffee :default
  .container_12
    .grid_4
      %h1 Mercury Example
    .clear
    .grid_12
      %p
        Lorem ipsum dolor sit amet,
        consectetur adipisicing elit,
        sed do eiusmod tempor incididunt
        ut labore et dolore magna aliqua.
    .clear


text:
class CommandLine;
class MessageLoop;

namespace remoting {

class WtsConsoleObserver;

class HostService : public WtsConsoleMonitor {
 public:
  static HostService* GetInstance();

  // This function parses the command line and selects the action routine.
  bool InitWithCommandLine(const CommandLine* command_line);

  // Invoke the choosen action routine.
  int Run();

  // WtsConsoleMonitor implementation
  virtual void AddWtsConsoleObserver(WtsConsoleObserver* observer) OVERRIDE;
  virtual void RemoveWtsConsoleObserver(
                   WtsConsoleObserver* observer) OVERRIDE;

 private:
  HostService();
  ~HostService();

  // Notifies the service of changes in session state.
  void OnSessionChange();

  // This is a common entry point to the main service loop called by both
  // RunAsService() and RunInConsole().
  void RunMessageLoop();

  // This function handshakes with the service control manager and starts
  // the service.
  int RunAsService();

  // This function starts the service in interactive mode (i.e. as a plain
  // console application).
  int RunInConsole();

  static BOOL WINAPI ConsoleControlHandler(DWORD event);

  // The control handler of the service.
  static DWORD WINAPI ServiceControlHandler(DWORD control,
                                            DWORD event_type,
                                            LPVOID event_data,
                                            LPVOID context);

  // The main service entry point.
  static VOID WINAPI ServiceMain(DWORD argc, WCHAR* argv[]);

  static LRESULT CALLBACK SessionChangeNotificationProc(HWND hwnd,
                                                        UINT message,
                                                        WPARAM wparam,
                                                        LPARAM lparam);

  // Current physical console session id.
  uint32 console_session_id_;

  // The list of observers receiving notifications about any session attached
  // to the physical console.
  ObserverList<WtsConsoleObserver> console_observers_;

  // The host binary name.
  FilePath host_binary_;

  // Service message loop.
  MessageLoop* message_loop_;

  // The action routine to be executed.
  int (HostService::*run_routine_)();

  // The service name.
  string16 service_name_;

  // The service status handle.
  SERVICE_STATUS_HANDLE service_status_handle_;

  // True if the service is being stopped.
  bool shutting_down_;

  // A waitable event that is used to wait until the service is stopped.
  base::WaitableEvent stopped_event_;

  // Singleton.
  friend struct DefaultSingletonTraits<HostService>;

  DISALLOW_COPY_AND_ASSIGN(HostService);
};

}  // namespace remoting

#endif  // REMOTING_HOST_HOST_SERVICE_WIN_H_


text:
'use strict';

define('jwplayer', [], function() {
    return { 'default': jwplayer };
});


text:
<?php
namespace Diegomoura\LaravelBoleto\Boleto;

use Carbon\Carbon;
use Diegomoura\LaravelBoleto\Boleto\Render;
use Diegomoura\LaravelBoleto\Contracts\Pessoa as PessoaContract;
use Diegomoura\LaravelBoleto\Contracts\Boleto\Boleto as BoletoContract;
use Diegomoura\LaravelBoleto\Pessoa;
use Diegomoura\LaravelBoleto\Util;

/**
 * Class AbstractBoleto
 *
 * @package Diegomoura\LaravelBoleto\Boleto
 */
abstract class AbstractBoleto implements BoletoContract
{
    const PDF     = 'P';
    const HTML    = 'H';

    /**
     * Campos que são necessários para o boleto
     *
     * @var array
     */
    private $camposObrigatorios = [
        'numero',
        'agencia',
        'conta',
        'carteira',
    ];

    protected $protectedFields = [
        '',
    ];

    /**
     * Código do banco
     *
     * @var string
     */
    protected $codigoBanco;
    /**
     * Moeda
     *
     * @var int
     */
    protected $moeda = 9;
    /**
     * Valor total do boleto
     *
     * @var float
     */
    protected $valor;
    /**
     * Desconto total do boleto
     *
     * @var float
     */
    protected $desconto;
    /**
     * Valor para multa
     *
     * @var float
     */
    protected $multa = 0;
    /**
     * Valor para mora juros
     *
     * @var float
     */
    protected $juros = 0;
    /**
     * Dias apos vencimento do juros
     *
     * @var int
     */
    protected $jurosApos = 0;
    /**
     * Dias para protesto
     *
     * @var integer
     */
    protected $diasProtesto = 0;
    /**
     * Dias para baixa automática
     *
     * @var integer
     */
    protected $diasBaixaAutomatica;
    /**
     * Data do documento
     *
     * @var \Carbon\Carbon
     */
    protected $dataDocumento;
    /**
     * Data de emissão
     *
     * @var \Carbon\Carbon
     */
    protected $dataProcessamento;
    /**
     * Data de vencimento
     *
     * @var \Carbon\Carbon
     */
    protected $dataVencimento;
    /**
     * Data de limite de desconto
     *
     * @var \Carbon\Carbon
     */
    protected $dataDesconto;
    /**
     * Campo de aceite
     *
     * @var string
     */
    protected $aceite = 'N';
    /**
     * Espécie do documento, geralmente DM (Duplicata Mercantil)
     *
     * @var string
     */
    protected $especieDoc = 'DM';
    /**
     * Espécie do documento, coódigo para remessa
     *
     * @var array
     */
    protected $especiesCodigo = [];
    /**
     * Número do documento
     *
     * @var int
     */
    protected $numeroDocumento;
    /**
     * Define o número definido pelo cliente para compor o Nosso Número
     *
     * @var int
     */
    protected $numero;
    /**
     * Define o número definido pelo cliente para controle da remessa
     *
     * @var string
     */
    protected $numeroControle;
    /**
     * Campo de uso do banco no boleto
     *
     * @var string
     */
    protected $usoBanco;
    /**
     * Agência
     *
     * @var string
     */
    protected $agencia;
    /**
     * Dígito da agência
     *
     * @var string
     */
    protected $agenciaDv;
    /**
     * Conta
     *
     * @var string
     */
    protected $conta;
    /**
     * Dígito da conta
     *
     * @var string
     */
    protected $contaDv;
    /**
     * Modalidade de cobrança do cliente, geralmente Cobrança Simples ou Registrada
     *
     * @var string
     */
    protected $carteira;
    /**
     * Define as carteiras disponíveis para cada banco
     *
     * @var array
     */
    protected $carteiras = [];
    /**
     * Define as carteiras disponíveis para cada banco
     *
     * @var array
     */
    protected $carteirasNomes = [];
    /**
     * Entidade beneficiario (quem emite o boleto)
     *
     * @var PessoaContract
     */
    protected $beneficiario;
    /**
     * Entidade pagadora (de quem se cobra o boleto)
     *
     * @var PessoaContract
     */
    protected $pagador;
    /**
     * Entidade sacador avalista
     *
     * @var PessoaContract
     */
    protected $sacadorAvalista;
    /**
     * Array com as linhas do demonstrativo (descrição do pagamento)
     *
     * @var array
     */
    protected $descricaoDemonstrativo;
    /**
     * Linha de local de pagamento
     *
     * @var string
     */
    protected $localPagamento = 'Pagável em qualquer agência bancária até o vencimento.';
    /**
     * Array com as linhas de instruções
     *
     * @var array
     */
    protected $instrucoes = ['Pagar até a data do vencimento.'];
    /**
     * Localização do logotipo do banco, referente ao diretório de imagens
     *
     * @var string
     */
    protected $logo;
    /**
     * Variáveis adicionais.
     *
     * @var array
     */
    public $variaveis_adicionais = [];

    /**
     * Cache do campo livre para evitar processamento desnecessário.
     *
     * @var string
     */
    protected $campoLivre;
    /**
     * Cache do nosso numero para evitar processamento desnecessário.
     *
     * @var string
     */
    protected $campoNossoNumero;

    /**
     * Cache da linha digitabel para evitar processamento desnecessário.
     *
     * @var string
     */
    protected $campoLinhaDigitavel;

    /**
     * Cache do codigo de barras para evitar processamento desnecessário.
     *
     * @var string
     */
    protected $campoCodigoBarras;

    /**
     * Status do boleto, se vai criar alterar ou baixa no banco.
     *
     * @var int
     */
    protected $status = BoletoContract::STATUS_REGISTRO;

    /**
     * Construtor
     *
     * @param array $params Parâmetros iniciais para construção do objeto
     */
    public function __construct($params = [])
    {
        Util::fillClass($this, $params);
        // Marca a data de emissão para hoje, caso não especificada
        if (! $this->getDataDocumento()) {
            $this->setDataDocumento(new Carbon());
        }
        // Marca a data de processamento para hoje, caso não especificada
        if (! $this->getDataProcessamento()) {
            $this->setDataProcessamento(new Carbon());
        }
        // Marca a data de vencimento para daqui a 5 dias, caso não especificada
        if (! $this->getDataVencimento()) {
            $this->setDataVencimento(new Carbon(date('Y-m-d', strtotime('+5 days'))));
        }
        // Marca a data de desconto
        if (! $this->getDataDesconto()) {
            $this->setDataDesconto($this->getDataVencimento());
        }
    }

    /**
     * @return array
     */
    public function getProtectedFields()
    {
        return $this->protectedFields;
    }

    /**
     * Seta os campos obrigatórios
     *
     * @return $this
     */
    protected function setCamposObrigatorios()
    {
        $args = func_get_args();
        $this->camposObrigatorios = [];
        foreach ($args as $arg) {
            $this->addCampoObrigatorio($arg);
        }
        return $this;
    }

    /**
     * Adiciona os campos obrigatórios
     *
     * @return $this
     */
    protected function addCampoObrigatorio()
    {
        $args = func_get_args();
        foreach ($args as $arg) {
            !is_array($arg) || call_user_func_array([$this, __FUNCTION__], $arg);
            !is_string($arg) || array_push($this->camposObrigatorios, $arg);
        }
        return $this;
    }
    /**
     * Define a agência
     *
     * @param  string $agencia
     *
     * @return AbstractBoleto
     */
    public function setAgencia($agencia)
    {
        $this->agencia = (string) $agencia;

        return $this;
    }

    /**
     * Retorna a agência
     *
     * @return string
     */
    public function getAgencia()
    {
        return $this->agencia;
    }

    /**
     * Define o dígito da agência
     *
     * @param  string $agenciaDv
     *
     * @return AbstractBoleto
     */
    public function setAgenciaDv($agenciaDv)
    {
        $this->agenciaDv = $agenciaDv;

        return $this;
    }

    /**
     * Retorna o dígito da agência
     *
     * @return string
     */
    public function getAgenciaDv()
    {
        return $this->agenciaDv;
    }

    /**
     * Define o código da carteira (Com ou sem registro)
     *
     * @param  string $carteira
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function setCarteira($carteira)
    {
        if (! in_array($carteira, $this->getCarteiras())) {
            throw new \Exception("Carteira não disponível!");
        }
        $this->carteira = $carteira;

        return $this;
    }

    /**
     * Retorna o código da carteira (Com ou sem registro)
     *
     * @return string
     */
    public function getCarteira()
    {
        return $this->carteira;
    }

    /**
     * Retorna as carteiras disponíveis para este banco
     *
     * @return array
     */
    public function getCarteiras()
    {
        return $this->carteiras;
    }

    /**
     * Define a entidade beneficiario
     *
     * @param $beneficiario
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function setBeneficiario($beneficiario)
    {
        Util::addPessoa($this->beneficiario, $beneficiario);
        return $this;
    }

    /**
     * Retorna a entidade beneficiario
     *
     * @return PessoaContract
     */
    public function getBeneficiario()
    {
        return $this->beneficiario;
    }

    /**
     * Retorna o código do banco
     *
     * @return string
     */
    public function getCodigoBanco()
    {
        return $this->codigoBanco;
    }

    /**
     * Define o número da conta
     *
     * @param  string $conta
     *
     * @return AbstractBoleto
     */
    public function setConta($conta)
    {
        $this->conta = (string) $conta;

        return $this;
    }

    /**
     * Retorna o número da conta
     *
     * @return string
     */
    public function getConta()
    {
        return $this->conta;
    }

    /**
     * Define o dígito verificador da conta
     *
     * @param  string $contaDv
     *
     * @return AbstractBoleto
     */
    public function setContaDv($contaDv)
    {
        $this->contaDv = $contaDv;

        return $this;
    }

    /**
     * Retorna o dígito verificador da conta
     *
     * @return string
     */
    public function getContaDv()
    {
        return $this->contaDv;
    }

    /**
     * Define a data de vencimento
     *
     * @param  \Carbon\Carbon $dataVencimento
     *
     * @return AbstractBoleto
     */
    public function setDataVencimento(Carbon $dataVencimento)
    {
        $this->dataVencimento = $dataVencimento;

        return $this;
    }

    /**
     * Retorna a data de vencimento
     *
     * @return \Carbon\Carbon
     */
    public function getDataVencimento()
    {
        return $this->dataVencimento;
    }

    /**
     * Define a data de limite de desconto
     *
     * @param  \Carbon\Carbon $dataDesconto
     *
     * @return AbstractBoleto
     */
    public function setDataDesconto(Carbon $dataDesconto)
    {
        $this->dataDesconto = $dataDesconto;

        return $this;
    }

    /**
     * Retorna a data de limite de desconto
     *
     * @return \Carbon\Carbon
     */
    public function getDataDesconto()
    {
        return $this->dataDesconto;
    }

    /**
     * Define a data do documento
     *
     * @param  \Carbon\Carbon $dataDocumento
     *
     * @return AbstractBoleto
     */
    public function setDataDocumento(Carbon $dataDocumento)
    {
        $this->dataDocumento = $dataDocumento;

        return $this;
    }

    /**
     * Retorna a data do documento
     *
     * @return \Carbon\Carbon
     */
    public function getDataDocumento()
    {
        return $this->dataDocumento;
    }

    /**
     * Define o campo aceite
     *
     * @param  string $aceite
     *
     * @return AbstractBoleto
     */
    public function setAceite($aceite)
    {
        $this->aceite = $aceite;

        return $this;
    }

    /**
     * Retorna o campo aceite
     *
     * @return string
     */
    public function getAceite()
    {
        return is_numeric($this->aceite) ? ($this->aceite ? 'A' : 'N') : $this->aceite;
    }

    /**
     * Define o campo Espécie Doc, geralmente DM (Duplicata Mercantil)
     *
     * @param  string $especieDoc
     *
     * @return AbstractBoleto
     */
    public function setEspecieDoc($especieDoc)
    {
        $this->especieDoc = $especieDoc;

        return $this;
    }

    /**
     * Retorna o campo Espécie Doc, geralmente DM (Duplicata Mercantil)
     *
     * @return string
     */
    public function getEspecieDoc()
    {
        return $this->especieDoc;
    }

    /**
     * Retorna o codigo da Espécie Doc
     *
     * @param int $default
     *
     * @return string
     */
    public function getEspecieDocCodigo($default = 99)
    {
        return key_exists(strtoupper($this->especieDoc), $this->especiesCodigo)
            ? $this->especiesCodigo[strtoupper($this->getEspecieDoc())]
            : $default;
    }

    /**
     * Define o campo Número do documento
     *
     * @param  int $numeroDocumento
     *
     * @return AbstractBoleto
     */
    public function setNumeroDocumento($numeroDocumento)
    {
        $this->numeroDocumento = $numeroDocumento;

        return $this;
    }

    /**
     * Retorna o campo Número do documento
     *
     * @return string
     */
    public function getNumeroDocumento()
    {
        return $this->numeroDocumento;
    }

    /**
     * Define o número  definido pelo cliente para compor o nosso número
     *
     * @param  int $numero
     *
     * @return AbstractBoleto
     */
    public function setNumero($numero)
    {
        $this->numero = $numero;

        return $this;
    }

    /**
     * Define o número  definido pelo cliente para compor o nosso número
     *
     * @param  int $numero
     *
     * @return AbstractBoleto
     */
    public function setCampoNossoNumero($campoNossoNumero)
    {
        $this->campoNossoNumero = $campoNossoNumero;

        return $this;
    }

    public function setCampoLinhaDigitavel($campoLinhaDigitavel)
    {
        $this->campoLinhaDigitavel = $campoLinhaDigitavel;

        return $this;
    }

    public function setCampoCodigoBarras($campoCodigoBarras)
    {
        $this->campoCodigoBarras = $campoCodigoBarras;

        return $this;
    }

    /**
     * Retorna o número definido pelo cliente para compor o nosso número
     *
     * @return int
     */
    public function getNumero()
    {
        return $this->numero;
    }

    /**
     * Define o número  definido pelo cliente para controle da remessa
     *
     * @param  string $numeroControle
     *
     * @return AbstractBoleto
     */
    public function setNumeroControle($numeroControle)
    {
        $this->numeroControle = $numeroControle;

        return $this;
    }

    /**
     * Retorna o número definido pelo cliente para controle da remessa
     *
     * @return int
     */
    public function getNumeroControle()
    {
        return $this->numeroControle;
    }

    /**
     * Define o campo Uso do banco
     *
     * @param  string $usoBanco
     *
     * @return AbstractBoleto
     */
    public function setUsoBanco($usoBanco)
    {
        $this->usoBanco = $usoBanco;

        return $this;
    }

    /**
     * Retorna o campo Uso do banco
     *
     * @return string
     */
    public function getUsoBanco()
    {
        return $this->usoBanco;
    }

    /**
     * Define a data de geração do boleto
     *
     * @param  \Carbon\Carbon $dataProcessamento
     *
     * @return AbstractBoleto
     */
    public function setDataProcessamento(Carbon $dataProcessamento)
    {
        $this->dataProcessamento = $dataProcessamento;

        return $this;
    }

    /**
     * Retorna a data de geração do boleto
     *
     * @return \Carbon\Carbon
     */
    public function getDataProcessamento()
    {
        return $this->dataProcessamento;
    }

    /**
     * Adiciona uma instrução (máximo 5)
     *
     * @param string $instrucao
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function addInstrucao($instrucao)
    {
        if (count($this->getInstrucoes()) > 8) {
            throw new \Exception('Atingido o máximo de 5 instruções.');
        }
        array_push($this->instrucoes, $instrucao);

        return $this;
    }

    /**
     * Define um array com instruções (máximo 8) para pagamento
     *
     * @param array $instrucoes
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function setInstrucoes(array $instrucoes)
    {
        if (count($instrucoes) > 8) {
            throw new \Exception('Máximo de 8 instruções.');
        }
        $this->instrucoes = $instrucoes;

        return $this;
    }

    /**
     * Retorna um array com instruções (máximo 8) para pagamento
     *
     * @return array
     */
    public function getInstrucoes()
    {
        return array_slice((array) $this->instrucoes + [null, null, null, null, null, null, null, null], 0, 8);
    }

    /**
     * Adiciona um demonstrativo (máximo 5)
     *
     * @param string $descricaoDemonstrativo
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function addDescricaoDemonstrativo($descricaoDemonstrativo)
    {
        if (count($this->getDescricaoDemonstrativo()) > 5) {
            throw new \Exception('Atingido o máximo de 5 demonstrativos.');
        }
        array_push($this->descricaoDemonstrativo, $descricaoDemonstrativo);

        return $this;
    }

    /**
     * Define um array com a descrição do demonstrativo (máximo 5)
     *
     * @param array $descricaoDemonstrativo
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function setDescricaoDemonstrativo(array $descricaoDemonstrativo)
    {
        if (count($descricaoDemonstrativo) > 5) {
            throw new \Exception('Máximo de 5 demonstrativos.');
        }
        $this->descricaoDemonstrativo = $descricaoDemonstrativo;

        return $this;
    }

    /**
     * Retorna um array com a descrição do demonstrativo (máximo 5)
     *
     * @return array
     */
    public function getDescricaoDemonstrativo()
    {
        return array_slice((array) $this->descricaoDemonstrativo + [null, null, null, null, null], 0, 5);
    }

    /**
     * Define o local de pagamento do boleto
     *
     * @param  string $localPagamento
     *
     * @return AbstractBoleto
     */
    public function setLocalPagamento($localPagamento)
    {
        $this->localPagamento = $localPagamento;

        return $this;
    }

    /**
     * Retorna o local de pagamento do boleto
     *
     * @return string
     */
    public function getLocalPagamento()
    {
        return $this->localPagamento;
    }

    /**
     * Define a moeda utilizada pelo boleto
     *
     * @param  int $moeda
     *
     * @return AbstractBoleto
     */
    public function setMoeda($moeda)
    {
        $this->moeda = $moeda;

        return $this;
    }

    /**
     * Retorna a moeda utilizada pelo boleto
     *
     * @return int
     */
    public function getMoeda()
    {
        return $this->moeda;
    }

    /**
     * Define o objeto do pagador
     *
     * @param $pagador
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function setPagador($pagador)
    {
        Util::addPessoa($this->pagador, $pagador);
        return $this;
    }

    /**
     * Retorna o objeto do pagador
     *
     * @return PessoaContract
     */
    public function getPagador()
    {
        return $this->pagador;
    }

    /**
     * Define o objeto sacador avalista do boleto
     *
     * @param $sacadorAvalista
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function setSacadorAvalista($sacadorAvalista)
    {
        Util::addPessoa($this->sacadorAvalista, $sacadorAvalista);
        return $this;
    }

    /**
     * Retorna o objeto sacador avalista do boleto
     *
     * @return PessoaContract
     */
    public function getSacadorAvalista()
    {
        return $this->sacadorAvalista;
    }

    /**
     * Define o valor total do boleto (incluindo taxas)
     *
     * @param  string $valor
     *
     * @return AbstractBoleto
     */
    public function setValor($valor)
    {
        $this->valor = Util::nFloat($valor, 2, false);

        return $this;
    }

    /**
     * Retorna o valor total do boleto (incluindo taxas)
     *
     * @return string
     */
    public function getValor()
    {
        return Util::nFloat($this->valor, 2, false);
    }

    /**
     * Define o desconto total do boleto (incluindo taxas)
     *
     * @param  string $desconto
     *
     * @return AbstractBoleto
     */
    public function setDesconto($desconto)
    {
        $this->desconto = Util::nFloat($desconto, 2, false);

        return $this;
    }

    /**
     * Retorna o desconto total do boleto (incluindo taxas)
     *
     * @return string
     */
    public function getDesconto()
    {
        return Util::nFloat($this->desconto, 2, false);
    }
    /**
     * Seta a % de multa
     *
     * @param  float $multa
     *
     * @return AbstractBoleto
     */
    public function setMulta($multa)
    {
        $this->multa = (float) ($multa > 0.00 ? $multa : 0.00);

        return $this;
    }

    /**
     * Retorna % de multa
     *
     * @return float
     */
    public function getMulta()
    {
        return $this->multa;
    }

    /**
     * Seta a % de juros
     *
     * @param  float $juros
     *
     * @return AbstractBoleto
     */
    public function setJuros($juros)
    {
        $this->juros = (float) ($juros > 0.00 ? $juros : 0.00);

        return $this;
    }

    /**
     * Retorna % juros
     *
     * @return float
     */
    public function getJuros()
    {
        return $this->juros;
    }

    /**
     * Seta a quantidade de dias apos o vencimento que cobra o juros
     *
     * @param  int $jurosApos
     *
     * @return AbstractBoleto
     */
    public function setJurosApos($jurosApos)
    {
        $jurosApos       = (int) $jurosApos;
        $this->jurosApos = $jurosApos > 0 ? $jurosApos : 0;

        return $this;
    }

    /**
     * Retorna a quantidade de dias apos o vencimento que cobrar a juros
     *
     * @return int
     */
    public function getJurosApos()
    {
        return $this->jurosApos;
    }

    /**
     * Seta dias para protesto
     *
     * @param int $diasProtesto
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function setDiasProtesto($diasProtesto)
    {
        if ($this->getDiasBaixaAutomatica() > 0) {
            throw new \Exception('Você deve usar dias de protesto ou dias de baixa, nunca os 2');
        }
        $diasProtesto       = (int) $diasProtesto;
        $this->diasProtesto = $diasProtesto > 0 ? $diasProtesto : 0;

        return $this;
    }

    /**
     * Retorna os diasProtesto
     *
     * @param int $default
     *
     * @return int
     */
    public function getDiasProtesto($default = 0)
    {
        return $this->diasProtesto > 0 ? $this->diasProtesto : $default;
    }

    /**
     * Seta dias para baixa automática
     *
     * @param int $baixaAutomatica
     *
     * @return AbstractBoleto
     * @throws \Exception
     */
    public function setDiasBaixaAutomatica($baixaAutomatica)
    {
        $exception = sprintf('O banco %s não suporta baixa automática, pode usar também: setDiasProtesto(%s)', basename(get_class($this)), $baixaAutomatica);
        throw new \Exception($exception);
    }

    /**
     * Retorna os diasProtesto
     *
     * @param int $default
     *
     * @return int
     */
    public function getDiasBaixaAutomatica($default = 0)
    {
        return $this->diasBaixaAutomatica > 0 ? $this->diasBaixaAutomatica : $default;
    }

    /**
     * Define a localização do logotipo
     *
     * @param  string $logo
     *
     * @return AbstractBoleto
     */
    public function setLogo($logo)
    {
        $this->logo = $logo;

        return $this;
    }

    /**
     * Retorna a localização do logotipo
     *
     * @return string
     */
    public function getLogo()
    {
        return $this->logo ? $this->logo : "http://dummyimage.com/300x70/f5/0.png&text=Sem+Logo";
    }

    /**
     * @return array
     */
    public function getVariaveisAdicionais()
    {
        return $this->variaveis_adicionais;
    }

    /**
     * @param array $variaveis_adicionais
     */
    public function setVariaveisAdicionais($variaveis_adicionais)
    {
        $this->variaveis_adicionais = $variaveis_adicionais;
    }

    /**
     * Retorna o logotipo em Base64, pronto para ser inserido na página
     *
     * @return string
     */
    public function getLogoBase64()
    {
        return 'data:image/' . pathinfo($this->getLogo(), PATHINFO_EXTENSION) .
            ';base64,' . base64_encode(file_get_contents($this->getLogo()));
    }

    /**
     * Retorna a localização do logotipo do banco relativo à pasta de imagens
     *
     * @return string
     */
    public function getLogoBanco()
    {
        return realpath(__DIR__ . '/../../logos/' . $this->getCodigoBanco() . '.png');
    }

    /**
     * @return int
     */
    public function getStatus()
    {
        return $this->status;
    }

    /**
     * Marca o boleto para ser alterado no banco
     *
     * @return AbstractBoleto
     */
    public function alterarBoleto()
    {
        $this->status = BoletoContract::STATUS_ALTERACAO;

        return $this;
    }

    /**
     * Marca o boleto para ser baixado no banco
     *
     * @return AbstractBoleto
     */
    public function baixarBoleto()
    {
        $this->status = BoletoContract::STATUS_BAIXA;

        return $this;
    }

    /**
     * Retorna o logotipo do banco em Base64, pronto para ser inserido na página
     *
     * @return string
     */
    public function getLogoBancoBase64()
    {
        return 'data:image/' . pathinfo($this->getLogoBanco(),
                PATHINFO_EXTENSION) . ';base64,' . base64_encode(file_get_contents($this->getLogoBanco()));
    }

    /**
     * Retorna o Nosso Número calculado.
     *
     * @return string
     */
    public function getNossoNumero()
    {
        if (empty($this->campoNossoNumero)) {
            return $this->campoNossoNumero = $this->gerarNossoNumero();
        }
        return $this->campoNossoNumero;
    }

    /**
     * Método que retorna o nosso numero usado no boleto. alguns bancos possuem algumas diferenças.
     *
     * @return string
     */
    public function getNossoNumeroBoleto()
    {
        return $this->getNossoNumero();
    }

    /**
     * Método onde o Boleto deverá gerar o Nosso Número.
     *
     * @return string
     */
    abstract protected function gerarNossoNumero();

    /**
     * Método onde qualquer boleto deve extender para gerar o código da posição de 20 a 44
     *
     * @return string
     */
    abstract protected function getCampoLivre();

    /**
     * Método que valida se o banco tem todos os campos obrigadotorios preenchidos
     *
     * @return boolean
     */
    public function isValid()
    {
        foreach ($this->camposObrigatorios as $campo) {
            if (call_user_func([$this, 'get' . ucwords($campo)]) == '') {
                return false;
            }
        }
        return true;
    }

    /**
     * Retorna o campo Agência/Beneficiário do boleto
     *
     * @return string
     */
    public function getAgenciaCodigoBeneficiario()
    {
        $agencia = $this->getAgenciaDv() !== null ? $this->getAgencia() . '-' . $this->getAgenciaDv() : $this->getAgencia();
        $conta   = $this->getContaDv() !== null ? $this->getConta() . '-' . $this->getContaDv() : $this->getConta();

        return $agencia . ' / ' . $conta;
    }

    /**
     * Retorna o nome da carteira para impressão no boleto
     *
     * Caso o nome da carteira a ser impresso no boleto seja diferente do número
     * Então crie uma variável na classe do banco correspondente $carteirasNomes
     * sendo uma array cujos índices sejam os números das carteiras e os valores
     * seus respectivos nomes
     *
     * @return string
     */
    public function getCarteiraNome()
    {
        return isset($this->carteirasNomes[$this->getCarteira()]) ? $this->carteirasNomes[$this->getCarteira()] : $this->getCarteira();
    }

    /**
     * Retorna o codigo de barras
     *
     * @return string
     * @throws \Exception
     */
    public function getCodigoBarras()
    {
        if (! empty($this->campoCodigoBarras)) {
            return $this->campoCodigoBarras;
        }

        if (! $this->isValid()) {
            throw new \Exception('Campos requeridos pelo banco, aparentam estar ausentes');
        }

        $codigo = Util::numberFormatGeral($this->getCodigoBanco(), 3)
            . $this->getMoeda()
            . Util::fatorVencimento($this->getDataVencimento())
            . Util::numberFormatGeral($this->getValor(), 10)
            . $this->getCampoLivre();

        $resto = Util::modulo11($codigo, 2, 9, 0);
        $dv    = (in_array($resto, [0, 10, 11])) ? 1 : $resto;

        return $this->campoCodigoBarras = substr($codigo, 0, 4) . $dv . substr($codigo, 4);
    }

    /**
     * Retorna o código do banco com o dígito verificador
     *
     * @return string
     */
    public function getCodigoBancoComDv()
    {
        $codigoBanco = $this->getCodigoBanco();

        return $codigoBanco . '-' . Util::modulo11($codigoBanco);
    }

    /**
     * Retorna a linha digitável do boleto
     *
     * @return string
     * @throws \Exception
     */
    public function getLinhaDigitavel()
    {
        if (! empty($this->campoLinhaDigitavel)) {

            return substr($this->campoLinhaDigitavel,0,5) . '.' .
                substr($this->campoLinhaDigitavel,5,5) . ' ' .
                substr($this->campoLinhaDigitavel,10,5) . '.' .
                substr($this->campoLinhaDigitavel,15,6) . ' ' .
                substr($this->campoLinhaDigitavel,21,5) . '.' .
                substr($this->campoLinhaDigitavel,26,6) . ' ' .
                substr($this->campoLinhaDigitavel,32,1) . ' ' .
                substr($this->campoLinhaDigitavel,33,14);
        }

        $codigo = $this->getCodigoBarras();

        $s1 = substr($codigo, 0, 4) . substr($codigo, 19, 5);
        $s1 = $s1 . Util::modulo10($s1);
        $s1 = substr_replace($s1, '.', 5, 0);

        $s2 = substr($codigo, 24, 10);
        $s2 = $s2 . Util::modulo10($s2);
        $s2 = substr_replace($s2, '.', 5, 0);

        $s3 = substr($codigo, 34, 10);
        $s3 = $s3 . Util::modulo10($s3);
        $s3 = substr_replace($s3, '.', 5, 0);

        $s4 = substr($codigo, 4, 1);

        $s5 = substr($codigo, 5, 14);

        return $this->campoLinhaDigitavel = sprintf('%s %s %s %s %s', $s1, $s2, $s3, $s4, $s5);
    }

    /**
     * Renderiza o(s) boleto(s) de acordo com o tipo informado
     *
     * @param string $tipo
     * @param bool $print
     * @param bool $instrucoes
     * @param string $dest
     * @param null $save_path
     * @return string
     */
    public function render($tipo = self::PDF, $print = false, $instrucoes = true, $dest = Render::OUTPUT_INLINE, $save_path = null)
    {
        $render = new Render();
        $render->addBoleto($this);
        !$print || $render->showPrint();
        $instrucoes || $render->hideInstrucoes();

        if ($tipo == self::HTML){
            return $render->gerarBoletoHtml();
        }
        return $render->gerarBoletoPdf($dest, $save_path);
    }

    /**
     * @return $this
     */
    public function copy() {
        return clone $this;
    }

    /**
     * On clone clean variables
     */
    function __clone()
    {
        $this->campoLivre = null;
        $this->campoNossoNumero = null;
        $this->campoLinhaDigitavel = null;
        $this->campoCodigoBarras = null;
    }

    /**
     * Return Boleto Array.
     *
     * @return array
     * @throws \Exception
     */
    public function toArray()
    {
        return array_merge(
            [
                'linha_digitavel'             => $this->getLinhaDigitavel(),
                'codigo_barras'               => $this->getCodigoBarras(),
                'beneficiario'                => $this->getBeneficiario()->toArray(),
                'logo_base64'                 => $this->getLogoBase64(),
                'logo'                        => $this->getLogo(),
                'logo_banco_base64'           => $this->getLogoBancoBase64(),
                'logo_banco'                  => $this->getLogoBanco(),
                'codigo_banco_com_dv'         => $this->getCodigoBancoComDv(),
                'especie'                     => 'R$',
                'data_vencimento'             => $this->getDataVencimento(),
                'data_processamento'          => $this->getDataProcessamento(),
                'data_documento'              => $this->getDataDocumento(),
                'data_desconto'               => $this->getDataDesconto(),
                'valor'                       => Util::nReal($this->getValor(), 2, false),
                'desconto'                    => Util::nReal($this->getDesconto(), 2, false),
                'multa'                       => Util::nReal($this->getMulta(), 2, false),
                'juros'                       => Util::nReal($this->getJuros(), 2, false),
                'juros_apos'                  => $this->getJurosApos(),
                'dias_protesto'               => $this->getDiasProtesto(),
                'sacador_avalista'            => $this->getSacadorAvalista() ? $this->getPagador()->toArray() : [],
                'pagador'                     => $this->getPagador()->toArray(),
                'demonstrativo'               => $this->getDescricaoDemonstrativo(),
                'instrucoes'                  => $this->getInstrucoes(),
                'local_pagamento'             => $this->getLocalPagamento(),
                'numero'                      => $this->getNumero(),
                'numero_documento'            => $this->getNumeroDocumento(),
                'numero_controle'             => $this->getNumeroControle(),
                'agencia_codigo_beneficiario' => $this->getAgenciaCodigoBeneficiario(),
                'nosso_numero'                => $this->getNossoNumero(),
                'nosso_numero_boleto'         => $this->getNossoNumeroBoleto(),
                'especie_doc'                 => $this->getEspecieDoc(),
                'especie_doc_cod'             => $this->getEspecieDocCodigo(),
                'aceite'                      => $this->getAceite(),
                'carteira'                    => $this->getCarteira(),
                'carteira_nome'               => $this->getCarteiraNome(),
                'uso_banco'                   => $this->getUsoBanco(),
                'status'                      => $this->getStatus(),
            ], $this->variaveis_adicionais
        );
    }
}