import sys
from pathlib import Path

import numpy as np
from scipy.io.wavfile import write


def pilot_fsk(length: int, frequency: int = 5287, sample_rate: int = 44100):
    t = np.linspace(0, length, int(sample_rate * length), endpoint=False)
    pilot_fsk = 0.5 * np.sin(2 * np.pi * frequency * t)
    pilot_fsk = np.int16(pilot_fsk * 32767)
    return pilot_fsk


def space_fsk(frequency: int = 3958, num_periods: int = 5, sample_rate: int = 44100):
    period_duration = 1 / frequency
    duration = num_periods * period_duration
    t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
    space_fsk = 0.75 * np.sin(2 * np.pi * frequency * t)
    space_fsk = np.int16(space_fsk * 32767)
    return space_fsk


def mark_fsk(frequency: int = 5278, num_periods: int = 9, sample_rate: int = 44100):
    period_duration = 1 / frequency
    duration = num_periods * period_duration
    t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
    mark_fsk = 0.85 * np.sin(2 * np.pi * frequency * t)
    mark_fsk = np.int16(mark_fsk * 32767)
    return mark_fsk


def byte_fsk(byte):
    byte_FSK = np.array(space_fsk())  # start bit
    for i in range(0, 8):
        bit = (byte >> i) & 0b00000001
        if bit:
            byte_FSK = np.append(byte_FSK, mark_fsk())
        else:
            byte_FSK = np.append(byte_FSK, space_fsk())
    byte_FSK = np.append(byte_FSK, mark_fsk())  # stop bit
    return byte_FSK


def injektor_long_positive_pulse():
    return np.array([4920, 18984, 29899, 31024,
                     30849, 30993, 27939, 25050,
                     22357, 18791, 14799, 7851])


def injektor_short_positive_pulse():
    return np.array([1500, 11721, 26596, 32498,
                     26596, 13076, -4856, -22109,
                     -30934, -27874, -17949, -8902])


def injektor_long_negative_pulse():
    return -injektor_long_positive_pulse()


def injektor_short_negative_pulse():
    return -injektor_short_positive_pulse()


def pilot_injektor(length):
    return np.tile(injektor_short_positive_pulse(), length * 4)


def byte_injektor(byte, last_state):
    # start bit
    if last_state == 1:
        injektor_byte = injektor_long_positive_pulse()
    else:
        injektor_byte = injektor_short_negative_pulse()
    last_state = 0

    for i in range(0, 8):
        bit = (byte >> i) & 0b00000001
        if bit:
            if last_state == 1:
                injektor_byte = np.append(injektor_byte, injektor_short_positive_pulse())
            else:
                injektor_byte = np.append(injektor_byte, injektor_long_negative_pulse())
            last_state = 1
        else:
            if last_state == 1:
                injektor_byte = np.append(injektor_byte, injektor_long_positive_pulse())
            else:
                injektor_byte = np.append(injektor_byte, injektor_short_negative_pulse())
            last_state = 0

    # stop bit
    if last_state == 1:
        injektor_byte = np.append(injektor_byte, injektor_short_positive_pulse())
    else:
        injektor_byte = np.append(injektor_byte, injektor_long_negative_pulse())
    last_state = 1
    return injektor_byte, last_state


# Main function
def main(input_file_name: str, output_file_name: str):
    with open(input_file_name, 'rb') as f:
        CAS_file_content = f.read()
    position = CAS_file_content.find(b'data', 0)
    if position < 0:
        print("I cannot find the first 'data' section in the CAS file.")
        print("END")
        raise SystemExit

    position += 8  # skipping the header "data..."
    chunk = CAS_file_content[position:position + 132]
    print("FSK 132 bytes")

    audio_data = pilot_fsk(15)
    for i in range(0, 132):
        audio_data = np.append(audio_data, byte_fsk(chunk[i]))

    position += 140
    turbo_block_number = 1
    while (position := CAS_file_content.find(b'data', position)) > 0:
        a = CAS_file_content[position + 4:position + 6]
        number_of_bytes = int.from_bytes(a, byteorder='little')

        a = CAS_file_content[position + 6:position + 8]
        pilot_length = int.from_bytes(a, byteorder='little')

        position = position + 8
        print("Injektor block", turbo_block_number, "-", number_of_bytes, "bytes")

        pilot = np.array(pilot_injektor(pilot_length))
        audio_data = np.append(audio_data, pilot)
        last_state = 1
        bulk_appends = []
        for i in range(position, position + number_of_bytes):
            pulses, last_state = byte_injektor(CAS_file_content[i], last_state)
            bulk_appends.append(pulses)
        audio_data = np.append(audio_data, bulk_appends)
        position += number_of_bytes
        turbo_block_number += 1

    pilot = np.array(pilot_injektor(300))
    audio_data = np.append(audio_data, pilot)

    audio_data = np.int16(audio_data)
    audio_data_stereo = np.column_stack((audio_data, audio_data))
    # Save WAV file
    write(output_file_name, 44100, audio_data_stereo)

    print("Saved to file: ", output_file_name)
    print("END")


if __name__ == "__main__":
    # Settings
    # input_file_name = "06-brucelee - coelsa injektor.CAS"
    input_file_name = sys.argv[1]
    # input_file_name = "06-brucelee - coelsa injektor.wav"
    output_file_name =  str(Path(input_file_name).with_suffix('.wav'))
    main(input_file_name, output_file_name)
