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

// Parse Plan 9 timezone(2) files.

package time

import (
	"errors"
	"runtime"
	"syscall"
)

func isSpace(r rune) bool {
	return r == ' ' || r == '\t' || r == '\n'
}

// Copied from strings to avoid a dependency.
func fields(s string) []string {
	// First count the fields.
	n := 0
	inField := false
	for _, rune := range s {
		wasInField := inField
		inField = !isSpace(rune)
		if inField && !wasInField {
			n++
		}
	}

	// Now create them.
	a := make([]string, n)
	na := 0
	fieldStart := -1 // Set to -1 when looking for start of field.
	for i, rune := range s {
		if isSpace(rune) {
			if fieldStart >= 0 {
				a[na] = s[fieldStart:i]
				na++
				fieldStart = -1
			}
		} else if fieldStart == -1 {
			fieldStart = i
		}
	}
	if fieldStart >= 0 { // Last field might end at EOF.
		a[na] = s[fieldStart:]
	}
	return a
}

func loadZoneDataPlan9(s string) (l *Location, err error) {
	f := fields(s)
	if len(f) < 4 {
		if len(f) == 2 && f[0] == "GMT" {
			return UTC, nil
		}
		return nil, badData
	}

	var zones [2]zone

	// standard timezone offset
	o, err := atoi(f[1])
	if err != nil {
		return nil, badData
	}
	zones[0] = zone{name: f[0], offset: o, isDST: false}

	// alternate timezone offset
	o, err = atoi(f[3])
	if err != nil {
		return nil, badData
	}
	zones[1] = zone{name: f[2], offset: o, isDST: true}

	// transition time pairs
	var tx []zoneTrans
	f = f[4:]
	for i := 0; i < len(f); i++ {
		zi := 0
		if i%2 == 0 {
			zi = 1
		}
		t, err := atoi(f[i])
		if err != nil {
			return nil, badData
		}
		t -= zones[0].offset
		tx = append(tx, zoneTrans{when: int64(t), index: uint8(zi)})
	}

	// Committed to succeed.
	l = &Location{zone: zones[:], tx: tx}

	// Fill in the cache with information about right now,
	// since that will be the most common lookup.
	sec, _ := now()
	for i := range tx {
		if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
			l.cacheStart = tx[i].when
			l.cacheEnd = 1<<63 - 1
			if i+1 < len(tx) {
				l.cacheEnd = tx[i+1].when
			}
			l.cacheZone = &l.zone[tx[i].index]
		}
	}

	return l, nil
}

func loadZoneFilePlan9(name string) (*Location, error) {
	b, err := readFile(name)
	if err != nil {
		return nil, err
	}
	return loadZoneDataPlan9(string(b))
}

func initTestingZone() {
	z, err := loadLocation("America/Los_Angeles")
	if err != nil {
		panic("cannot load America/Los_Angeles for testing: " + err.Error())
	}
	z.name = "Local"
	localLoc = *z
}

func initLocal() {
	t, ok := syscall.Getenv("timezone")
	if ok {
		if z, err := loadZoneDataPlan9(t); err == nil {
			localLoc = *z
			return
		}
	} else {
		if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
			localLoc = *z
			localLoc.name = "Local"
			return
		}
	}

	// Fall back to UTC.
	localLoc.name = "UTC"
}

func loadLocation(name string) (*Location, error) {
	if z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name); err == nil {
		z.name = name
		return z, nil
	}
	return nil, errors.New("unknown time zone " + name)
}

func forceZipFileForTesting(zipOnly bool) {
	// We only use the zip file anyway.
}
