#if not(CBM)
#warn cbm_file module should be only used on Commodore targets
#endif

import string
import err

byte __last_used_device @$ba
inline byte last_used_device() {
    byte device
    device = __last_used_device
    if device == 0 { device = 8 }
    return device
}

void load_file(byte device, pointer name) {
    setnamz(name)
    setlfs(1, device, 1)
    asm {
        lda #0
        jsr load
        ? jmp __handle_disk_err
    }
}

void load_file_at(byte device, pointer name, pointer at) {
    setnamz(name)
    setlfs(1, device, 0)
    asm {
        lda #0
        ? ldx at
        ? ldy at+1
        jsr load
        ? jmp __handle_disk_err
    }
}

asm void __handle_disk_err() {
    bcs __handle_disk_err_failed
    lda #err_ok
    ? jmp __handle_disk_err_store
    __handle_disk_err_failed:
    ora #$40
    jsr $FFD2
    and #$BF
    lsr
    eor #2
    bne __handle_disk_err_not_4_or_5
    lda #err_nofile
    bcc __handle_disk_err_store
    lda #err_nodevice
    [ $2c ]
    __handle_disk_err_not_4_or_5:
    lda #err_fail
    __handle_disk_err_store:
    ? sta errno
    ? rts
}

void save_file(byte device, pointer name, pointer start, word length) {
    setnamz(name)
    setlfs(1, device, 0)
    word end
    end = start + length
    asm {
        lda #start
        ? ldx end
        ? ldy end+1
        jsr save
        ? jmp __handle_disk_err
    }
}

inline void setnamz(pointer name) {
    setnam(name, strzlen(name))
}

array __cbm_cmd_buffer[64]

void exec_disk(byte device, pointer command) {
    setnamz(command)
    setlfs(1, device, 15)
    open()
    close(1)
}

void __exec_disk(byte device) {
    setnamz(__cbm_cmd_buffer)
    setlfs(1, device, 15)
    open()
    close(1)
}

void delete_file(byte device, pointer name) {
    byte i
    byte length
    __cbm_cmd_buffer[0] = 's'
    __cbm_cmd_buffer[1] = '0'
    __cbm_cmd_buffer[2] = ':'
    length = strzlen(name)
    for i,0,parallelto,length {
    __cbm_cmd_buffer[i + 3] = name[i]
    }
    __exec_disk(device)
}

void initialize_disk(byte device) {
    __cbm_cmd_buffer[0] = 'i'
    __cbm_cmd_buffer[1] = '0'
    __cbm_cmd_buffer[2] = 0
    __exec_disk(device)
}

void validate_disk(byte device) {
    __cbm_cmd_buffer[0] = 'v'
    __cbm_cmd_buffer[1] = '0'
    __cbm_cmd_buffer[2] = 0
    __exec_disk(device)
}

void format_disk(byte device) {
    setnam(__cmd_format_disk, __cmd_format_disk.length)
    setlfs(1, device, 15)
    open()
    close(1)
}

const byte MODE_READ = 0
const byte MODE_WRITE = 1
// const byte MODE_OVERWRITE = 3 // TODO: SAVE@ bug?

void open_file(byte device, pointer name, byte fd, byte mode) {
    byte length
    byte i
    __cbm_cmd_buffer[0] = '0'
    __cbm_cmd_buffer[1] = ':'
    length = strzlen(name)
    for i,0,parallelto,length {
        __cbm_cmd_buffer[i + 2] = name[i]
    }
    if length < 3 || __cbm_cmd_buffer[length] != ',' || (__cbm_cmd_buffer[length+1] != 'r' && __cbm_cmd_buffer[length+1] != 'w') {
        strzappendchar(__cbm_cmd_buffer, ',')
        if mode & MODE_WRITE != 0 {
            strzappendchar(__cbm_cmd_buffer, 'w')
        } else {
            strzappendchar(__cbm_cmd_buffer, 'r')
        }
        length += 2
    }
    setnam(__cbm_cmd_buffer, length + 2)
    setlfs(fd, device, fd)
    asm {
        jsr open
        ? jmp __handle_disk_err
    }
}

void close_file(byte fd) {
    asm {
        ? lda fd
        ? jsr close
        ? jmp __handle_disk_err
    }
}

void __translate_st_to_errno() {
    byte st
    st = readst()
    if st == 0 {
        errno = err_ok
    } else if st & 0x80 != 0 {
        errno = err_nodevice
    } else if st & 0x40 != 0 {
        errno = err_eof
    } else {
        errno = err_fail
    }
}

byte getbyte_safe() {
    byte b
    b = getchar()
    __translate_st_to_errno()
    return b
}

void putbyte_safe(byte b) {
    putchar(b)
    __translate_st_to_errno()
}

array __cmd_format_disk = "n0:disk,01"z


void rename_file(byte device, pointer old_name, pointer new_name) {
    __cbm_cmd_buffer[0] = 'r'
    __cmd_rename_copy_common(device, old_name, new_name)
}

void copy_file(byte device, pointer old_name, pointer new_name) {
    __cbm_cmd_buffer[0] = 'c'
    __cmd_rename_copy_common(device, old_name, new_name)
}

void __cmd_rename_copy_common(byte device, pointer old_name, pointer new_name) {
    __cbm_cmd_buffer[1] = '0'
    __cbm_cmd_buffer[2] = ':'
    __cbm_cmd_buffer[3] = 0
    strzappend(__cbm_cmd_buffer, new_name)
    strzappendchar(__cbm_cmd_buffer, '=')
    strzappend(__cbm_cmd_buffer, old_name)
    __exec_disk(device)
}