#!/usr/bin/env python3
# -*- coding:Utf-8 -*-

import sys, datetime

freqs = [15556, 7778, 5185, 3889,
         15639, 7819, 5213, 3909]


# Help:
def printHelp(retcode):
    print("  Usage:")
    print(sys.argv[0], "outfile fmode infile artist title")
    print("\toutfile\tOutput file name")
    print("\tfmode\tConverting mode")
    print("\tinfile\tInput file name")
    print("\tartist\tMusic's Artist")
    print("\ttitle\tMusic's Title")
    sys.exit(retcode)

# Artist / Title "converter":
def nameConvert(string):
    string = string.upper()
    string = string.replace("_", " ")
    if len(string) > 32:
        string = string[:32]
    else:
        while len(string) < 32:
            string += "\x00"
    string = string.encode("ascii")
    return string

# PETSCII Logo "converter":
def logoConvert(logotype, logoba):
    if logotype == "1":                 # 200 BYTE colors, 200 BYTE chars, simple C64/C128 PETSCII logo
        colors = logoba[0:200]
        chars = logoba[200:400]
        colpack = bytearray()
        for a in range(100):
            c1 = colors[(a*2)+0] & 0x0f
            c2 = colors[(a*2)+1] & 0x0f
            c = c1 + (c2 << 4)
            colpack.append(c)
        coltab = b"\x00\x71\x32\x53\x44\x45\x36\x77\x48\x29\x6b\x31\x41\x6f\x6d\x61"
        return 1, chars + colpack + coltab
    elif logotype == "2":               # 200 BYTE colors, 200 BYTE chars, simple plus/4 PETSCII logo
        colors = logoba[0:200]
        chars = logoba[200:400]
        sz = {}
        p = 0
        for a in colors:
            if a in sz:
                sz[a][1] += 1
            else:
                sz[a] = [p, 1]
                p += 1
        if len(sz) > 16:
            print("Too many colors on PETSCII logo!")
            sys.exit(10)
        colpack = bytearray()
        for a in range(100):
            c1 = sz[colors[(a*2)+0]]
            c2 = sz[colors[(a*2)+1]]
            c = c1[0] + (c2[0] << 4)
            colpack.append(c)
        coltab = bytearray(16)
        for a in sz:
            coltab[sz[a][0]] = a
        return 1, chars + colpack + coltab
    return 0, b""

