package data

/*
Data package: Handles data from text to number,
and vice versa
*/

import (
    "fmt"
    "config"
    "strings"
    "strconv"
    "tools/rsa/integer"
)

type Data string
type DataType int

const (
    LL = config.RSA_SIZE
    AE = config.AE
    OE = config.OE
    AA = config.AA
    SPACE = config.SPACE
    COMMA = config.COMMA
    DOT = config.DOT
    PAD = SPACE
    NUM DataType = 1 << iota
    TXT DataType = 1 << iota
)


type Blob struct {
    d Data
    t DataType
}

func New() *Blob {
    return new(Blob)
}

func (b *Blob) Data() *Data {
    return &b.d
}

func (b *Blob) Set(t DataType, s Data) *Blob{
    b.t = t
    b.Data().set(s)
    return b
}

func (b *Blob) AsNumbers() *Blob {
    if b.t == NUM {
        return b
    }
    b.Set(NUM,
        Data(
            strings.Map(func (c rune) rune {
                switch c {
                    case 'æ': c = AE
                    case 'ø': c = OE
                    case 'å': c = AA
                    case ' ': c = SPACE
                    case ',': c = COMMA
                    case '.': c = DOT
                    default: c %= 97
                }
                return c
            }, string(b.Data().Get())),
        ),
    )
    b.Data().pad()
    return b
}

func (b *Blob) AsText() *Blob {
    if b.t == TXT {
        return b
    }
    b.Set(TXT,
        Data(
            strings.Map(func (c rune) rune {
                switch c {
                    case AE: c = 'æ'
                    case OE: c = 'ø'
                    case AA: c = 'å'
                    case SPACE: c = ' '
                    case COMMA: c = ','
                    case DOT: c = '.'
                    default: c += 97
                }
                return c
            }, string(b.Data().Get())),
        ),
    )
    return b
}

func (b *Blob) ToList() []*integer.RSAInt {
    var l []*integer.RSAInt

    for s, i := strings.Split(b.Repr(), "\n"), 0; i < len(s); i++ {
        rint, _ := integer.New(0).SetString(s[i])
        l = append(l, rint)
    }

    return l
}

func (b *Blob) FromList(l []*integer.RSAInt) {
    str := ""
    for x := 0; x < len(l); x++ {
        if l[x].Len() < LL {
            str += "0" + l[x].String()
        } else {
            str += l[x].String()
        }
    }
    b.Data().SetRaw(str)
}

func (b *Blob) Repr() string {
    s := ""
    if b.t == TXT {
        s = string(b.Data().Get())
    } else {
        for i := 0; i < b.Data().Len(); i++ {
            if (i % (LL / 2)) == 0 {
                s += "\n"
            }
            s += fmt.Sprintf("%.2d", b.Data().Get()[i])
        }
    }
    return strings.Trim(s, "\n")
}

func (d *Data) set(s Data) {
    *d = s
}

func (d *Data) SetRaw(s string) {
    raw := ""
    for y := 0; y < len(s); y += 2 {
        yy := y+2
        if yy > len(s) {
            yy = len(s)
        }
        a, _ := strconv.Atoi(s[y:yy])
        raw += fmt.Sprintf("%c", (rune(a)))
    }
    d.set(Data(raw))
}

func (d *Data) add(s Data) {
    *d += s
}

func (d *Data) Get() Data {
    return *d
}

func (d *Data) Len() int {
    return len(d.Get())
}

func (d *Data) pad () {
    l := d.Len() * 2
    ll := l % LL
    if ll == 0 {
        return
    }
    for m, i := LL - ll, 0; i < m; i += 2 {
        d.add(Data(PAD))
    }
}
