<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package thrift

import (
	"log"
	"runtime/debug"
	"sync"
)

/*
 * This is not a typical TSimpleServer as it is not blocked after accept a socket.
 * It is more like a TThreadedServer that can handle different connections in different goroutines.
 * This will work if golang user implements a conn-pool like thing in client side.
 */
type TSimpleServer struct {
	quit chan struct{}

	processorFactory       TProcessorFactory
	serverTransport        TServerTransport
	inputTransportFactory  TTransportFactory
	outputTransportFactory TTransportFactory
	inputProtocolFactory   TProtocolFactory
	outputProtocolFactory  TProtocolFactory
}

func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer {
	return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport)
}

func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
	return NewTSimpleServerFactory4(NewTProcessorFactory(processor),
		serverTransport,
		transportFactory,
		protocolFactory,
	)
}

func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
	return NewTSimpleServerFactory6(NewTProcessorFactory(processor),
		serverTransport,
		inputTransportFactory,
		outputTransportFactory,
		inputProtocolFactory,
		outputProtocolFactory,
	)
}

func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer {
	return NewTSimpleServerFactory6(processorFactory,
		serverTransport,
		NewTTransportFactory(),
		NewTTransportFactory(),
		NewTBinaryProtocolFactoryDefault(),
		NewTBinaryProtocolFactoryDefault(),
	)
}

func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
	return NewTSimpleServerFactory6(processorFactory,
		serverTransport,
		transportFactory,
		transportFactory,
		protocolFactory,
		protocolFactory,
	)
}

func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
	return &TSimpleServer{
		processorFactory:       processorFactory,
		serverTransport:        serverTransport,
		inputTransportFactory:  inputTransportFactory,
		outputTransportFactory: outputTransportFactory,
		inputProtocolFactory:   inputProtocolFactory,
		outputProtocolFactory:  outputProtocolFactory,
		quit: make(chan struct{}, 1),
	}
}

func (p *TSimpleServer) ProcessorFactory() TProcessorFactory {
	return p.processorFactory
}

func (p *TSimpleServer) ServerTransport() TServerTransport {
	return p.serverTransport
}

func (p *TSimpleServer) InputTransportFactory() TTransportFactory {
	return p.inputTransportFactory
}

func (p *TSimpleServer) OutputTransportFactory() TTransportFactory {
	return p.outputTransportFactory
}

func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory {
	return p.inputProtocolFactory
}

func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory {
	return p.outputProtocolFactory
}

func (p *TSimpleServer) Listen() error {
	return p.serverTransport.Listen()
}

func (p *TSimpleServer) AcceptLoop() error {
	for {
		client, err := p.serverTransport.Accept()
		if err != nil {
			select {
			case <-p.quit:
				return nil
			default:
			}
			return err
		}
		if client != nil {
			go func() {
				if err := p.processRequests(client); err != nil {
					log.Println("error processing request:", err)
				}
			}()
		}
	}
}

func (p *TSimpleServer) Serve() error {
	err := p.Listen()
	if err != nil {
		return err
	}
	p.AcceptLoop()
	return nil
}

var once sync.Once

func (p *TSimpleServer) Stop() error {
	q := func() {
		p.quit <- struct{}{}
		p.serverTransport.Interrupt()
	}
	once.Do(q)
	return nil
}

func (p *TSimpleServer) processRequests(client TTransport) error {
	processor := p.processorFactory.GetProcessor(client)
	inputTransport := p.inputTransportFactory.GetTransport(client)
	outputTransport := p.outputTransportFactory.GetTransport(client)
	inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)
	outputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport)
	defer func() {
		if e := recover(); e != nil {
			log.Printf("panic in processor: %s: %s", e, debug.Stack())
		}
	}()
	if inputTransport != nil {
		defer inputTransport.Close()
	}
	if outputTransport != nil {
		defer outputTransport.Close()
	}
	for {
		ok, err := processor.Process(inputProtocol, outputProtocol)
		if err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE {
			return nil
		} else if err != nil {
			return err
		}
		if err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD {
			continue
		}
 		if !ok {
			break
		}
	}
	return nil
}
<commit_msg>THRIFT-4203 thrift server stop gracefully Client: Go Patch: libinbin <libinbin@17paipai.cn><commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package thrift

import (
	"log"
	"runtime/debug"
	"sync"
)

/*
 * This is not a typical TSimpleServer as it is not blocked after accept a socket.
 * It is more like a TThreadedServer that can handle different connections in different goroutines.
 * This will work if golang user implements a conn-pool like thing in client side.
 */
type TSimpleServer struct {
	quit chan struct{}

	processorFactory       TProcessorFactory
	serverTransport        TServerTransport
	inputTransportFactory  TTransportFactory
	outputTransportFactory TTransportFactory
	inputProtocolFactory   TProtocolFactory
	outputProtocolFactory  TProtocolFactory
	sync.WaitGroup
}

func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer {
	return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport)
}

func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
	return NewTSimpleServerFactory4(NewTProcessorFactory(processor),
		serverTransport,
		transportFactory,
		protocolFactory,
	)
}

func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
	return NewTSimpleServerFactory6(NewTProcessorFactory(processor),
		serverTransport,
		inputTransportFactory,
		outputTransportFactory,
		inputProtocolFactory,
		outputProtocolFactory,
	)
}

func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer {
	return NewTSimpleServerFactory6(processorFactory,
		serverTransport,
		NewTTransportFactory(),
		NewTTransportFactory(),
		NewTBinaryProtocolFactoryDefault(),
		NewTBinaryProtocolFactoryDefault(),
	)
}

func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
	return NewTSimpleServerFactory6(processorFactory,
		serverTransport,
		transportFactory,
		transportFactory,
		protocolFactory,
		protocolFactory,
	)
}

func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
	return &TSimpleServer{
		processorFactory:       processorFactory,
		serverTransport:        serverTransport,
		inputTransportFactory:  inputTransportFactory,
		outputTransportFactory: outputTransportFactory,
		inputProtocolFactory:   inputProtocolFactory,
		outputProtocolFactory:  outputProtocolFactory,
		quit: make(chan struct{}, 1),
	}
}

func (p *TSimpleServer) ProcessorFactory() TProcessorFactory {
	return p.processorFactory
}

func (p *TSimpleServer) ServerTransport() TServerTransport {
	return p.serverTransport
}

func (p *TSimpleServer) InputTransportFactory() TTransportFactory {
	return p.inputTransportFactory
}

func (p *TSimpleServer) OutputTransportFactory() TTransportFactory {
	return p.outputTransportFactory
}

func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory {
	return p.inputProtocolFactory
}

func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory {
	return p.outputProtocolFactory
}

func (p *TSimpleServer) Listen() error {
	return p.serverTransport.Listen()
}

func (p *TSimpleServer) AcceptLoop() error {
	for {
		client, err := p.serverTransport.Accept()
		if err != nil {
			select {
			case <-p.quit:
				return nil
			default:
			}
			return err
		}
		if client != nil {
			p.Add(1)
			go func() {
				if err := p.processRequests(client); err != nil {
					log.Println("error processing request:", err)
				}
			}()
		}
	}
}

func (p *TSimpleServer) Serve() error {
	err := p.Listen()
	if err != nil {
		return err
	}
	p.AcceptLoop()
	return nil
}

var once sync.Once

func (p *TSimpleServer) Stop() error {
	q := func() {
		close(p.quit)
		p.serverTransport.Interrupt()
		p.Wait()
	}
	once.Do(q)
	return nil
}

func (p *TSimpleServer) processRequests(client TTransport) error {
	defer p.Done()

	processor := p.processorFactory.GetProcessor(client)
	inputTransport := p.inputTransportFactory.GetTransport(client)
	outputTransport := p.outputTransportFactory.GetTransport(client)
	inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)
	outputProtocol := p.outputProtocolFactory.GetProtocol(outputTransport)
	defer func() {
		if e := recover(); e != nil {
			log.Printf("panic in processor: %s: %s", e, debug.Stack())
		}
	}()

	if inputTransport != nil {
		defer inputTransport.Close()
	}
	if outputTransport != nil {
		defer outputTransport.Close()
	}
	for {
		select {
		case <-p.quit:
			return nil
		default:
		}

		ok, err := processor.Process(inputProtocol, outputProtocol)
		if err, ok := err.(TTransportException); ok && err.TypeId() == END_OF_FILE {
			return nil
		} else if err != nil {
			return err
		}
		if err, ok := err.(TApplicationException); ok && err.TypeId() == UNKNOWN_METHOD {
			continue
		}
		if !ok {
			break
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
        "sort"
        "unicode"
        "fmt"
        "math/rand"
)

type Task struct {
        id int
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
        raw_todo string
        finished bool
        finish_date time.Time
        id_padding int
}

type TaskList []Task

func ParseTask(text string, id int) (Task) {
        var task = Task{}
        task.id = id
        task.raw_todo = text

        splits := strings.Split(text, " ")

        // checking if the task is already finished
        if text[0] == 'x' &&
           text[1] == ' ' &&
           !unicode.IsSpace(rune(text[2])) {
                task.finished = true
                splits = splits[1:]
        }

        date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"

        // checking for finish date
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.finish_date = date
                }

                splits = splits[1:]
        }

        head := splits[0]

        // checking for priority
        if (len(head) == 3) &&
           (head[0] == '(') &&
           (head[2] == ')') &&
           (head[1] >= 65 && head[1] <= 90) { // checking if it's in range [A-Z]
                task.priority = head[1]
                splits = splits[1:]
        }

        // checking for creation date and building the actual todo item
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.create_date = date
                }

                task.todo = strings.Join(splits[1:], " ")
        } else {
                task.todo = strings.Join(splits[0:], " ")
        }

        context_regexp, _ := regexp.Compile("@[[:word:]]+")
        contexts := context_regexp.FindAllStringSubmatch(text, -1)
        if len(contexts) != 0 {
                task.contexts = contexts[0]
        }

        project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
        projects := project_regexp.FindAllStringSubmatch(text, -1)
        if len(projects) != 0 {
                task.projects = projects[0]
        }

        return task
}

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)

        for scanner.Scan() {
                text := scanner.Text()
                tasklist.Add(text)
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

type By func(t1, t2 Task) bool

func (by By) Sort(tasks TaskList) {
        ts := &taskSorter{
                tasks: tasks,
                by:    by,
        }
        sort.Sort(ts)
}

type taskSorter struct {
        tasks TaskList
        by func(t1, t2 Task) bool
}

func (s *taskSorter) Len() int {
        return len(s.tasks)
}

func (s *taskSorter) Swap(i, j int) {
        s.tasks[i], s.tasks[j] = s.tasks[j], s.tasks[i]
}

func (s *taskSorter) Less(i, j int) bool {
        return s.by(s.tasks[i], s.tasks[j])
}

func (tasks TaskList) Len() int {
        return len(tasks)
}

func prioCmp(t1, t2 Task) bool {
        return t1.Priority() < t2.Priority()
}

func prioRevCmp(t1, t2 Task) bool {
        return t1.Priority() > t2.Priority()
}

func dateCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 > tm2
        }
}

func dateRevCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 < tm2
        }
}

func lenCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 < tl2
        }
}

func lenRevCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 > tl2
        }
}

func idCmp(t1, t2 Task) bool {
        return t1.Id() < t2.Id()
}

func randCmp(t1, t2 Task) bool {
        rand.Seed(time.Now().UnixNano()%1e6/1e3)
        return rand.Intn(len(t1.raw_todo)) > rand.Intn(len(t2.raw_todo))
}

