package door

import (
	"bytes"
	"fmt"
	"log"
	"strings"
	"time"
)

/*
WOPR

[      GAME      ]
[  TIME ELAPSED  ]
[     XX HRS     ]
[ XX MIN  XX SEC ]
[■               ]  ->
[      GAME      ]
[ TIME REMAINING ]
[     XX HRS     ]
[ XX MIN  XX SEC ]
[               ■]  <-

Border SINGLE
Ok, Width = 16
1000 / 16 = 62.5 ms = 62500 us

Width 16  (at pos 1, update time/sec increment)
Black on Cyan
*/
type WOPR struct {
	ElapsedPanel   Panel
	Elapsed        time.Time
	ElapsedD       time.Duration
	RemainingPanel Panel
	Remaining      time.Time
	RemainingD     time.Duration
	Color          string
	Index          int
	Ticker         *time.Ticker
	StopIt         chan bool
	output         *bytes.Buffer
}

const DEBUG_WOPR bool = true

func (w *WOPR) Clear() []byte {
	var output bytes.Buffer
	output.Write(w.ElapsedPanel.Clear())
	output.Write(w.RemainingPanel.Clear())
	return output.Bytes()
}

// Initialize, Set X, Y on Panels, Animate()

// Initialize, and create panels
func (w *WOPR) Init(elapsed time.Time, remaining time.Time, color string) {
	if color == "" {
		color = ColorText("BLACK ON CYAN")
	}
	w.output = &bytes.Buffer{}
	w.Elapsed = elapsed
	w.Remaining = remaining
	w.Index = 0
	w.ElapsedPanel = Panel{Width: 16,
		BorderColor: color,
		Style:       SINGLE}

	w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, NewLine("      GAME      "))
	w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, NewLine("  TIME ELAPSED  "))

	var ehour Line = Line{}
	ehour.UpdateF = func(u *bytes.Buffer) {
		var hours int = int(w.ElapsedD.Hours())
		u.Reset()
		fmt.Fprintf(u, "     %02d HRS     ", hours%100)
	}
	ehour.Update()
	// ehour.Text.Write(ehour.update)
	w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, ehour)

	var eminsec Line = Line{}
	eminsec.UpdateF = func(u *bytes.Buffer) {
		var mins int = int(w.ElapsedD.Minutes()) % 60
		var secs int = int(w.ElapsedD.Seconds()) % 60
		u.Reset()
		fmt.Fprintf(u, " %02d MIN  %02d SEC ", mins, secs)
	}
	eminsec.Update() // Text.Write(eminsec.UpdateF())
	w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, eminsec)

	var eanimate Line = Line{}
	eanimate.UpdateF = func(u *bytes.Buffer) {
		u.Reset()
		// Left to Right
		if Unicode {
			var buffer []rune = []rune(strings.Repeat(" ", 16))
			buffer[w.Index] = '\u25a0'
			u.WriteString(string(buffer))
		} else {
			var buffer []byte = bytes.Repeat([]byte(" "), 16)
			buffer[w.Index] = '\xfe'
			u.WriteString(string(buffer))
		}
	}
	eanimate.Update() // Text.Write(eanimate.UpdateF())
	w.ElapsedPanel.Lines = append(w.ElapsedPanel.Lines, eanimate)

	w.RemainingPanel = Panel{Width: 16,
		BorderColor: color,
		Style:       SINGLE}
	w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, NewLine("      GAME      "))
	w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, NewLine(" TIME REMAINING "))

	var rhour Line = Line{}
	rhour.UpdateF = func(u *bytes.Buffer) {
		var hours int = int(w.RemainingD.Hours())
		u.Reset()
		fmt.Fprintf(u, "     %02d HRS     ", hours%100)
	}
	rhour.Update() // Text.Write(rhour.UpdateF())
	w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, rhour)

	var rminsec Line = Line{}
	rminsec.UpdateF = func(u *bytes.Buffer) {
		var mins int = int(w.RemainingD.Minutes()) % 60
		var secs int = int(w.RemainingD.Seconds()) % 60
		u.Reset()
		fmt.Fprintf(u, " %02d MIN  %02d SEC ", mins, secs)
	}
	rminsec.Update() // Text.Write(rminsec.UpdateF())
	w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, rminsec)

	var ranimate Line = Line{}
	ranimate.UpdateF = func(u *bytes.Buffer) {
		u.Reset()
		// Left to Right
		if Unicode {
			var buffer []rune = []rune(strings.Repeat(" ", 16))
			buffer[15-w.Index] = '\u25a0'
			u.WriteString(string(buffer))
		} else {
			var buffer []byte = bytes.Repeat([]byte(" "), 16)
			buffer[15-w.Index] = '\xfe'
			u.WriteString(string(buffer))
		}
	}
	ranimate.Update() // Text.Write(ranimate.UpdateF())
	w.RemainingPanel.Lines = append(w.RemainingPanel.Lines, ranimate)
}

func (w *WOPR) Inc() {
	w.Index++
	if w.Index == 16 {
		w.Index = 0
		w.ElapsedPanel.Update()
		w.RemainingPanel.Update()

		w.RemainingD = time.Duration(w.RemainingD.Seconds()-1) * time.Second
		w.ElapsedD = time.Duration(w.ElapsedD.Seconds()+1) * time.Second

	}

}
func (w *WOPR) Animate(d *Door) {
	// til := time.Now().UnixNano() % int64(time.Second)

	// w.Index = int((til / int64(time.Microsecond)) / 62500)
	// either put the sync sleep in the go routine, or sleep and sync Index.

	// time.Sleep(time.Duration(int64(time.Second) - til)) // time.Now().UnixMilli()%1000) * time.Millisecond)

	// time.Second / 16
	// w.Ticker = time.NewTicker(time.Microsecond * time.Duration(62500))

	// w.Index = 0
	// Convert time.Time to time.Duration
	// This gives us consistency when resuming.
	w.ElapsedD = time.Since(w.Elapsed)
	w.RemainingD = time.Until(w.Remaining)
	w.StopIt = make(chan bool)

	go func(d *Door, output *bytes.Buffer) {
		// til := time.Now().UnixNano() % int64(time.Second)

		sec16 := int64(time.Second) / 16
		// tilms := til % sec16
		w.Index = 0 // int(til / sec16)
		// log.Printf("til: %d, sec: %d, w.Index: %d\n", til, sec16, w.Index)

		// either put the sync sleep in the go routine, or sleep and sync Index.

		// time.Sleep(time.Second - time.Duration(til)) // sec16 - (til % sec16))) //int64(time.Second) - til)) // time.Now().UnixMilli()%1000) * time.Millisecond)

		// time.Second / 16
		w.Ticker = time.NewTicker(time.Duration(sec16))

		// var output bytes.Buffer

		for {
			select {
			case <-w.StopIt:
				return

			case <-w.Ticker.C:
				w.ElapsedPanel.Update()
				w.RemainingPanel.Update()
				var using int
				using = output.Cap()
				output.Reset()
				output.WriteString(SavePos)
				output.Write(w.ElapsedPanel.Output())
				output.Write(w.RemainingPanel.Output())
				output.WriteString(RestorePos)
				if DEBUG_WOPR {
					if output.Cap() > using {
						using = output.Cap()
						log.Printf("WOPR: now %d\n", using)
					}
				}
				if !d.Writer.IsClosed() {
					d.Write(output.Bytes())
					output.Reset()
				} else {
					w.Ticker.Stop()
					return
				}

				w.Inc()
			}
		}
	}(d, w.output)
}

func (w *WOPR) Stop() {
	w.Ticker.Stop()
	w.StopIt <- true
}