// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
// Copyright (C) 2020-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package tai64n

import (
	"time"
)

const Leapsecs1972 = 10

type LeapsecsList []int64

// Database of Unix timestamps of the time when leap second occurred.
// Library contains and initializes it with leap seconds up to 2016-12-31.
var Leapsecs LeapsecsList

// Add leap seconds to the time (convert UTC to TAI).
func (leapsecs LeapsecsList) Add(utc time.Time) (tai time.Time) {
	orig := utc.Unix()
	v := orig
	v += Leapsecs1972
	for _, leapsec := range Leapsecs {
		if v >= leapsec {
			v++
		}
	}
	tai = utc.Add(time.Second * time.Duration(v-orig))
	return
}

// Subtract leap seconds from the time (convert TAI to UTC).
func (leapsecs LeapsecsList) Sub(tai time.Time) (utc time.Time, isLeap bool) {
	diff := int64(Leapsecs1972)
	v := tai.Unix()
	for _, leapsec := range leapsecs {
		if v < leapsec {
			break
		}
		diff++
		if v == leapsec {
			isLeap = true
			break
		}
	}
	utc = tai.Add(-time.Second * time.Duration(diff))
	return
}

// Load "leapsecs.dat"-like database (concatenated TAI64 leap seconds).
func (leapsecs *LeapsecsList) Load(buf []byte) {
	db := make([]int64, 0, len(buf)/TAI64Size)
	var tai TAI64
	for i := 0; i < len(buf); i += TAI64Size {
		tai = [TAI64Size]byte(buf[i : i+TAI64Size])
		db = append(db, tai.Time().Unix())
	}
	*leapsecs = db
}

func init() {
	def := []time.Time{
		time.Date(1972, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1972, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1973, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1974, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1975, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1976, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1977, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1978, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1979, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1981, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1982, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1983, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1985, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1987, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1989, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1992, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1993, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1994, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1995, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(1997, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(1998, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(2005, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(2008, 12, 31, 0, 0, 0, 0, time.UTC),
		time.Date(2012, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(2015, 6, 30, 0, 0, 0, 0, time.UTC),
		time.Date(2016, 12, 31, 0, 0, 0, 0, time.UTC),
	}
	db := make([]int64, len(def))
	for i, t := range def {
		db[i] = t.Add(time.Second*86400).Unix() + Leapsecs1972 + int64(i)
	}
	Leapsecs = LeapsecsList(db)
}