func (tasks TaskList) Sort(by string) {
        switch by {
        default:
        case "prio":
                By(prioCmp).Sort(tasks)
        case "prio-rev":
                By(prioRevCmp).Sort(tasks)
        case "date":
                By(dateCmp).Sort(tasks)
        case "date-rev":
                By(dateRevCmp).Sort(tasks)
        case "len":
                By(lenCmp).Sort(tasks)
        case "len-rev":
                By(lenRevCmp).Sort(tasks)
        case "id":
                By(idCmp).Sort(tasks)
        case "rand":
                By(randCmp).Sort(tasks)
        }
}

func (tasks TaskList) Save(filename string) {
        tasks.Sort("id")

        f, err := os.Create(filename)
        if err != nil {
                panic(err)
        }

        defer f.Close()

        for _, task := range tasks {
                f.WriteString(task.RawText() + "\n")
        }
        f.Sync()
}

func (tasks *TaskList) Add(todo string) {
        task := ParseTask(todo, tasks.Len())
        *tasks = append(*tasks, task)
}

func (tasks TaskList) Done(id int, finish_date bool) error {
        if id > tasks.Len() || id < 0 {
                return fmt.Errorf("Error: id is %v", id)
        }

        tasks[id].finished = true
        if finish_date {
                t := time.Now()
                tasks[id].raw_todo = "x " + t.Format("2006-01-02") + " " +
                                        tasks[id].raw_todo
        } else {
                tasks[id].raw_todo = "x " + tasks[id].raw_todo
        }

        return nil
}

func (task Task) Id() int {
        return task.id
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) RawText() string {
        return task.raw_todo
}

func (task Task) Priority() byte {
        // if priority is not from [A-Z], let it be 94 (^)
        if task.priority < 65 || task.priority > 90 {
                return 94 // you know, ^
        } else {
                return task.priority
        }
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}

func (task Task) Finished() bool {
        return task.finished
}

func (task Task) FinishDate() time.Time {
        return task.finish_date
}

func (task *Task) SetIdPaddingBy(tasklist TaskList) {
        l := tasklist.Len()

        if l >= 10000 {
                task.id_padding = 5
        } else if l >= 1000 {
                task.id_padding = 4
        } else if l >= 100 {
                task.id_padding = 3
        } else if l >= 10 {
                task.id_padding = 2
        } else {
                task.id_padding = 1
        }
}

func (task *Task) RebuildRawTodo() {
        if task.finished {
                task.raw_todo = task.PrettyPrint("x %P%t")
        } else {
                task.raw_todo = task.PrettyPrint("%P%t")
        }
}

func (task *Task) SetPriority(prio byte) {
        if task.priority < 65 || task.priority > 90 {
                task.priority = '^'
        } else {
                task.priority = prio
        }
}

func (task *Task) SetTodo(todo string) {
        task.todo = todo
}

func (task Task) IdPadding() int {
        return task.id_padding
}

func pad(in string, length int) string {
        if (length == -1) {
                return in
        }

        if (length > len(in)) {
                return strings.Repeat(" ", length - len(in)) + in
        } else {
                return in[:length]
        }
}

func (task Task) PrettyPrint(pretty string) string {
        rp := regexp.MustCompile("(%(\\.\\d+|)[a-zA-Z])")
        padding := -1
        out := rp.ReplaceAllStringFunc(pretty, func(s string) string {
                if (len(s) < 2) {
                        return ""
                }

                var f string
                if (s[0] == '%' && s[1] == '.') {
                        if _, e := fmt.Sscanf(s[2:], "%d%s", &padding, &f); e != nil {
                                panic(e);
                        }
                        f = "%" + f
                } else {
                        f = s
                }

                ret := s
                switch f {
                case "%i":
                        str := fmt.Sprintf("%%0%dd", task.IdPadding())
                        ret = fmt.Sprintf(str, task.Id())
                case "%t":
                        ret = task.Text()
                case "%T":
                        ret = task.RawText()
                case "%p":
                        ret = string(task.Priority())
                case "%P":
                        if task.Priority() != '^' {
                                ret = "(" + string(task.Priority()) + ") "
                        } else {
                                ret = ""
                        }
                case "%d":
                        ret = fmt.Sprintf("%d", task.CreateDate().Day())
                case "%m":
                        ret = fmt.Sprintf("%d", int(task.CreateDate().Month()))
                case "%y":
                        ret = fmt.Sprintf("%d", task.CreateDate().Year())

                case "%D":
                        ret = fmt.Sprintf("%d", task.FinishDate().Day())
                case "%M":
                        ret = fmt.Sprintf("%d", int(task.FinishDate().Month()))
                case "%Y":
                        ret = fmt.Sprintf("%d", task.FinishDate().Year())

                default:
                        ret = s
                }

                ret = pad(ret, padding)
                padding = -1
                return ret
        })
        return out
}
<commit_msg>soft padding<commit_after>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
        "sort"
        "unicode"
        "fmt"
        "math/rand"
)

type Task struct {
        id int
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
        raw_todo string
        finished bool
        finish_date time.Time
        id_padding int
}

type TaskList []Task

func ParseTask(text string, id int) (Task) {
        var task = Task{}
        task.id = id
        task.raw_todo = text

        splits := strings.Split(text, " ")

        // checking if the task is already finished
        if text[0] == 'x' &&
           text[1] == ' ' &&
           !unicode.IsSpace(rune(text[2])) {
                task.finished = true
                splits = splits[1:]
        }

        date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"

        // checking for finish date
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.finish_date = date
                }

                splits = splits[1:]
        }

        head := splits[0]

        // checking for priority
        if (len(head) == 3) &&
           (head[0] == '(') &&
           (head[2] == ')') &&
           (head[1] >= 65 && head[1] <= 90) { // checking if it's in range [A-Z]
                task.priority = head[1]
                splits = splits[1:]
        }

        // checking for creation date and building the actual todo item
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.create_date = date
                }

                task.todo = strings.Join(splits[1:], " ")
        } else {
                task.todo = strings.Join(splits[0:], " ")
        }

        context_regexp, _ := regexp.Compile("@[[:word:]]+")
        contexts := context_regexp.FindAllStringSubmatch(text, -1)
        if len(contexts) != 0 {
                task.contexts = contexts[0]
        }

        project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
        projects := project_regexp.FindAllStringSubmatch(text, -1)
        if len(projects) != 0 {
                task.projects = projects[0]
        }

        return task
}

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)

        for scanner.Scan() {
                text := scanner.Text()
                tasklist.Add(text)
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

type By func(t1, t2 Task) bool

func (by By) Sort(tasks TaskList) {
        ts := &taskSorter{
                tasks: tasks,
                by:    by,
        }
        sort.Sort(ts)
}

type taskSorter struct {
        tasks TaskList
        by func(t1, t2 Task) bool
}

func (s *taskSorter) Len() int {
        return len(s.tasks)
}

func (s *taskSorter) Swap(i, j int) {
        s.tasks[i], s.tasks[j] = s.tasks[j], s.tasks[i]
}

func (s *taskSorter) Less(i, j int) bool {
        return s.by(s.tasks[i], s.tasks[j])
}

func (tasks TaskList) Len() int {
        return len(tasks)
}

func prioCmp(t1, t2 Task) bool {
        return t1.Priority() < t2.Priority()
}

func prioRevCmp(t1, t2 Task) bool {
        return t1.Priority() > t2.Priority()
}

func dateCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 > tm2
        }
}

func dateRevCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 < tm2
        }
}

func lenCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 < tl2
        }
}

func lenRevCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 > tl2
        }
}

func idCmp(t1, t2 Task) bool {
        return t1.Id() < t2.Id()
}

func randCmp(t1, t2 Task) bool {
        rand.Seed(time.Now().UnixNano()%1e6/1e3)
        return rand.Intn(len(t1.raw_todo)) > rand.Intn(len(t2.raw_todo))
}

func (tasks TaskList) Sort(by string) {
        switch by {
        default:
        case "prio":
                By(prioCmp).Sort(tasks)
        case "prio-rev":
                By(prioRevCmp).Sort(tasks)
        case "date":
                By(dateCmp).Sort(tasks)
        case "date-rev":
                By(dateRevCmp).Sort(tasks)
        case "len":
                By(lenCmp).Sort(tasks)
        case "len-rev":
                By(lenRevCmp).Sort(tasks)
        case "id":
                By(idCmp).Sort(tasks)
        case "rand":
                By(randCmp).Sort(tasks)
        }
}

func (tasks TaskList) Save(filename string) {
        tasks.Sort("id")

        f, err := os.Create(filename)
        if err != nil {
                panic(err)
        }

        defer f.Close()

        for _, task := range tasks {
                f.WriteString(task.RawText() + "\n")
        }
        f.Sync()
}

func (tasks *TaskList) Add(todo string) {
        task := ParseTask(todo, tasks.Len())
        *tasks = append(*tasks, task)
}

func (tasks TaskList) Done(id int, finish_date bool) error {
        if id > tasks.Len() || id < 0 {
                return fmt.Errorf("Error: id is %v", id)
        }

        tasks[id].finished = true
        if finish_date {
                t := time.Now()
                tasks[id].raw_todo = "x " + t.Format("2006-01-02") + " " +
                                        tasks[id].raw_todo
        } else {
                tasks[id].raw_todo = "x " + tasks[id].raw_todo
        }

        return nil
}

func (task Task) Id() int {
        return task.id
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) RawText() string {
        return task.raw_todo
}

func (task Task) Priority() byte {
        // if priority is not from [A-Z], let it be 94 (^)
        if task.priority < 65 || task.priority > 90 {
                return 94 // you know, ^
        } else {
                return task.priority
        }
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}

func (task Task) Finished() bool {
        return task.finished
}

func (task Task) FinishDate() time.Time {
        return task.finish_date
}

func (task *Task) SetIdPaddingBy(tasklist TaskList) {
        l := tasklist.Len()

        if l >= 10000 {
                task.id_padding = 5
        } else if l >= 1000 {
                task.id_padding = 4
        } else if l >= 100 {
                task.id_padding = 3
        } else if l >= 10 {
                task.id_padding = 2
        } else {
                task.id_padding = 1
        }
}

func (task *Task) RebuildRawTodo() {
        if task.finished {
                task.raw_todo = task.PrettyPrint("x %P%t")
        } else {
                task.raw_todo = task.PrettyPrint("%P%t")
        }
}

func (task *Task) SetPriority(prio byte) {
        if task.priority < 65 || task.priority > 90 {
                task.priority = '^'
        } else {
                task.priority = prio
        }
}

func (task *Task) SetTodo(todo string) {
        task.todo = todo
}

func (task Task) IdPadding() int {
        return task.id_padding
}

func pad(in string, length int) string {
        if (length == -1) {
                return in
        }

        if (length > len(in)) {
                return strings.Repeat(" ", length - len(in)) + in
        } else {
                return in[:length]
        }
}

func soft_pad(in string, length int) string {
        if (length == -1) {
                return in
        }

        if (length > len(in)) {
                return strings.Repeat(" ", length - len(in)) + in
        } else {
                slice := strings.Split(in, " ")
                return soft_pad(strings.Join(slice[:len(slice)-1], " "), length)
        }
}

