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

def pilot_FSK(length):
    frequency = 5278
    sample_rate = 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 = 3958 
    num_periods = 7 
    sample_rate = 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 = 5278 
    num_periods = 9
    sample_rate = 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):
        byte_value = int.from_bytes(byte, byteorder='big')
        bit =  (byte_value >> 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 0 - Injektor_long_positive_pulse()

def Injektor_short_negative_pulse():
    return 0 - 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:
       byte_INJEKTOR = Injektor_long_positive_pulse()
    else:
       byte_INJEKTOR = Injektor_short_negative_pulse()
    last_state = 0
           
    for i in range (0,8):
        byte_value = int.from_bytes(byte, byteorder='big')
        bit =  (byte_value >> i) & 0b00000001
        if bit:
            if last_state == 1:
               byte_INJEKTOR = np.append(byte_INJEKTOR, Injektor_short_positive_pulse())
            else:
               byte_INJEKTOR = np.append(byte_INJEKTOR, Injektor_long_negative_pulse())
            last_state = 1
        else:
            if last_state == 1:
               byte_INJEKTOR = np.append(byte_INJEKTOR, Injektor_long_positive_pulse())
            else:
               byte_INJEKTOR = np.append(byte_INJEKTOR, Injektor_short_negative_pulse())
            last_state = 0
            
    # stop bit
    if last_state == 1:
       byte_INJEKTOR = np.append(byte_INJEKTOR, Injektor_short_positive_pulse())
    else:
       byte_INJEKTOR = np.append(byte_INJEKTOR, Injektor_long_negative_pulse())
    last_state = 1
    return(byte_INJEKTOR, last_state)

def load_binary_file_to_byte_list(filename):
    byte_list = []
    with open(filename, 'rb') as file:
        while True:
            # Read 1 byte from the file
            byte_read = file.read(1)
            if not byte_read:
                break  # End of file reached
            byte_list.append(byte_read) # Append the byte value to the list
    return byte_list # Return the list of byte values

def find_pattern_in_byte_list(byte_list, pattern, start_position):
    pattern_length = len(pattern)
    found = False
    for i in range(start_position, len(byte_list) - len(pattern)  + 1):
        if byte_list[i:i + pattern_length] == pattern:
            found = True
            break            
    if found:
        return i
    else:
        return 0


# Main function
def main():        
    CAS_file_content = load_binary_file_to_byte_list(input_file_name)
    position = find_pattern_in_byte_list(CAS_file_content,  [b'\x64', b'\x61', b'\x74', b'\x61'], 0)
    if position == 0:
        print("I cannot find the first 'data' section in the CAS file.")
        print("END")
        raise SystemExit

    position = 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 = position + 140
    turbo_block_number = 1
    while True:
        position = find_pattern_in_byte_list(CAS_file_content,  [b'\x64', b'\x61', b'\x74', b'\x61'], position)
        if position == 0:
           break

        a = CAS_file_content[position+4:position+6]
        number_of_bytes = int.from_bytes(b''.join(a), byteorder='little')
        
        a = CAS_file_content[position+6:position+8]
        pilot_length = int.from_bytes(b''.join(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
        j = 1
        for i in range(position, position + number_of_bytes):
           pulses, last_state = byte_Injektor(CAS_file_content[i], last_state)
           audio_data = np.append(audio_data, pulses)
           j = j + 1

        position = position + number_of_bytes
        turbo_block_number = 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"
    output_file_name = "output.wav"
    
    main()
