/packet/repeat.go
package packet

import (
    "fmt"
    "reflect"

    "bytex64.net/code/bitsmash/ac"
)

type RepeatPacket struct {
    distance uint8
    packetSlice []Packet
}

func (self *RepeatPacket) Length() int {
    c := 0
    for _, p := range(self.packetSlice) {
        c += p.Length()
    }
    return c
}

func (*RepeatPacket) ByteLength() int {
    return 1
}

func (self *RepeatPacket) Encode(encoder ac.CodecManager) {
    encoder.Encode(CONTEXT_REPEAT_DISTANCE, uint32(self.distance - 1))
    encoder.Encode(CONTEXT_REPEAT_LENGTH, uint32(self.Count() - 1))
}

func DecodeRepeat(decoder ac.CodecManager) Packet {
    distance := byte(decoder.Decode(CONTEXT_REPEAT_DISTANCE))
    length := byte(decoder.Decode(CONTEXT_REPEAT_LENGTH))
    return &RepeatPacket{distance + 1, make([]Packet, length + 1)}
}

func (self *RepeatPacket) Pixels() []uint8 {
    pixels := make([]uint8, self.Length())
    c := 0
    for _, p := range(self.packetSlice) {
        pp := p.Pixels()
        copy(pixels[c:], pp)
        c += len(pp)
    }
    return pixels
}

func (self *RepeatPacket) String() string {
    return fmt.Sprintf("Repeat distance %d length %d", self.distance, len(self.packetSlice))
}

func (self *RepeatPacket) Count() int {
    return len(self.packetSlice)
}

func (self *RepeatPacket) Distance() int {
    return int(self.distance)
}

func (self *RepeatPacket) Slice() []Packet {
    return self.packetSlice
}

func (self *RepeatPacket) UnRepeat(packets []Packet, head int) []Packet {
    count := self.Count()
    start := head - int(self.distance)

    for i := range(self.packetSlice) {
        packets = append(packets, packets[start + i])
    }
    self.packetSlice = packets[start:start + count]
    return packets
}

type packetMatch struct {
    Start int
    Length int
}

func findMatch(packets []Packet, head int, start int) *packetMatch {
    matchlen := 0
    maxlen := len(packets) - head
    if maxlen > 8 {
        maxlen = 8
    }

    for ; matchlen < maxlen && reflect.DeepEqual(packets[start + matchlen], packets[head + matchlen]); matchlen++ {
    }

    if matchlen == 0 {
        return nil
    }

    headbytelen := packets[head].ByteLength()
    if headbytelen == 1 && matchlen < 2 {
        // Not saving any space for a single byte packet if we're not matching
        // at least two packets
        return nil
    }

    return &packetMatch{start, matchlen}
}

func ScanRepeat(packets []Packet, head int) Packet {
    if head == 0 {
        // Won't find any repeats at the head of the list
        return nil
    }

    start := head - 32
    if start < 0 {
        start = 0
    }

    var bestMatch *packetMatch
    for i := head - 1; i >= start; i-- {
        match := findMatch(packets, head, i)
        if match == nil {
            continue
        }
        if bestMatch == nil || match.Length > bestMatch.Length {
            bestMatch = match
        }
    }
    if (bestMatch == nil) {
        return nil
    }

    return &RepeatPacket{uint8(head - bestMatch.Start), packets[bestMatch.Start:bestMatch.Start + bestMatch.Length]}
}