func (task Task) PrettyPrint(pretty string) string {
        rp := regexp.MustCompile("(%((\\.|\\*)\\d+|)[a-zA-Z])")
        padding := -1
        out := rp.ReplaceAllStringFunc(pretty, func(s string) string {
                if (len(s) < 2) {
                        return ""
                }

                var f string
                if (s[0] == '%' && (s[1] == '.' || s[1] == '*')) {
                        if _, e := fmt.Sscanf(s[2:], "%d%s", &padding, &f); e != nil {
                                panic(e);
                        }
                        f = "%" + f
                } else {
                        f = s
                }

                ret := s
                switch f {
                case "%i":
                        str := fmt.Sprintf("%%0%dd", task.IdPadding())
                        ret = fmt.Sprintf(str, task.Id())
                case "%t":
                        ret = task.Text()
                case "%T":
                        ret = task.RawText()
                case "%p":
                        ret = string(task.Priority())
                case "%P":
                        if task.Priority() != '^' {
                                ret = "(" + string(task.Priority()) + ") "
                        } else {
                                ret = ""
                        }
                case "%d":
                        ret = fmt.Sprintf("%d", task.CreateDate().Day())
                case "%m":
                        ret = fmt.Sprintf("%d", int(task.CreateDate().Month()))
                case "%y":
                        ret = fmt.Sprintf("%d", task.CreateDate().Year())

                case "%D":
                        ret = fmt.Sprintf("%d", task.FinishDate().Day())
                case "%M":
                        ret = fmt.Sprintf("%d", int(task.FinishDate().Month()))
                case "%Y":
                        ret = fmt.Sprintf("%d", task.FinishDate().Year())

                default:
                        ret = s
                }

                switch s[1] {
                case '.':
                        ret = soft_pad(ret, padding)
                case '*':
                        ret = pad(ret, padding)
                }

                padding = -1
                return ret
        })
        return out
}
<|endoftext|>


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

// +build windows

package walk

import (
	"syscall"
	"unsafe"
)

import (
	"github.com/lxn/win"
)

func init() {
	var err error
	if globalToolTip, err = NewToolTip(); err != nil {
		panic(err)
	}
}

type ToolTip struct {
	WindowBase
}

var globalToolTip *ToolTip

func NewToolTip() (*ToolTip, error) {
	tt := new(ToolTip)

	if err := InitWindow(
		tt,
		nil,
		"tooltips_class32",
		win.WS_POPUP|win.TTS_ALWAYSTIP,
		win.WS_EX_TOPMOST); err != nil {
		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			tt.Dispose()
		}
	}()

	win.SetWindowPos(tt.hWnd, win.HWND_TOPMOST, 0, 0, 0, 0, win.SWP_NOMOVE|win.SWP_NOSIZE|win.SWP_NOACTIVATE)

	succeeded = true

	return tt, nil
}

func (*ToolTip) LayoutFlags() LayoutFlags {
	return 0
}

func (tt *ToolTip) SizeHint() Size {
	return Size{0, 0}
}

func (tt *ToolTip) Title() string {
	var gt win.TTGETTITLE

	buf := make([]uint16, 100)

	gt.DwSize = uint32(unsafe.Sizeof(gt))
	gt.Cch = uint32(len(buf))
	gt.PszTitle = &buf[0]

	tt.SendMessage(win.TTM_GETTITLE, 0, uintptr(unsafe.Pointer(&gt)))

	return syscall.UTF16ToString(buf)
}

func (tt *ToolTip) SetTitle(title string) error {
	return tt.setTitle(title, win.TTI_NONE)
}

func (tt *ToolTip) SetInfoTitle(title string) error {
	return tt.setTitle(title, win.TTI_INFO)
}

func (tt *ToolTip) SetWarningTitle(title string) error {
	return tt.setTitle(title, win.TTI_WARNING)
}

func (tt *ToolTip) SetErrorTitle(title string) error {
	return tt.setTitle(title, win.TTI_ERROR)
}

func (tt *ToolTip) setTitle(title string, icon uintptr) error {
	if len(title) > 99 {
		title = title[:99]
	}

	if win.FALSE == tt.SendMessage(win.TTM_SETTITLE, icon, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title)))) {
		return newError("TTM_SETTITLE failed")
	}

	return nil
}

func (tt *ToolTip) AddTool(tool Widget) error {
	hwnd := tool.Handle()

	var ti win.TOOLINFO
	ti.CbSize = uint32(unsafe.Sizeof(ti))
	ti.Hwnd = hwnd
	ti.UFlags = win.TTF_IDISHWND | win.TTF_SUBCLASS
	ti.UId = uintptr(hwnd)

	if win.FALSE == tt.SendMessage(win.TTM_ADDTOOL, 0, uintptr(unsafe.Pointer(&ti))) {
		return newError("TTM_ADDTOOL failed")
	}

	return nil
}

func (tt *ToolTip) RemoveTool(tool Widget) error {
	hwnd := tool.Handle()

	var ti win.TOOLINFO
	ti.CbSize = uint32(unsafe.Sizeof(ti))
	ti.Hwnd = hwnd
	ti.UId = uintptr(hwnd)

	tt.SendMessage(win.TTM_DELTOOL, 0, uintptr(unsafe.Pointer(&ti)))

	return nil
}

func (tt *ToolTip) Text(tool Widget) string {
	ti := tt.toolInfo(tool)
	if ti == nil {
		return ""
	}

	return win.UTF16PtrToString(ti.LpszText)
}

func (tt *ToolTip) SetText(tool Widget, text string) error {
	ti := tt.toolInfo(tool)
	if ti == nil {
		return newError("unknown tool")
	}

	if len(text) > 79 {
		text = text[:79]
	}

	ti.LpszText = syscall.StringToUTF16Ptr(text)

	tt.SendMessage(win.TTM_SETTOOLINFO, 0, uintptr(unsafe.Pointer(ti)))

	return nil
}

func (tt *ToolTip) toolInfo(tool Widget) *win.TOOLINFO {
	var ti win.TOOLINFO
	var buf [80]uint16

	hwnd := tool.Handle()

	ti.CbSize = uint32(unsafe.Sizeof(ti))
	ti.Hwnd = hwnd
	ti.UId = uintptr(hwnd)
	ti.LpszText = &buf[0]

	if win.FALSE == tt.SendMessage(win.TTM_GETTOOLINFO, 0, uintptr(unsafe.Pointer(&ti))) {
		return nil
	}

	return &ti
}
<commit_msg>ToolTip: allow 79 code points, not bytes for text<commit_after>// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

package walk

import (
	"syscall"
	"unsafe"
)

import (
	"github.com/lxn/win"
)

// see https://msdn.microsoft.com/en-us/library/windows/desktop/bb760416(v=vs.85).aspx
const maxToolTipTextLen = 80 // including NUL terminator

func init() {
	var err error
	if globalToolTip, err = NewToolTip(); err != nil {
		panic(err)
	}
}

type ToolTip struct {
	WindowBase
}

var globalToolTip *ToolTip

func NewToolTip() (*ToolTip, error) {
	tt := new(ToolTip)

	if err := InitWindow(
		tt,
		nil,
		"tooltips_class32",
		win.WS_POPUP|win.TTS_ALWAYSTIP,
		win.WS_EX_TOPMOST); err != nil {
		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			tt.Dispose()
		}
	}()

	win.SetWindowPos(tt.hWnd, win.HWND_TOPMOST, 0, 0, 0, 0, win.SWP_NOMOVE|win.SWP_NOSIZE|win.SWP_NOACTIVATE)

	succeeded = true

	return tt, nil
}

func (*ToolTip) LayoutFlags() LayoutFlags {
	return 0
}

func (tt *ToolTip) SizeHint() Size {
	return Size{0, 0}
}

func (tt *ToolTip) Title() string {
	var gt win.TTGETTITLE

	buf := make([]uint16, 100)

	gt.DwSize = uint32(unsafe.Sizeof(gt))
	gt.Cch = uint32(len(buf))
	gt.PszTitle = &buf[0]

	tt.SendMessage(win.TTM_GETTITLE, 0, uintptr(unsafe.Pointer(&gt)))

	return syscall.UTF16ToString(buf)
}

func (tt *ToolTip) SetTitle(title string) error {
	return tt.setTitle(title, win.TTI_NONE)
}

func (tt *ToolTip) SetInfoTitle(title string) error {
	return tt.setTitle(title, win.TTI_INFO)
}

func (tt *ToolTip) SetWarningTitle(title string) error {
	return tt.setTitle(title, win.TTI_WARNING)
}

func (tt *ToolTip) SetErrorTitle(title string) error {
	return tt.setTitle(title, win.TTI_ERROR)
}

func (tt *ToolTip) setTitle(title string, icon uintptr) error {
	if len(title) > 99 {
		title = title[:99]
	}

	if win.FALSE == tt.SendMessage(win.TTM_SETTITLE, icon, uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title)))) {
		return newError("TTM_SETTITLE failed")
	}

	return nil
}

func (tt *ToolTip) AddTool(tool Widget) error {
	hwnd := tool.Handle()

	var ti win.TOOLINFO
	ti.CbSize = uint32(unsafe.Sizeof(ti))
	ti.Hwnd = hwnd
	ti.UFlags = win.TTF_IDISHWND | win.TTF_SUBCLASS
	ti.UId = uintptr(hwnd)

	if win.FALSE == tt.SendMessage(win.TTM_ADDTOOL, 0, uintptr(unsafe.Pointer(&ti))) {
		return newError("TTM_ADDTOOL failed")
	}

	return nil
}

func (tt *ToolTip) RemoveTool(tool Widget) error {
	hwnd := tool.Handle()

	var ti win.TOOLINFO
	ti.CbSize = uint32(unsafe.Sizeof(ti))
	ti.Hwnd = hwnd
	ti.UId = uintptr(hwnd)

	tt.SendMessage(win.TTM_DELTOOL, 0, uintptr(unsafe.Pointer(&ti)))

	return nil
}

func (tt *ToolTip) Text(tool Widget) string {
	ti := tt.toolInfo(tool)
	if ti == nil {
		return ""
	}

	return win.UTF16PtrToString(ti.LpszText)
}

func (tt *ToolTip) SetText(tool Widget, text string) error {
	ti := tt.toolInfo(tool)
	if ti == nil {
		return newError("unknown tool")
	}

	n := 0
	for i, r := range text {
		if r < 0x10000 {
			n++
		} else {
			n += 2 // surrogate pair
		}
		if n >= maxToolTipTextLen {
			text = text[:i]
			break
		}
	}

	ti.LpszText = syscall.StringToUTF16Ptr(text)

	tt.SendMessage(win.TTM_SETTOOLINFO, 0, uintptr(unsafe.Pointer(ti)))

	return nil
}

func (tt *ToolTip) toolInfo(tool Widget) *win.TOOLINFO {
	var ti win.TOOLINFO
	var buf [maxToolTipTextLen]uint16

	hwnd := tool.Handle()

	ti.CbSize = uint32(unsafe.Sizeof(ti))
	ti.Hwnd = hwnd
	ti.UId = uintptr(hwnd)
	ti.LpszText = &buf[0]

	if win.FALSE == tt.SendMessage(win.TTM_GETTOOLINFO, 0, uintptr(unsafe.Pointer(&ti))) {
		return nil
	}

	return &ti
}
<|endoftext|>


<commit_before>// Package totango provides bindings for the Totango server side integration API
package totango

import (
	"errors"
	"net/http"
	"net/url"
)

const (
	// The base URL for all Totango API requests
	baseURL = "https://sdr.totango.com/pixel.gif/?sdr_s="
)

// The data for an API request
type request struct {
	accountID   string
	accountName string
	userName    string
	activity    string
	module      string
	attributes  map[string]string
}

func encode(s string) string {
	return url.QueryEscape(s)
}