# Calculate Audio length parameters:
def calculateAudioLength(audiotype, fsiz, freq):
    if audiotype == 8:
        sca = fsiz // freq
        bno = (fsiz // 256) + 1
    elif audiotype == 9:
        sca = (fsiz * 2) // freq
        bno = ((fsiz * 2) // 256) + 1
    elif audiotype == 10:
        sca = (fsiz * 4) // freq
        bno = ((fsiz * 4) // 256) + 1
    else:
        print("Unknown 'audiotype'")
        sys.exit(10)
    sc = sca % 60
    mna = sca // 60
    mn = mna % 60
    hr = mna // 60
    adder = 0x1000000 // bno
    return hr, mn, sc, adder


# Create Comm8Wave file header:
def createHeader(typ, lbgnewbl, freq, musicartist, musictitle, logotype, logoba):
    h = bytearray()
    h += b"Com8Wave"
    h.append(0x01)              # Com8Wave file version
    h.append(255)               # Header size in blocks, adjust later
    h.append(typ)               # Type of Comm8Wave file
    h.append(lbgnewbl)          # New BookBlock in LBG
    h.append(0)                 # n.u.
    h.append(freq & 0xff)
    h.append(freq >> 8)         # Playback frequency
    h += b"\x00\x00\x00\x00\x00\x00"    # Length in HH:MM:SS + ADDER, adjust later
    h += musicartist
    h += musictitle
    now = datetime.datetime.now().strftime("%Y%m%d %H%M%S ")
    h += now.encode("ascii")
    h += b"\x00\x00\x00\x00\x00"    # data length + data checksum, adjust later

    if logotype:
        logotyp, logo = logoConvert(logotype, logoba)
        h.append(logotyp)
        h += logo
    
    while len(h) % 256 != 0:
        h.append(0)
    h[9] = len(h) // 256
    return h

# Create integer from ByteArray:
def createInt(ba):
    return int.from_bytes(ba, byteorder='little', signed=False)

# Check playback frequency and print warning if required:
def checkPlaybackFreq(freq):
    if freq not in freqs:
        print("========================================")
        print("WARNING: Playback frequency not standard (%s)" % freq)
        print("========================================")

# WAVe header decoder:
def readWavHeader(wavfile):
    hd1 = wavfile.read(4)
    if hd1 != b'RIFF':
        print("No WAV file (missing 'RIFF')")
        return False, 0, 0, False
    hd1 = wavfile.read(4)           # Read filesize, not important now
    hd1 = wavfile.read(4)
    if hd1 != b'WAVE':
        print("No WAV file (missing 'WAVE')")
        return False, 0, 0, False
    hd1 = wavfile.read(4)
    if hd1[0:3] != b'fmt':
        print("No WAV params found (no 'fmt')")
        return False, 0, 0, False
    hdl = createInt(wavfile.read(4))
    if hdl != 16:
        print("WAV params length mismatch (%s)" % hdl)
        return False, 0, 0, False
    frm = createInt(wavfile.read(2))
    if frm != 1:
        print("WAV file format not PCM (%s)" % frm)
        return False, 0, 0, False
    chn = createInt(wavfile.read(2))
    if chn != 1:
        print("WAV file not mono (%s)" % chn)
        return False, 0, 0, False
    freq = createInt(wavfile.read(4))
    checkPlaybackFreq(freq)
    hd1 = wavfile.read(4)           # Not important
    byps = createInt(wavfile.read(2))
    if byps != 1:
        print("WAV file not 1 BYTE/Sample (%s)" % byps)
        return False, 0, 0, False
    bps = createInt(wavfile.read(2))
    if bps != 8:
        print("WAV file not 8 Bit/Sample (%s)" % bps)
        return False, 0, 0, False
    hd1 = wavfile.read(4)
    if hd1 != b'data':
        print("WAV data block not found")
        return False, 0, 0, False
    length = createInt(wavfile.read(4))
    return True, freq, length, False

# Return RAW file datas:
def readRawHeader(fl, frqstr):
    if frqstr[0] == 'u':
        signed = False
    elif frqstr[0] == 's':
        signed = True
    else:
        print("No unsigned, no signed, ???")
        return False, 0, 0, False
    try:
        freq = int(frqstr[2:], 10)
    except:
        print("Playback frequency error!")
        return False, 0, 0, False
    checkPlaybackFreq(freq)
    fl.seek(0, 2)
    length = fl.tell()
    fl.seek(0, 0)
    print("Length:", length)
    return True, freq, length, signed

# Read 256 BYTE from audio file:
def readBlock(flhndlr, length, chksum, signed):
    if length >= 256:
        a = flhndlr.read(256)
        length -= 256
    else:
        a = flhndlr.read(length)
        while len(a) < 256:
            if signed:
                a += b'\x00'
            else:
                a += b'\x80'
        length = 0
    ba = bytearray()
    ba += a
    if signed:
        for b in range(len(ba)):
            ba[b] ^= 0x80               # If signed, convert to unsigned
    for b in ba:
        chksum += b
    return ba, length, chksum

# Finalize header (set length datas, checksum)
def finalizeHeader(fwhndlr, typ, length, freq, chksum):
    hr, mn, sc, adder = calculateAudioLength(typ, length, freq)
    h = bytearray()
    h.append(hr)
    h.append(mn)
    h.append(sc)
    h.append(adder & 0xff)
    h.append((adder >> 8) & 0xff)
    h.append((adder >> 16) & 0xff)
    fwhndlr.seek(15)                    # Music length pos: H:M:S
    fwhndlr.write(h)
    h = bytearray()
    h.append(length & 0xff)
    h.append((length >> 8) & 0xff)
    h.append((length >> 16) & 0xff)
    h.append((length >> 24) & 0xff)
    h.append(chksum & 0xff)
    fwhndlr.seek(101)                   # Music length pos: in BYTES
    fwhndlr.write(h)

# Read input file, convert, write output file:
def readConvertWrite(fl, fw, length, signed):
    l = 0
    cs = 0
    while length != 0:
        try:
            ab, length, cs = readBlock(fl, length, cs, signed)      # Read one (256 BYTEs) block
        except:
            print("Input audio file error")
            sys.exit(4)
        fw.write(ab)
        l += 256
    return l, cs







print("\n============================================")
print("Audio file converter for 'WavePlay-SD' stuff")
print("V2.0, BSZ")
print("©2022.06.15.+")
print("============================================")

if len(sys.argv) < 6:
    printHelp(1)
outfile = sys.argv[1]                   # Output file name
infile = sys.argv[3]                    # Input file name
cmd = sys.argv[2].lower()               # Command
musicartist = nameConvert(sys.argv[4])  # Music's Artist
musictitle = nameConvert(sys.argv[5])   # Music's Title
logotype = False
logoba = b''

if len(sys.argv) >= 8:
    try:
        logotype = sys.argv[6].lower()
        fg = open(sys.argv[7], "rb")
        logoba = fg.read(400)
        fg.close()
    except:
        print("Logo file / type error")
        sys.exit(2)

if cmd == 'w':
    try:
        fl = open(infile, "rb")
        ok, freq, length, signed = readWavHeader(fl)
    except:
        print("WAV file / type error")
        sys.exit(3)
    if ok:
        print("WAV mode - file: '%s' / Freq: %s / Length: %s" % (infile, freq, hex(length)))
        header = createHeader(8, 0, freq, musicartist, musictitle, logotype, logoba)
        fw = open(outfile, "wb")
        fw.write(header)
        l, cs = readConvertWrite(fl, fw, length, signed)
        finalizeHeader(fw, 8, l, freq, cs)
        fw.close()
    fl.close()
elif (cmd[0:2] == 'u:') or (cmd[0:2] == 's:'):
    try:
        fl = open(infile, "rb")
        ok, freq, length, signed = readRawHeader(fl, cmd)
    except:
        print("RAW file / type error")
        sys.exit(3)
    if ok:
        print("RAW mode - file: '%s' / Freq: %s \n\t / Length: %s / Sign: %s" % (infile, freq, hex(length), signed))
        header = createHeader(8, 0, freq, musicartist, musictitle, logotype, logoba)
        fw = open(outfile, "wb")
        fw.write(header)
        l, cs = readConvertWrite(fl, fw, length, signed)
        finalizeHeader(fw, 8, l, freq, cs)
        fw.close()
    fl.close()
else:
    print("Unknown mode!")
    printHelp(1)

print("============================================")
