penguin/golifehk

@golifehk bot in Telegram

datasources/kmb/QueryResult.go

raw · 5885 bytes

package kmb

import (
	"fmt"
	"math"
	"sort"
	"strings"
	"time"

	i18n "github.com/tgckpg/golifehk/i18n"
	query "github.com/tgckpg/golifehk/query"
	utils "github.com/tgckpg/golifehk/utils"
)

type QueryResult struct {
	Schedules *map[*RouteStop]*[]*Schedule
	Lang      string
	Error     error
	Query     *query.QueryObject

	isConsumed bool
	dataType   string
	tableData  [][]query.TableCell

	FallbackNearest int
	NearestRange    float64
}

func writeRouteHead(sb *strings.Builder, r *RouteStop) {
	utils.WriteMDv2Text(sb, r.RouteId)
	if r.Direction == "O" {
		sb.WriteString("↑")
	} else if r.Direction == "I" {
		sb.WriteString("↓")
	}
	if r.ServiceType != "1" {
		utils.WriteMDv2Text(sb, utils.ToPower(r.ServiceType))
	}
}

func writeShortRoute(lang *string, sb *strings.Builder, r *RouteStop) {

	if r.PrevStop() != nil {
		utils.WriteMDv2Text(sb, (*(r.PrevStop().BusStop).Name)[*lang])
		sb.WriteString(" \\> ")
	}

	sb.WriteString("*")
	utils.WriteMDv2Text(sb, (*(r.BusStop).Name)[*lang])
	sb.WriteString("*")

	if r.NextStop() != nil {
		sb.WriteString(" \\> ")
		utils.WriteMDv2Text(sb, (*(r.NextStop().BusStop).Name)[*lang])
	}

	sb.WriteString("\n")
}

func (this QueryResult) DataType() string                  { return this.dataType }
func (this QueryResult) Consumed() bool                    { return this.isConsumed }
func (this QueryResult) UserLang() string                  { return this.Lang }
func (this QueryResult) GetTableData() [][]query.TableCell { return this.tableData }

func (this *QueryResult) Message() (string, error) {

	this.dataType = "PlainText"

	if this.Error != nil {
		return "", this.Error
	}

	langPack, err := i18n.LoadKeys(this.Lang)
	if err != nil {
		return "", err
	}

	sb := strings.Builder{}

	if 0 < len(*this.Query.Results) {

		// Print Stop Names, then print the list of routes
		if this.Query.Key == "" {
			loc := this.Query.Message.Location
			if loc != nil {

				this.dataType = "Table"

				table := [][]query.TableCell{}
				minDist := math.MaxFloat64
				maxDist := -1.0

				for _, item := range *this.Query.Results {
					b := any(item).(*BusStop)

					row := []query.TableCell{}

					bDist := b.Dist(loc.Lat(), loc.Lon())
					if bDist < minDist {
						minDist = bDist
					}

					if maxDist < bDist {
						maxDist = bDist
					}

					cell := query.TableCell{
						Name:  fmt.Sprintf("%s (%s)", (*b.Name)[this.Lang], i18n.FormatDistance(langPack, bDist)),
						Value: fmt.Sprintf("%s", b.BusStopId),
					}
					row = append(row, cell)

					for colIndex, r := range *b.Routes {
						if colIndex%6 == 0 {
							table = append(table, row)
							row = []query.TableCell{}
						}
						sb_i := strings.Builder{}
						writeRouteHead(&sb_i, r)
						cell := query.TableCell{
							Name:  sb_i.String(),
							Value: fmt.Sprintf("%s %s", r.RouteId, (*b.Name)[this.Lang]),
						}
						row = append(row, cell)
					}

					table = append(table, row)
				}
				this.tableData = table

				rangeText := i18n.FormatDistance(langPack, this.NearestRange)
				if maxDist < this.NearestRange {
					utils.WriteMDv2Text(&sb, i18n.DS_KMB_NEAREST_STOPS.Text(langPack, rangeText))
				} else if this.NearestRange < minDist {
					utils.WriteMDv2Text(&sb, i18n.DS_KMB_NO_NEAREST_STOPS.Text(langPack, rangeText, this.FallbackNearest))
				}

			} else {
				busStops := map[string]*BusStop{}
				for _, item := range *this.Query.Results {
					var r *RouteStop
					r = any(item).(*RouteStop)

					b := r.BusStop
					if b.Routes == nil {
						continue
					}

					busStops[b.BusStopId] = b
				}

				for _, b := range busStops {
					utils.WriteMDv2Text(&sb, (*b.Name)[this.Lang])
					sb.WriteString("\n  ")
					for _, route := range *b.Routes {
						writeRouteHead(&sb, route)
						sb.WriteString(" ")
					}
					sb.WriteString("\n")
				}
			}

			// We got a route key
		} else {

			// We also got other search keys with 1 < Results
			// Get the ETA for this stop
			if 1 < len(*this.Query.SearchTerms) {

				now := time.Now()
				for _, item := range *this.Query.Results {
					var r *RouteStop
					r = any(item).(*RouteStop)
					writeRouteHead(&sb, r)
					sb.WriteString("\n")
					writeShortRoute(&this.Lang, &sb, r)

					noSchedules := true

					for _, schedule := range *(*this.Schedules)[r] {

						if !schedule.ETA.IsZero() {

							_m := schedule.ETA.Sub(now).Minutes()

							sb.WriteString("  \\* ")

							eta := i18n.UNITS_MINUTE.Text(langPack, _m)

							if _m < 0 {
								utils.WriteMDv2Text(&sb, i18n.DS_KMB_ETA_DEPARTED.Text(langPack, eta))
							} else {
								utils.WriteMDv2Text(&sb, eta)
							}

							noSchedules = false
						}

						if schedule.Remarks_en != "" {
							sb.WriteString("  \\*\\* ")
							switch this.Lang {
							case "en":
								utils.WriteMDv2Text(&sb, schedule.Remarks_en)
							case "zh-Hant":
								utils.WriteMDv2Text(&sb, schedule.Remarks_tc)
							}
						}

						sb.WriteString("\n")
					}

					if noSchedules {
						utils.WriteMDv2Text(&sb, i18n.DS_KMB_NO_SCHEDULES.Text(langPack))
					}

					sb.WriteString("\n")
				}

				// We got only the route key, proceed to list the route stops
			} else {
				// Result contains all route stops, we only need the starting one
				routes := []*RouteStop{}

				for _, item := range *this.Query.Results {
					var r *RouteStop
					r = any(item).(*RouteStop)
					if r.PrevStop() == nil {
						routes = append(routes, r)
					}
				}

				sort.Sort(ByRoute(routes))

				for _, r := range routes {
					writeRouteHead(&sb, r)
					sb.WriteString("\n")
					for {
						b := *r.BusStop
						utils.WriteMDv2Text(&sb, (*b.Name)[this.Lang])
						r = r.NextStop()
						if r == nil {
							break
						}

						sb.WriteString(" \\> ")
					}
					sb.WriteString("\n")
				}

			}
		}
	} else {
		return "", fmt.Errorf(i18n.NO_RESULTS.Text(langPack))
	}

	return sb.String(), nil
}