// Construct a URL query param string from the provided fields
func (r *request) String() string {
	var url string

	switch {
	case r.accountID != "":
		url += "&sdr_o=" + encode(r.accountID)
	case r.accountName != "":
		url += "&sdr_odn=" + encode(r.accountName)
	case r.userName != "":
		url += "&sdr_u=" + encode(r.userName)
	case r.activity != "":
		url += "&sdr_a=" + encode(r.activity)
	case r.module != "":
		url += "&sdr_m=" + encode(r.module)
	case len(r.attributes) > 0:
		for name, val := range r.attributes {
			url += "&sdr_o." + encode(name) + "=" + encode(val)
		}
	}

	return url
}

type Tracker struct {
	serviceID string
}

// Construct a Totango API request from a request type
func (t *Tracker) getURL(r *request) string {
	return baseURL + t.ServiceID + r.String()
}

func NewTracker(serviceID string) (*Tracker, error) {
	if serviceID == "" {
		return nil, errors.New("Tracker requires a valid Totango Service ID")
	}

	return &Tracker{serviceID: serviceID}, nil
}

func (t *Tracker) Track(accountID, accountName, userName, activity, module string) (*http.Response, error) {
	r := &request{
		accountID:   accountID,
		accountName: accountName,
		userName:    userName,
		activity:    activity,
		module:      module,
	}

	return http.Get(t.getURL(r))
}

func (t *Tracker) TrackAttribute(accountID, userName, name, value string) (*http.Response, error) {
	r := &request{
		accountID:  accountID,
		userName:   userName,
		attributes: map[string]string{name: val},
	}

	return http.Get(t.getURL(r))
}

func (t *Tracker) TrackAttributes(accountID, userName string, attributes map[string]string) (*http.Response, error) {
	r := &request{
		accountID:  accountID,
		userName:   userName,
		attributes: attributes,
	}

	return http.Get(t.getURL(r))
}
<commit_msg>Fix another bug<commit_after>// Package totango provides bindings for the Totango server side integration API
package totango

import (
	"errors"
	"net/http"
	"net/url"
)

const (
	// The base URL for all Totango API requests
	baseURL = "https://sdr.totango.com/pixel.gif/?sdr_s="
)

// The data for an API request
type request struct {
	accountID   string
	accountName string
	userName    string
	activity    string
	module      string
	attributes  map[string]string
}

func encode(s string) string {
	return url.QueryEscape(s)
}

// Construct a URL query param string from the provided fields
func (r *request) String() string {
	var url string

	switch {
	case r.accountID != "":
		url += "&sdr_o=" + encode(r.accountID)
	case r.accountName != "":
		url += "&sdr_odn=" + encode(r.accountName)
	case r.userName != "":
		url += "&sdr_u=" + encode(r.userName)
	case r.activity != "":
		url += "&sdr_a=" + encode(r.activity)
	case r.module != "":
		url += "&sdr_m=" + encode(r.module)
	case len(r.attributes) > 0:
		for name, val := range r.attributes {
			url += "&sdr_o." + encode(name) + "=" + encode(val)
		}
	}

	return url
}

type Tracker struct {
	serviceID string
}

// Construct a Totango API request from a request type
func (t *Tracker) getURL(r *request) string {
	return baseURL + t.serviceID + r.String()
}

func NewTracker(serviceID string) (*Tracker, error) {
	if serviceID == "" {
		return nil, errors.New("Tracker requires a valid Totango Service ID")
	}

	return &Tracker{serviceID: serviceID}, nil
}

func (t *Tracker) Track(accountID, accountName, userName, activity, module string) (*http.Response, error) {
	r := &request{
		accountID:   accountID,
		accountName: accountName,
		userName:    userName,
		activity:    activity,
		module:      module,
	}

	return http.Get(t.getURL(r))
}

func (t *Tracker) TrackAttribute(accountID, userName, name, value string) (*http.Response, error) {
	r := &request{
		accountID:  accountID,
		userName:   userName,
		attributes: map[string]string{name: value},
	}

	return http.Get(t.getURL(r))
}

func (t *Tracker) TrackAttributes(accountID, userName string, attributes map[string]string) (*http.Response, error) {
	r := &request{
		accountID:  accountID,
		userName:   userName,
		attributes: attributes,
	}

	return http.Get(t.getURL(r))
}
<|endoftext|>


<commit_before>// Package tracerx implements a simple tracer function that uses environment
// variables to control the output. It is a generalized package inspired by
// git's GIT_TRACE mechanism.
//
// By default, tracerx will look for the TRACERX_TRACE environment variable.
// The default can by changed by setting the DefaultKey.
//
// The values control where the tracing is output as follows:
//     unset, 0, or "false":   no output
//     1, 2:                   stderr
//     absolute path:          output will be written to the file
//     3 - 10:                 output will be written to that file descriptor
//
// By default, messages will be prefixed with "trace: ". This prefix can be
// modified by setting Prefix.
//
// Each key can have an associated performance key, e.g. TRACERX_TRACE_PERFORMANCE.
// If this key is 1 or "true" performance output will be written to the same output
// as the tracing output.
package tracerx

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	DefaultKey = "TRACERX"
	Prefix     = "trace: "
	tracers    map[string]*tracer
	tracerLock sync.Mutex
)

type tracer struct {
	enabled     bool
	performance bool
	w           io.Writer
}

// Printf writes a trace message for the DefaultKey
func Printf(format string, args ...interface{}) {
	PrintfKey(DefaultKey, format, args...)
}

// PrintfKey writes a trace message for the given key
func PrintfKey(key, format string, args ...interface{}) {
	tracer := getTracer(key)
	if tracer.enabled {
		fmt.Fprintf(tracer.w, time.Now().Format("15:04:05.999999 ")+Prefix+format+"\n", args...)
		return
	}
}

// PerformanceSince writes out the time since the given time, if
// tracing for the default key is enabled and the performance key is set
func PerformanceSince(what string, t time.Time) {
	PerformanceSinceKey(DefaultKey, what, t)
}

// PerformanceSince writes out the time since the given time, if
// tracing for the given key is enabled and the performance key is set
func PerformanceSinceKey(key, what string, t time.Time) {
	tracer := getTracer(key)

	if tracer.performance {
		since := time.Since(t)
		fmt.Fprintf(tracer.w, time.Now().Format("15:04:05.999999 ")+"performance %s: %.9f s\n", what, since.Seconds())
	}
}

// Disable will disable tracing for the given key, regardless of
// the environment variable
func Disable(key string) {
	uppedKey := strings.ToUpper(key)
	if tracer, ok := tracers[uppedKey]; ok {
		tracer.enabled = false
	}
}

// Enable will enable tracing for the given key, regardless of
// the environment variable
func Enable(key string) {
	uppedKey := strings.ToUpper(key)
	if tracer, ok := tracers[uppedKey]; ok {
		tracer.enabled = true
	}
}

func getTracer(key string) *tracer {
	uppedKey := strings.ToUpper(key)

	tracerLock.Lock()
	tracer, ok := tracers[uppedKey]
	if !ok {
		tracer = initializeTracer(uppedKey)
	}
	tracerLock.Unlock()
	return tracer
}

func initializeTracer(key string) *tracer {
	if tracer, ok := tracers[key]; ok {
		return tracer // Someone else initialized while we were blocked
	}

	tracer := &tracer{false, false, os.Stderr}
	tracers[key] = tracer

	perf := os.Getenv(fmt.Sprintf("%s_TRACE_PERFORMANCE", key))
	if perf == "1" || strings.ToLower(perf) == "true" {
		tracer.performance = true
	}

	trace := os.Getenv(fmt.Sprintf("%s_TRACE", key))
	fd, err := strconv.Atoi(trace)
	if err != nil {
		// Not a number, it could be a path for a log file
		if filepath.IsAbs(trace) {
			tracerOut, err := os.OpenFile(trace, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Could not open '%s' for tracing: %s\nDefaulting to tracing on stderr...\n", trace, err)
				tracerOut = os.Stderr
			}
			tracer.w = tracerOut
			tracer.enabled = true
		} else if strings.ToLower(trace) == "true" {
			tracer.enabled = true
		}
	} else {
		switch fd {
		case 0:
		case 1, 2:
			tracer.enabled = true
		default:
			tracer.w = os.NewFile(uintptr(fd), "trace")
			tracer.enabled = true
		}
	}

	return tracer
}

func init() {
	tracers = make(map[string]*tracer, 0)
}
<commit_msg>keep 0 padding<commit_after>// Package tracerx implements a simple tracer function that uses environment
// variables to control the output. It is a generalized package inspired by
// git's GIT_TRACE mechanism.
//
// By default, tracerx will look for the TRACERX_TRACE environment variable.
// The default can by changed by setting the DefaultKey.
//
// The values control where the tracing is output as follows:
//     unset, 0, or "false":   no output
//     1, 2:                   stderr
//     absolute path:          output will be written to the file
//     3 - 10:                 output will be written to that file descriptor
//
// By default, messages will be prefixed with "trace: ". This prefix can be
// modified by setting Prefix.
//
// Each key can have an associated performance key, e.g. TRACERX_TRACE_PERFORMANCE.
// If this key is 1 or "true" performance output will be written to the same output
// as the tracing output.
package tracerx

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	DefaultKey = "TRACERX"
	Prefix     = "trace: "
	tracers    map[string]*tracer
	tracerLock sync.Mutex
)

type tracer struct {
	enabled     bool
	performance bool
	w           io.Writer
}

// Printf writes a trace message for the DefaultKey
func Printf(format string, args ...interface{}) {
	PrintfKey(DefaultKey, format, args...)
}

// PrintfKey writes a trace message for the given key
func PrintfKey(key, format string, args ...interface{}) {
	tracer := getTracer(key)
	if tracer.enabled {
		fmt.Fprintf(tracer.w, time.Now().Format("15:04:05.000000 ")+Prefix+format+"\n", args...)
		return
	}
}

// PerformanceSince writes out the time since the given time, if
// tracing for the default key is enabled and the performance key is set
func PerformanceSince(what string, t time.Time) {
	PerformanceSinceKey(DefaultKey, what, t)
}

// PerformanceSince writes out the time since the given time, if
// tracing for the given key is enabled and the performance key is set
func PerformanceSinceKey(key, what string, t time.Time) {
	tracer := getTracer(key)

	if tracer.performance {
		since := time.Since(t)
		fmt.Fprintf(tracer.w, time.Now().Format("15:04:05.000000 ")+"performance %s: %.9f s\n", what, since.Seconds())
	}
}

// Disable will disable tracing for the given key, regardless of
// the environment variable
func Disable(key string) {
	uppedKey := strings.ToUpper(key)
	if tracer, ok := tracers[uppedKey]; ok {
		tracer.enabled = false
	}
}

// Enable will enable tracing for the given key, regardless of
// the environment variable
func Enable(key string) {
	uppedKey := strings.ToUpper(key)
	if tracer, ok := tracers[uppedKey]; ok {
		tracer.enabled = true
	}
}

func getTracer(key string) *tracer {
	uppedKey := strings.ToUpper(key)

	tracerLock.Lock()
	tracer, ok := tracers[uppedKey]
	if !ok {
		tracer = initializeTracer(uppedKey)
	}
	tracerLock.Unlock()
	return tracer
}

func initializeTracer(key string) *tracer {
	if tracer, ok := tracers[key]; ok {
		return tracer // Someone else initialized while we were blocked
	}

	tracer := &tracer{false, false, os.Stderr}
	tracers[key] = tracer

	perf := os.Getenv(fmt.Sprintf("%s_TRACE_PERFORMANCE", key))
	if perf == "1" || strings.ToLower(perf) == "true" {
		tracer.performance = true
	}

	trace := os.Getenv(fmt.Sprintf("%s_TRACE", key))
	fd, err := strconv.Atoi(trace)
	if err != nil {
		// Not a number, it could be a path for a log file
		if filepath.IsAbs(trace) {
			tracerOut, err := os.OpenFile(trace, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Could not open '%s' for tracing: %s\nDefaulting to tracing on stderr...\n", trace, err)
				tracerOut = os.Stderr
			}
			tracer.w = tracerOut
			tracer.enabled = true
		} else if strings.ToLower(trace) == "true" {
			tracer.enabled = true
		}
	} else {
		switch fd {
		case 0:
		case 1, 2:
			tracer.enabled = true
		default:
			tracer.w = os.NewFile(uintptr(fd), "trace")
			tracer.enabled = true
		}
	}

	return tracer
}

func init() {
	tracers = make(map[string]*tracer, 0)
}
<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2016 Sascha Hanse
Copyright (c) 2017 Shinya Yagyu

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package giota

import (
	"errors"
	"fmt"
	"strings"
	"unsafe"
)

var (
	tryteToTritsMappings = [][]int8{
		[]int8{0, 0, 0}, []int8{1, 0, 0}, []int8{-1, 1, 0}, []int8{0, 1, 0},
		[]int8{1, 1, 0}, []int8{-1, -1, 1}, []int8{0, -1, 1}, []int8{1, -1, 1},
		[]int8{-1, 0, 1}, []int8{0, 0, 1}, []int8{1, 0, 1}, []int8{-1, 1, 1},
		[]int8{0, 1, 1}, []int8{1, 1, 1}, []int8{-1, -1, -1}, []int8{0, -1, -1},
		[]int8{1, -1, -1}, []int8{-1, 0, -1}, []int8{0, 0, -1}, []int8{1, 0, -1},
		[]int8{-1, 1, -1}, []int8{0, 1, -1}, []int8{1, 1, -1}, []int8{-1, -1, 0},
		[]int8{0, -1, 0}, []int8{1, -1, 0}, []int8{-1, 0, 0},
	}
)

// Trits is a slice of int8. You should not use cast, use ToTrits instead to ensure
// the validity.
type Trits []int8

// ToTrits casts Trits and checks its validity.
func ToTrits(t []int8) (Trits, error) {
	tr := Trits(t)
	err := tr.IsValid()
	return tr, err
}

// IsValidTrit returns true if t is a valid trit.
func IsValidTrit(t int8) error {
	if t >= -1 && t <= 1 {
		return nil
	}
	return errors.New("invalid number")
}

// IsValid returns true if t is valid trits.
func (t Trits) IsValid() error {
	for _, tt := range t {
		if err := IsValidTrit(tt); err != nil {
			return fmt.Errorf("%s in trits", err)
		}
	}
	return nil
}

// Equal returns true if t and b are equal Trits
func (t Trits) Equal(b Trits) bool {
	if len(t) != len(b) {
		return false
	}

	for i := range t {
		if t[i] != b[i] {
			return false
		}
	}
	return true
}

// Int2Trits converts int64 to trits.
func Int2Trits(v int64, size int) Trits {
	tr := make(Trits, size)
	neg := false
	if v < 0 {
		v = -v
		neg = true
	}

	for i := 0; v != 0 && i < size; i++ {
		tr[i] = int8((v+1)%Radix) - 1

		if neg {
			tr[i] = -tr[i]
		}

		v = (v + 1) / Radix
	}
	return tr
}

// Int converts a slice of trits into an integer and assumes little-endian notation.
func (t Trits) Int() int64 {
	var val int64
	for i := len(t) - 1; i >= 0; i-- {
		val = val*3 + int64(t[i])
	}
	return val
}

// CanTrytes returns true if t can be converted to trytes.
func (t Trits) CanTrytes() bool {
	return len(t)%3 == 0
}

// TrailingZeros returns the number of trailing zeros of the given trits.
func (t Trits) TrailingZeros() int64 {
	z := int64(0)
	for i := len(t) - 1; i >= 0 && t[i] == 0; i-- {
		z++
	}
	return z
}

// Trytes converts a slice of trits into trytes. panics if len(t)%3!=0
func (t Trits) Trytes() Trytes {
	if !t.CanTrytes() {
		panic("length of trits must be a multiple of three")
	}

	o := make([]byte, len(t)/3)
	for i := 0; i < len(t)/3; i++ {
		j := t[i*3] + t[i*3+1]*3 + t[i*3+2]*9
		if j < 0 {
			j += int8(len(TryteAlphabet))
		}
		o[i] = TryteAlphabet[j]
	}
	return Trytes(o)
}

// constants regarding byte and trit lengths
const (
	ByteLength     = 48
	TritHashLength = 243
	IntLength      = ByteLength / 4
)

// 3^(242/2)
// 12 * 32 bit
var halfThree = []uint32{
	0xa5ce8964,
	0x9f007669,
	0x1484504f,
	0x3ade00d9,
	0x0c24486e,
	0x50979d57,
	0x79a4c702,
	0x48bbae36,
	0xa9f6808b,
	0xaa06a805,
	0xa87fabdf,
	0x5e69ebef,
}

// IsValidLength returns the validity of the trit length
func (t Trits) IsValidLength() bool {
	return len(t) != TritHashLength
}

// Bytes is only defined for hashes, i.e. slices of trits of length 243. It returns 48 bytes.
// nolint: gocyclo, gas
func (t Trits) Bytes() ([]byte, error) {
	if t.IsValidLength() {
		return nil, fmt.Errorf("Bytes() is only defined for trit slices of length %d", TritHashLength)
	}

	allNeg := true
	for _, e := range t[0 : TritHashLength-1] { // Last position should be always zero.
		if e != -1 {
			allNeg = false
			break
		}
	}

	// Trit to BigInt
	b := make([]byte, 48) // 48 bytes/384 bits

	// 12 * 32 bits = 384 bits
	base := (*(*[]uint32)(unsafe.Pointer(&b)))[0:IntLength]

	if allNeg {
		// If all trits are -1 then we're half way through all the numbers,
		// since they're in two's complement notation.
		copy(base, halfThree)

		// Compensate for setting the last position to zero.
		bigIntNot(base)
		bigIntAddSmall(base, 1)

		return reverse(b), nil
	}

	revT := make([]int8, len(t))
	copy(revT, t)
	size := 1

	for _, e := range reverseT(revT[0 : TritHashLength-1]) {
		sz := size
		var carry uint32
		for j := 0; j < sz; j++ {
			v := uint64(base[j])*uint64(Radix) + uint64(carry)
			carry = uint32(v >> 32)
			base[j] = uint32(v)
		}

		if carry > 0 {
			base[sz] = carry
			size = size + 1
		}

		trit := uint32(e + 1)

		ns := bigIntAddSmall(base, trit)
		if ns > size {
			size = ns
		}
	}

	if !bigIntIsNull(base) {
		if bigIntCmp(halfThree, base) <= 0 {
			// base >= HALF_3
			// just do base - HALF_3
			bigIntSub(base, halfThree)
		} else {
			// we don't have a wrapping sub.
			// so let's use some bit magic to achieve it
			tmp := make([]uint32, IntLength)
			copy(tmp, halfThree)
			bigIntSub(tmp, base)
			bigIntNot(tmp)
			bigIntAddSmall(tmp, 1)
			copy(base, tmp)
		}
	}
	return reverse(b), nil
}

// BytesToTrits converts binary to ternay
func BytesToTrits(b []byte) (Trits, error) {
	if len(b) != ByteLength {
		return nil, fmt.Errorf("BytesToTrits() is only defined for byte slices of length %d", ByteLength)
	}

	rb := make([]byte, len(b))
	copy(rb, b)
	reverse(rb)

	t := Trits(make([]int8, TritHashLength))
	t[TritHashLength-1] = 0

	// nolint: gas
	base := (*(*[]uint32)(unsafe.Pointer(&rb)))[0:IntLength] // 12 * 32 bits = 384 bits

	if bigIntIsNull(base) {
		return t, nil
	}

	var flipTrits bool

	// Check if the MSB is 0, i.e. we have a positive number
	// nolint: gas
	msbM := (unsafe.Sizeof(base[IntLength-1]) * 8) - 1

	switch {
	case base[IntLength-1]>>msbM == 0:
		bigIntAdd(base, halfThree)
	default:
		bigIntNot(base)
		if bigIntCmp(base, halfThree) == 1 {
			bigIntSub(base, halfThree)
			flipTrits = true
		} else {
			bigIntAddSmall(base, 1)
			tmp := make([]uint32, IntLength)
			copy(tmp, halfThree)
			bigIntSub(tmp, base)
			copy(base, tmp)
		}
	}

	var rem uint64
	for i := range t[0 : TritHashLength-1] {
		rem = 0
		for j := IntLength - 1; j >= 0; j++ {
			lhs := (rem << 32) | uint64(base[j])
			rhs := uint64(Radix)
			q := uint32(lhs / rhs)
			r := uint32(lhs % rhs)
			base[j] = q
			rem = uint64(r)
		}
		t[i] = int8(rem) - 1
	}

	if flipTrits {
		for i := range t {
			t[i] = -t[i]
		}
	}

	return t, nil
}

func reverseT(a Trits) Trits {
	for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
		a[left], a[right] = a[right], a[left]
	}

	return a
}

// Trytes is a string of trytes. You should not typecast, use ToTrytes instead to be safe
type Trytes string

// ToTrytes casts to Trytes and checks its validity.
func ToTrytes(t string) (Trytes, error) {
	tr := Trytes(t)
	err := tr.IsValid()
	return tr, err
}

// Trits converts a slice of trytes into trits,
func (t Trytes) Trits() Trits {
	trits := make(Trits, len(t)*3)
	for i := range t {
		idx := strings.Index(TryteAlphabet, string(t[i:i+1]))
		copy(trits[i*3:i*3+3], tryteToTritsMappings[idx])
	}
	return trits
}

// Normalize normalized bits into trits so that the sum of trits TODO: (and?) bits is zero.
// nolint: gocyclo
func (t Trytes) Normalize() []int8 {
	normalized := make([]int8, len(t))
	sum := 0
	for i := 0; i < 3; i++ {
		for j := 0; j < 27; j++ {
			normalized[i*27+j] = int8(t[i*27+j : i*27+j+1].Trits().Int())
			sum += int(normalized[i*27+j])
		}

		switch {
		case sum >= 0:
			for ; sum > 0; sum-- {
				for j := 0; j < 27; j++ {
					if normalized[i*27+j] > -13 {
						normalized[i*27+j]--
						break
					}
				}
			}
		default:
			for ; sum < 0; sum++ {
				for j := 0; j < 27; j++ {
					if normalized[i*27+j] < 13 {
						normalized[i*27+j]++
						break
					}
				}
			}
		}
	}
	return normalized
}

// IsValidTryte returns the validity of a tryte( must be rune A-Z or 9 )
func IsValidTryte(t rune) error {
	if ('A' <= t && t <= 'Z') || t == '9' {
		return nil
	}
	return errors.New("invalid character")
}

// IsValid returns true if t is made of valid trytes.
func (t Trytes) IsValid() error {
	for _, t := range t {
		if err := IsValidTryte(t); err != nil {
			return fmt.Errorf("%s in trytes", err)
		}
	}
	return nil
}

func incTrits(t Trits) {
	for j := range t {
		t[j]++
		switch {
		case t[j] > 1:
			t[j] = -1
		default:
			break
		}
	}
}
<commit_msg>Fix bug in loop<commit_after>/*
MIT License

Copyright (c) 2016 Sascha Hanse
Copyright (c) 2017 Shinya Yagyu

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package giota

import (
	"errors"
	"fmt"
	"strings"
	"unsafe"
)

var (
	tryteToTritsMappings = [][]int8{
		[]int8{0, 0, 0}, []int8{1, 0, 0}, []int8{-1, 1, 0}, []int8{0, 1, 0},
		[]int8{1, 1, 0}, []int8{-1, -1, 1}, []int8{0, -1, 1}, []int8{1, -1, 1},
		[]int8{-1, 0, 1}, []int8{0, 0, 1}, []int8{1, 0, 1}, []int8{-1, 1, 1},
		[]int8{0, 1, 1}, []int8{1, 1, 1}, []int8{-1, -1, -1}, []int8{0, -1, -1},
		[]int8{1, -1, -1}, []int8{-1, 0, -1}, []int8{0, 0, -1}, []int8{1, 0, -1},
		[]int8{-1, 1, -1}, []int8{0, 1, -1}, []int8{1, 1, -1}, []int8{-1, -1, 0},
		[]int8{0, -1, 0}, []int8{1, -1, 0}, []int8{-1, 0, 0},
	}
)

// Trits is a slice of int8. You should not use cast, use ToTrits instead to ensure
// the validity.
type Trits []int8

// ToTrits casts Trits and checks its validity.
func ToTrits(t []int8) (Trits, error) {
	tr := Trits(t)
	err := tr.IsValid()
	return tr, err
}

// IsValidTrit returns true if t is a valid trit.
func IsValidTrit(t int8) error {
	if t >= -1 && t <= 1 {
		return nil
	}
	return errors.New("invalid number")
}

// IsValid returns true if t is valid trits.
func (t Trits) IsValid() error {
	for _, tt := range t {
		if err := IsValidTrit(tt); err != nil {
			return fmt.Errorf("%s in trits", err)
		}
	}
	return nil
}

// Equal returns true if t and b are equal Trits
func (t Trits) Equal(b Trits) bool {
	if len(t) != len(b) {
		return false
	}

	for i := range t {
		if t[i] != b[i] {
			return false
		}
	}
	return true
}

// Int2Trits converts int64 to trits.
func Int2Trits(v int64, size int) Trits {
	tr := make(Trits, size)
	neg := false
	if v < 0 {
		v = -v
		neg = true
	}

	for i := 0; v != 0 && i < size; i++ {
		tr[i] = int8((v+1)%Radix) - 1

		if neg {
			tr[i] = -tr[i]
		}

		v = (v + 1) / Radix
	}
	return tr
}

// Int converts a slice of trits into an integer and assumes little-endian notation.
func (t Trits) Int() int64 {
	var val int64
	for i := len(t) - 1; i >= 0; i-- {
		val = val*3 + int64(t[i])
	}
	return val
}

// CanTrytes returns true if t can be converted to trytes.
func (t Trits) CanTrytes() bool {
	return len(t)%3 == 0
}

// TrailingZeros returns the number of trailing zeros of the given trits.
func (t Trits) TrailingZeros() int64 {
	z := int64(0)
	for i := len(t) - 1; i >= 0 && t[i] == 0; i-- {
		z++
	}
	return z
}

// Trytes converts a slice of trits into trytes. panics if len(t)%3!=0
func (t Trits) Trytes() Trytes {
	if !t.CanTrytes() {
		panic("length of trits must be a multiple of three")
	}

	o := make([]byte, len(t)/3)
	for i := 0; i < len(t)/3; i++ {
		j := t[i*3] + t[i*3+1]*3 + t[i*3+2]*9
		if j < 0 {
			j += int8(len(TryteAlphabet))
		}
		o[i] = TryteAlphabet[j]
	}
	return Trytes(o)
}

// constants regarding byte and trit lengths
const (
	ByteLength     = 48
	TritHashLength = 243
	IntLength      = ByteLength / 4
)

// 3^(242/2)
// 12 * 32 bit
var halfThree = []uint32{
	0xa5ce8964,
	0x9f007669,
	0x1484504f,
	0x3ade00d9,
	0x0c24486e,
	0x50979d57,
	0x79a4c702,
	0x48bbae36,
	0xa9f6808b,
	0xaa06a805,
	0xa87fabdf,
	0x5e69ebef,
}

// IsValidLength returns the validity of the trit length
func (t Trits) IsValidLength() bool {
	return len(t) != TritHashLength
}

// Bytes is only defined for hashes, i.e. slices of trits of length 243. It returns 48 bytes.
// nolint: gocyclo, gas
func (t Trits) Bytes() ([]byte, error) {
	if t.IsValidLength() {
		return nil, fmt.Errorf("Bytes() is only defined for trit slices of length %d", TritHashLength)
	}

	allNeg := true
	for _, e := range t[0 : TritHashLength-1] { // Last position should be always zero.
		if e != -1 {
			allNeg = false
			break
		}
	}

	// Trit to BigInt
	b := make([]byte, 48) // 48 bytes/384 bits

	// 12 * 32 bits = 384 bits
	base := (*(*[]uint32)(unsafe.Pointer(&b)))[0:IntLength]

	if allNeg {
		// If all trits are -1 then we're half way through all the numbers,
		// since they're in two's complement notation.
		copy(base, halfThree)

		// Compensate for setting the last position to zero.
		bigIntNot(base)
		bigIntAddSmall(base, 1)

		return reverse(b), nil
	}

	revT := make([]int8, len(t))
	copy(revT, t)
	size := 1

	for _, e := range reverseT(revT[0 : TritHashLength-1]) {
		sz := size
		var carry uint32
		for j := 0; j < sz; j++ {
			v := uint64(base[j])*uint64(Radix) + uint64(carry)
			carry = uint32(v >> 32)
			base[j] = uint32(v)
		}

		if carry > 0 {
			base[sz] = carry
			size = size + 1
		}

		trit := uint32(e + 1)

		ns := bigIntAddSmall(base, trit)
		if ns > size {
			size = ns
		}
	}

	if !bigIntIsNull(base) {
		if bigIntCmp(halfThree, base) <= 0 {
			// base >= HALF_3
			// just do base - HALF_3
			bigIntSub(base, halfThree)
		} else {
			// we don't have a wrapping sub.
			// so let's use some bit magic to achieve it
			tmp := make([]uint32, IntLength)
			copy(tmp, halfThree)
			bigIntSub(tmp, base)
			bigIntNot(tmp)
			bigIntAddSmall(tmp, 1)
			copy(base, tmp)
		}
	}
	return reverse(b), nil
}

// BytesToTrits converts binary to ternay
func BytesToTrits(b []byte) (Trits, error) {
	if len(b) != ByteLength {
		return nil, fmt.Errorf("BytesToTrits() is only defined for byte slices of length %d", ByteLength)
	}

	rb := make([]byte, len(b))
	copy(rb, b)
	reverse(rb)

	t := Trits(make([]int8, TritHashLength))
	t[TritHashLength-1] = 0

	// nolint: gas
	base := (*(*[]uint32)(unsafe.Pointer(&rb)))[0:IntLength] // 12 * 32 bits = 384 bits

	if bigIntIsNull(base) {
		return t, nil
	}

	var flipTrits bool

	// Check if the MSB is 0, i.e. we have a positive number
	// nolint: gas
	msbM := (unsafe.Sizeof(base[IntLength-1]) * 8) - 1

	switch {
	case base[IntLength-1]>>msbM == 0:
		bigIntAdd(base, halfThree)
	default:
		bigIntNot(base)
		if bigIntCmp(base, halfThree) == 1 {
			bigIntSub(base, halfThree)
			flipTrits = true
		} else {
			bigIntAddSmall(base, 1)
			tmp := make([]uint32, IntLength)
			copy(tmp, halfThree)
			bigIntSub(tmp, base)
			copy(base, tmp)
		}
	}

	var rem uint64
	for i := range t[0 : TritHashLength-1] {
		rem = 0
		for j := IntLength - 1; j >= 0; j-- {
			lhs := (rem << 32) | uint64(base[j])
			rhs := uint64(Radix)
			q := uint32(lhs / rhs)
			r := uint32(lhs % rhs)
			base[j] = q
			rem = uint64(r)
		}
		t[i] = int8(rem) - 1
	}

	if flipTrits {
		for i := range t {
			t[i] = -t[i]
		}
	}

	return t, nil
}

func reverseT(a Trits) Trits {
	for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
		a[left], a[right] = a[right], a[left]
	}

	return a
}

// Trytes is a string of trytes. You should not typecast, use ToTrytes instead to be safe
type Trytes string

// ToTrytes casts to Trytes and checks its validity.
func ToTrytes(t string) (Trytes, error) {
	tr := Trytes(t)
	err := tr.IsValid()
	return tr, err
}

// Trits converts a slice of trytes into trits,
func (t Trytes) Trits() Trits {
	trits := make(Trits, len(t)*3)
	for i := range t {
		idx := strings.Index(TryteAlphabet, string(t[i:i+1]))
		copy(trits[i*3:i*3+3], tryteToTritsMappings[idx])
	}
	return trits
}

// Normalize normalized bits into trits so that the sum of trits TODO: (and?) bits is zero.
// nolint: gocyclo
func (t Trytes) Normalize() []int8 {
	normalized := make([]int8, len(t))
	sum := 0
	for i := 0; i < 3; i++ {
		for j := 0; j < 27; j++ {
			normalized[i*27+j] = int8(t[i*27+j : i*27+j+1].Trits().Int())
			sum += int(normalized[i*27+j])
		}

		switch {
		case sum >= 0:
			for ; sum > 0; sum-- {
				for j := 0; j < 27; j++ {
					if normalized[i*27+j] > -13 {
						normalized[i*27+j]--
						break
					}
				}
			}
		default:
			for ; sum < 0; sum++ {
				for j := 0; j < 27; j++ {
					if normalized[i*27+j] < 13 {
						normalized[i*27+j]++
						break
					}
				}
			}
		}
	}
	return normalized
}

// IsValidTryte returns the validity of a tryte( must be rune A-Z or 9 )
func IsValidTryte(t rune) error {
	if ('A' <= t && t <= 'Z') || t == '9' {
		return nil
	}
	return errors.New("invalid character")
}

// IsValid returns true if t is made of valid trytes.
func (t Trytes) IsValid() error {
	for _, t := range t {
		if err := IsValidTryte(t); err != nil {
			return fmt.Errorf("%s in trytes", err)
		}
	}
	return nil
}

func incTrits(t Trits) {
	for j := range t {
		t[j]++
		switch {
		case t[j] > 1:
			t[j] = -1
		default:
			break
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"time"

	"github.com/wushilin/threads"
)

var auth string
var search string
var tCount int
var hxwstore bool
var current_page = 1
var current_download = 1
var page_limit = -1
var minWidth = -1
var minHeight = -1

var base_folder string = "./Output"
var thread_pool *threads.ThreadPool

type WallPaperJSON struct {
	Success    bool `json:"success"`
	Wallpapers []struct {
		ID       string `json:"id"`
		Width    string `json:"width"`
		Height   string `json:"height"`
		FileType string `json:"file_type"`
		FileSize string `json:"file_size"`
		URLImage string `json:"url_image"`
		URLThumb string `json:"url_thumb"`
		URLPage  string `json:"url_page"`
	} `json:"wallpapers"`
	TotalMatch string `json:"total_match"`
}

func main() {
	flag.StringVar(&auth, "auth", "", "API key from AlphaCoders")
	flag.StringVar(&search, "search", "", "Search key for wallpapers to download")
	flag.StringVar(&base_folder, "output", "./Output", "Output folder for images")
	flag.IntVar(&page_limit, "pl", -1, "Max pages to download from")
	flag.BoolVar(&hxwstore, "save-by-size", true, "Store images in different folders by width and height")
	flag.IntVar(&minHeight, "min-height", -1, "Min height of the image to download")
	flag.IntVar(&minWidth, "min-width", -1, "Min width of the image to download")
	flag.IntVar(&tCount, "threads", runtime.NumCPU(), "Number of threads to download on concurrently")

	flag.Parse()

	if _, err := os.Stat("./auth.txt"); err == nil {
		b, _ := ioutil.ReadFile("./auth.txt")
		auth = string(b)
	}

	if auth == "" {
		flag.Usage()
		fmt.Print("Auth (API) is not set use -auth=api")
		return
	}

	if search == "" {
		flag.Usage()
		fmt.Print("Search param not set use -search='keyword'")
		return
	}

	if search == "?" || search == "help" {
		flag.Usage()
		return
	}

	thread_pool = threads.NewPool(tCount, 1000000)

	os.MkdirAll(base_folder+"/"+search, 0777)

	thread_pool.Start()

	GetAllWallpapers(ReadJSONFromAPI(current_page))

	thread_pool.Shutdown()
	thread_pool.Wait()

	fmt.Print("Finished downloading all Wallpapers")
}

func GetAllWallpapers(contents string) {
	var dat WallPaperJSON
	if err := json.Unmarshal([]byte(contents), &dat); err != nil {
		panic(err)
	}

	for i := 0; i < len(dat.Wallpapers); i++ {
		var folder = ""

		if !hxwstore {
			folder = base_folder + "/" + search
		} else {
			folder = base_folder + "/" + search + "/" + dat.Wallpapers[i].Width + "x" + dat.Wallpapers[i].Height
		}

		var wp = dat.Wallpapers[i]
		var ur = wp.URLImage

		var h, _ = strconv.Atoi(wp.Height)

		var w, _ = strconv.Atoi(wp.Width)

		if h < minHeight || w < minWidth {
			continue
		}

		os.MkdirAll(folder, 0777)

		if _, err := os.Stat(folder + "/" + filepath.Base(ur)); err == nil {
			continue
		}

		current_download++
		fmt.Printf("Downloading Wallpaper: %s (%v/%s)\n", ur, current_download, dat.TotalMatch)
		thread_pool.Submit(func() interface{} {
			downloadFile(folder+"/"+filepath.Base(ur), ur)

			fmt.Printf("Downloaded Wallpaper: %s to %s \n", filepath.Base(ur), folder+"/"+filepath.Base(ur))
			return "done"
		})
	}

	if current_page == page_limit {
		return
	}

	if len(dat.Wallpapers) < 30 {
		return
	}

	current_page = current_page + 1
	time.Sleep(time.Duration(2) * time.Second)
	GetAllWallpapers(ReadJSONFromAPI(current_page))
}

func downloadFile(filepath string, url string) (err error) {
	// Create the file
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return err
	}

	return nil
}

func ReadJSONFromAPI(page int) string {
	if page_limit > -1 {
		if page > page_limit {
			return
		}
	}

	var p = strconv.Itoa(page)
	var url = "https://wall.alphacoders.com/api2.0/get.php?auth=" + auth + "&method=search&term=" + search + "&page=" + p
	response, err := http.Get(url)

	if err != nil {
		fmt.Printf("%s", err)
		return ReadJSONFromAPI(page)
	} else {
		defer response.Body.Close()
		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			fmt.Printf("%s", err)
			return ReadJSONFromAPI(page)
		}
		return string(contents)
	}
}
<commit_msg>Fixed page limit finally to actually honor the amount you set<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"time"

	"github.com/wushilin/threads"
)

var auth string
var search string
var tCount int
var hxwstore bool
var current_page = 1
var current_download = 1
var page_limit = -1
var minWidth = -1
var minHeight = -1

var base_folder string = "./Output"
var thread_pool *threads.ThreadPool

type WallPaperJSON struct {
	Success    bool `json:"success"`
	Wallpapers []struct {
		ID       string `json:"id"`
		Width    string `json:"width"`
		Height   string `json:"height"`
		FileType string `json:"file_type"`
		FileSize string `json:"file_size"`
		URLImage string `json:"url_image"`
		URLThumb string `json:"url_thumb"`
		URLPage  string `json:"url_page"`
	} `json:"wallpapers"`
	TotalMatch string `json:"total_match"`
}

func main() {
	flag.StringVar(&auth, "auth", "", "API key from AlphaCoders")
	flag.StringVar(&search, "search", "", "Search key for wallpapers to download")
	flag.StringVar(&base_folder, "output", "./Output", "Output folder for images")
	flag.IntVar(&page_limit, "pl", -1, "Max pages to download from")
	flag.BoolVar(&hxwstore, "save-by-size", true, "Store images in different folders by width and height")
	flag.IntVar(&minHeight, "min-height", -1, "Min height of the image to download")
	flag.IntVar(&minWidth, "min-width", -1, "Min width of the image to download")
	flag.IntVar(&tCount, "threads", runtime.NumCPU(), "Number of threads to download on concurrently")

	flag.Parse()

	if _, err := os.Stat("./auth.txt"); err == nil {
		b, _ := ioutil.ReadFile("./auth.txt")
		auth = string(b)
	}

	if auth == "" {
		flag.Usage()
		fmt.Print("Auth (API) is not set use -auth=api")
		return
	}

	if search == "" {
		flag.Usage()
		fmt.Print("Search param not set use -search='keyword'")
		return
	}

	if search == "?" || search == "help" {
		flag.Usage()
		return
	}

	thread_pool = threads.NewPool(tCount, 1000000)

	os.MkdirAll(base_folder+"/"+search, 0777)

	thread_pool.Start()

	GetAllWallpapers(ReadJSONFromAPI(current_page))

	thread_pool.Shutdown()
	thread_pool.Wait()

	fmt.Print("Finished downloading all Wallpapers")
}

func GetAllWallpapers(contents string) {
	if contents == "" {
		return
	}

	var dat WallPaperJSON
	if err := json.Unmarshal([]byte(contents), &dat); err != nil {
		panic(err)
	}

	for i := 0; i < len(dat.Wallpapers); i++ {
		var folder = ""

		if !hxwstore {
			folder = base_folder + "/" + search
		} else {
			folder = base_folder + "/" + search + "/" + dat.Wallpapers[i].Width + "x" + dat.Wallpapers[i].Height
		}

		var wp = dat.Wallpapers[i]
		var ur = wp.URLImage

		var h, _ = strconv.Atoi(wp.Height)

		var w, _ = strconv.Atoi(wp.Width)

		if h < minHeight || w < minWidth {
			continue
		}

		os.MkdirAll(folder, 0777)

		if _, err := os.Stat(folder + "/" + filepath.Base(ur)); err == nil {
			continue
		}

		current_download++
		fmt.Printf("Downloading Wallpaper: %s (%v/%s)\n", ur, current_download, dat.TotalMatch)
		thread_pool.Submit(func() interface{} {
			downloadFile(folder+"/"+filepath.Base(ur), ur)

			fmt.Printf("Downloaded Wallpaper: %s to %s \n", filepath.Base(ur), folder+"/"+filepath.Base(ur))
			return "done"
		})
	}

	if current_page == page_limit {
		return
	}

	if len(dat.Wallpapers) < 30 {
		return
	}

	current_page = current_page + 1
	time.Sleep(time.Duration(2) * time.Second)
	GetAllWallpapers(ReadJSONFromAPI(current_page))
}

func downloadFile(filepath string, url string) (err error) {
	// Create the file
	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return err
	}

	return nil
}

func ReadJSONFromAPI(page int) string {
	if page_limit > -1 {
		if page > page_limit {
			return ""
		}
	}

	var p = strconv.Itoa(page)
	var url = "https://wall.alphacoders.com/api2.0/get.php?auth=" + auth + "&method=search&term=" + search + "&page=" + p
	response, err := http.Get(url)

	if err != nil {
		fmt.Printf("%s", err)
		return ReadJSONFromAPI(page)
	} else {
		defer response.Body.Close()
		contents, err := ioutil.ReadAll(response.Body)
		if err != nil {
			fmt.Printf("%s", err)
			return ReadJSONFromAPI(page)
		}
		return string(contents)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/dghubble/go-twitter/twitter"
	"github.com/dghubble/oauth1"
	"strings"
	"time"
)

var (
	twitterBot *TwitterBot
)

// TwitterBot ...
type TwitterBot struct {
	ID      string
	ImgPath string
	Client  *twitter.Client
	Follows map[string]string
}

// NewTwitterBot ...
func NewTwitterBot(cfg *TwitterConfig) *TwitterBot {
	config := oauth1.NewConfig(cfg.ConsumerKey, cfg.ConsumerSecret)
	token := oauth1.NewToken(cfg.AccessToken, cfg.AccessSecret)
	httpClient := config.Client(oauth1.NoContext, token)
	client := twitter.NewClient(httpClient)
	bot := &TwitterBot{
		ID:      cfg.IDSelf,
		ImgPath: cfg.ImgPath,
		Client:  client,
		Follows: map[string]string{
			"KanColle_STAFF": "294025417",
			"komatan":        "96604067",
			"maesanpicture":  "2381595966",
			"Strangestone":   "93332575",
			"kazuharukina":   "28787294",
		},
	}
	return bot
}

func hasHashTags(s string, tags []twitter.HashtagEntity) bool {
	for _, tag := range tags {
		if s == tag.Text {
			return true
		}
	}
	return false
}

func getMedias(tweet *twitter.Tweet) []twitter.MediaEntity {
	ee := tweet.ExtendedEntities
	if ee != nil {
		return ee.Media
	}
	return tweet.Entities.Media
}

func sendPics(medias []twitter.MediaEntity) {
	for _, media := range medias {
		switch media.Type {
		case "photo":
			go qqBot.SendPics(qqBot.SendGroupMsg, media.MediaURLHttps)
		}
	}
}

func logAllTrack(msg interface{}) {
	logger.Debug(msg)
}

func (t *TwitterBot) trackTweet(tweet *twitter.Tweet) {
	if tweet.RetweetedStatus != nil {
		// logger.Debugf("ignore retweet (%s):{%s}", tweet.User.Name, tweet.Text)
		return
	}
	flattenedText := strings.Replace(tweet.Text, "\n", `\n`, -1)
	medias := getMedias(tweet)
	switch tweet.User.IDStr {
	case t.Follows["KanColle_STAFF"]:
		msg := tweet.Text
		if tweet.Truncated {
			msg = tweet.FullText
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		t := tweet.CreatedAt
		ct, err := tweet.CreatedAtTime()
		if err == nil {
			tz, err := time.LoadLocation("Asia/Tokyo")
			if err == nil {
				t = ct.In(tz).String()
			}
		}
		qqBot.SendGroupMsg(tweet.User.Name + "\n" + t + "\n\n" + msg)
		sendPics(medias)

	case t.Follows["komatan"]:
		if len(medias) == 0 {
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		sendPics(medias)

	case t.Follows["maesanpicture"]:
		if len(medias) == 0 {
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		if hasHashTags("毎日五月雨", tweet.Entities.Hashtags) {
			qqBot.SendGroupMsg(tweet.Text)
			sendPics(medias)
		}

	case t.Follows["Strangestone"]:
		if len(medias) == 0 {
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		if strings.HasPrefix(tweet.Text, "月曜日のたわわ") {
			qqBot.SendGroupMsg(tweet.Text)
			sendPics(medias)
		}

	case t.Follows["kazuharukina"]:
		if len(medias) == 0 {
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		if hasHashTags("和遥キナ毎日JK企画", tweet.Entities.Hashtags) {
			sendPics(medias)
		}

	default:
		// logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
	}
}

func (t *TwitterBot) trackEvent(event *twitter.Event) {
	switch event.Source.IDStr {
	case t.Follows["KanColle_STAFF"]:
		flattenedText := strings.Replace(event.TargetObject.Text, "\n", `\n`, -1)
		logger.Debugf("%s: (%s):{%s}", event.Event, event.Source.Name, flattenedText)
	default:
	}
}

func (t *TwitterBot) selfProceedPics(medias []twitter.MediaEntity, action int) {
	for _, media := range medias {
		switch media.Type {
		case "photo":
			switch action {
			case 1:
				downloadFile(media.MediaURLHttps, t.ImgPath)
				go qqBot.SendPics(qqBot.SendSelfMsg, media.MediaURLHttps)
			case -1:
				removeFile(media.MediaURLHttps, t.ImgPath)
			}
		}
	}
}

func (t *TwitterBot) selfEvent(event *twitter.Event) {
	if event.Source.IDStr != t.ID {
		logger.Debugf("%s: (%s):{%s}", event.Event, event.Source.Name, flattenedText)
		return
	}
	switch event.Event {
	case "favorite":
		flattenedText := strings.Replace(event.TargetObject.Text, "\n", `\n`, -1)
		medias := getMedias(event.TargetObject)
		logger.Infof("favorite: (%s):{%s} %d medias", event.TargetObject.User.Name, flattenedText, len(medias))
		go t.selfProceedPics(medias, 1)
	case "unfavorite":
		flattenedText := strings.Replace(event.TargetObject.Text, "\n", `\n`, -1)
		medias := getMedias(event.TargetObject)
		logger.Debugf("unfavorite: (%s):{%s} %d medias", event.TargetObject.User.Name, flattenedText, len(medias))
		go t.selfProceedPics(medias, -1)
	default:
		logger.Debug(event.Event)
	}
}

func (t *TwitterBot) selfTweet(tweet *twitter.Tweet) {
	if qqBot.Config.NameGroup != "" {
		if hasHashTags(qqBot.Config.NameGroup, tweet.Entities.Hashtags) {
			if tweet.QuotedStatus != nil {
				logger.Infof("(%s):{%s}", qqBot.Config.NameGroup, strings.Replace(tweet.QuotedStatus.Text, "\n", `\n`, -1))
				sendPics(getMedias(tweet.QuotedStatus))
			} else {
				logger.Infof("(%s):{%s}", qqBot.Config.NameGroup, strings.Replace(tweet.Text, "\n", `\n`, -1))
				sendPics(getMedias(tweet))
			}
		}
	}
}

// Track ...
func (t *TwitterBot) Track() {
	follows := []string{}
	for _, value := range t.Follows {
		follows = append(follows, value)
	}
	for i := 1; ; i++ {
		demux := twitter.NewSwitchDemux()
		demux.Tweet = t.trackTweet
		demux.Event = t.trackEvent
		filterParams := &twitter.StreamFilterParams{
			Follow: follows,
		}
		stream, err := t.Client.Streams.Filter(filterParams)
		if err != nil {
			logger.Error(err)
			time.Sleep(time.Duration(i) * time.Second)
		}
		demux.HandleChan(stream.Messages)
	}
}

// Self ...
func (t *TwitterBot) Self() {
	for i := 1; ; i++ {
		demux := twitter.NewSwitchDemux()
		demux.Event = t.selfEvent
		demux.Tweet = t.selfTweet
		userParams := &twitter.StreamUserParams{
			With: t.ID,
		}
		stream, err := t.Client.Streams.User(userParams)
		if err != nil {
			logger.Error(err)
			time.Sleep(time.Duration(i) * time.Second)
		}
		demux.HandleChan(stream.Messages)
	}
}
<commit_msg>log less<commit_after>package main

import (
	"github.com/dghubble/go-twitter/twitter"
	"github.com/dghubble/oauth1"
	"strings"
	"time"
)

var (
	twitterBot *TwitterBot
)

// TwitterBot ...
type TwitterBot struct {
	ID      string
	ImgPath string
	Client  *twitter.Client
	Follows map[string]string
}

// NewTwitterBot ...
func NewTwitterBot(cfg *TwitterConfig) *TwitterBot {
	config := oauth1.NewConfig(cfg.ConsumerKey, cfg.ConsumerSecret)
	token := oauth1.NewToken(cfg.AccessToken, cfg.AccessSecret)
	httpClient := config.Client(oauth1.NoContext, token)
	client := twitter.NewClient(httpClient)
	bot := &TwitterBot{
		ID:      cfg.IDSelf,
		ImgPath: cfg.ImgPath,
		Client:  client,
		Follows: map[string]string{
			"KanColle_STAFF": "294025417",
			"komatan":        "96604067",
			"maesanpicture":  "2381595966",
			"Strangestone":   "93332575",
			"kazuharukina":   "28787294",
		},
	}
	return bot
}

func hasHashTags(s string, tags []twitter.HashtagEntity) bool {
	for _, tag := range tags {
		if s == tag.Text {
			return true
		}
	}
	return false
}

func getMedias(tweet *twitter.Tweet) []twitter.MediaEntity {
	ee := tweet.ExtendedEntities
	if ee != nil {
		return ee.Media
	}
	return tweet.Entities.Media
}

func sendPics(medias []twitter.MediaEntity) {
	for _, media := range medias {
		switch media.Type {
		case "photo":
			go qqBot.SendPics(qqBot.SendGroupMsg, media.MediaURLHttps)
		}
	}
}

func logAllTrack(msg interface{}) {
	logger.Debug(msg)
}

func (t *TwitterBot) trackTweet(tweet *twitter.Tweet) {
	if tweet.RetweetedStatus != nil {
		// logger.Debugf("ignore retweet (%s):{%s}", tweet.User.Name, tweet.Text)
		return
	}
	flattenedText := strings.Replace(tweet.Text, "\n", `\n`, -1)
	medias := getMedias(tweet)
	switch tweet.User.IDStr {
	case t.Follows["KanColle_STAFF"]:
		msg := tweet.Text
		if tweet.Truncated {
			msg = tweet.FullText
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		t := tweet.CreatedAt
		ct, err := tweet.CreatedAtTime()
		if err == nil {
			tz, err := time.LoadLocation("Asia/Tokyo")
			if err == nil {
				t = ct.In(tz).String()
			}
		}
		qqBot.SendGroupMsg(tweet.User.Name + "\n" + t + "\n\n" + msg)
		sendPics(medias)

	case t.Follows["komatan"]:
		if len(medias) == 0 {
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		sendPics(medias)

	case t.Follows["maesanpicture"]:
		if len(medias) == 0 {
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		if hasHashTags("毎日五月雨", tweet.Entities.Hashtags) {
			qqBot.SendGroupMsg(tweet.Text)
			sendPics(medias)
		}

	case t.Follows["Strangestone"]:
		if len(medias) == 0 {
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		if strings.HasPrefix(tweet.Text, "月曜日のたわわ") {
			qqBot.SendGroupMsg(tweet.Text)
			sendPics(medias)
		}

	case t.Follows["kazuharukina"]:
		if len(medias) == 0 {
			return
		}
		logger.Infof("(%s):{%s}", tweet.User.Name, flattenedText)
		if hasHashTags("和遥キナ毎日JK企画", tweet.Entities.Hashtags) {
			sendPics(medias)
		}

	default:
		// logger.Debugf("(%s):{%s}", tweet.User.Name, flattenedText)
	}
}

func (t *TwitterBot) trackEvent(event *twitter.Event) {
	switch event.Source.IDStr {
	case t.Follows["KanColle_STAFF"]:
		flattenedText := strings.Replace(event.TargetObject.Text, "\n", `\n`, -1)
		logger.Debugf("%s: (%s):{%s}", event.Event, event.Source.Name, flattenedText)
	default:
	}
}

func (t *TwitterBot) selfProceedPics(medias []twitter.MediaEntity, action int) {
	for _, media := range medias {
		switch media.Type {
		case "photo":
			switch action {
			case 1:
				downloadFile(media.MediaURLHttps, t.ImgPath)
				go qqBot.SendPics(qqBot.SendSelfMsg, media.MediaURLHttps)
			case -1:
				removeFile(media.MediaURLHttps, t.ImgPath)
			}
		}
	}
}

func (t *TwitterBot) selfEvent(event *twitter.Event) {
	if event.Source.IDStr != t.ID {
		logger.Debugf("%s: (%s)", event.Event, event.Source.Name)
		return
	}
	switch event.Event {
	case "favorite":
		flattenedText := strings.Replace(event.TargetObject.Text, "\n", `\n`, -1)
		medias := getMedias(event.TargetObject)
		logger.Infof("favorite: (%s):{%s} %d medias", event.TargetObject.User.Name, flattenedText, len(medias))
		go t.selfProceedPics(medias, 1)
	case "unfavorite":
		flattenedText := strings.Replace(event.TargetObject.Text, "\n", `\n`, -1)
		medias := getMedias(event.TargetObject)
		logger.Debugf("unfavorite: (%s):{%s} %d medias", event.TargetObject.User.Name, flattenedText, len(medias))
		go t.selfProceedPics(medias, -1)
	default:
		logger.Debug(event.Event)
	}
}

func (t *TwitterBot) selfTweet(tweet *twitter.Tweet) {
	if qqBot.Config.NameGroup != "" {
		if hasHashTags(qqBot.Config.NameGroup, tweet.Entities.Hashtags) {
			if tweet.QuotedStatus != nil {
				logger.Infof("(%s):{%s}", qqBot.Config.NameGroup, strings.Replace(tweet.QuotedStatus.Text, "\n", `\n`, -1))
				sendPics(getMedias(tweet.QuotedStatus))
			} else {
				logger.Infof("(%s):{%s}", qqBot.Config.NameGroup, strings.Replace(tweet.Text, "\n", `\n`, -1))
				sendPics(getMedias(tweet))
			}
		}
	}
}

// Track ...
func (t *TwitterBot) Track() {
	follows := []string{}
	for _, value := range t.Follows {
		follows = append(follows, value)
	}
	for i := 1; ; i++ {
		demux := twitter.NewSwitchDemux()
		demux.Tweet = t.trackTweet
		demux.Event = t.trackEvent
		filterParams := &twitter.StreamFilterParams{
			Follow: follows,
		}
		stream, err := t.Client.Streams.Filter(filterParams)
		if err != nil {
			logger.Error(err)
			time.Sleep(time.Duration(i) * time.Second)
		}
		demux.HandleChan(stream.Messages)
	}
}

// Self ...
func (t *TwitterBot) Self() {
	for i := 1; ; i++ {
		demux := twitter.NewSwitchDemux()
		demux.Event = t.selfEvent
		demux.Tweet = t.selfTweet
		userParams := &twitter.StreamUserParams{
			With: t.ID,
		}
		stream, err := t.Client.Streams.User(userParams)
		if err != nil {
			logger.Error(err)
			time.Sleep(time.Duration(i) * time.Second)
		}
		demux.HandleChan(stream.Messages)
	}
}
<|endoftext|>