//
//  disk.c
//  Steve ][
//
//  Created by Tamas Rudnai on 2/15/20.
//  Copyright © 2019, 2020 Tamas Rudnai. All rights reserved.
//
// This file is part of Steve ][ -- The Apple ][ Emulator.
//
// Steve ][ is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Steve ][ is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Steve ][.  If not, see <https://www.gnu.org/licenses/>.
//


#include <stdlib.h>

#include "disk.h"
#include "6502.h"
#include "common.h"
#include "woz.h"
#include "speaker.h"


disk_t disk = {
    { 0, 0, 0 },    // phase
    0,              // clk_last_access
    0,              // clk_since_last_read
    0,              // drive number
};

const int diskAccelerator_frames  = 2;
int diskAccelerator_count = 0;
int diskAccelerator_speed = 25; // if less than actual CPU speed means no acceleration
int diskAccelerator_enabled = 0;
int disk_sfx_enabled = 1;

// motor position from the magnet state
// -1 means invalid, not supported
const int magnet_to_Poistion[16] = {
//   0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
       -1,   0,   2,   1,   4,  -1,   3,  -1,   6,   7,  -1,  -1,   5,  -1,  -1,  -1
};

const int position_to_direction[8][8] = {
//     N  NE   E  SE   S  SW   W  NW
//     0   1   2   3   4   5   6   7
    {  0,  1,  2,  3,  0, -3, -2, -1 }, // 0 N
    { -1,  0,  1,  2,  3,  0, -3, -2 }, // 1 NE
    { -2, -1,  0,  1,  2,  3,  0, -3 }, // 2 E
    { -3, -2, -1,  0,  1,  2,  3,  0 }, // 3 SE
    {  0, -3, -2, -1,  0,  1,  2,  3 }, // 4 S
    {  3,  0, -3, -2, -1,  0,  1,  2 }, // 5 SW
    {  2,  3,  0, -3, -2, -1,  0,  1 }, // 6 W
    {  1,  2,  3,  0, -3, -2, -1,  0 }, // 7 NW
};


const uint8_t phy2log_dos33[16] = {
    0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15
};
const uint8_t log2phy_dos33[16] = {
    0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15
};

const uint8_t phy2log_pascal[16] = {
    0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15
};
const uint8_t log2phy_pascal[16] = {
    0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15
};

const uint8_t phy2log_cpm[16] = {
    0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5
};
const uint8_t log2phy_cpm[16] = {
    0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13
};



void disk_accelerator_speedup() {
    if ( ( diskAccelerator_enabled ) && ( FRAME(diskAccelerator_speed) >= clk_6502_per_frm ) ) {
        clk_6502_per_frm =
        clk_6502_per_frm_max = FRAME(diskAccelerator_speed);  // clk_6502_per_frm_diskAccelerator;
        diskAccelerator_count = diskAccelerator_frames;
    }
}

void disk_phase() {

    int position = magnet_to_Poistion[disk.phase.magnet];
    if ( position >= 0 ) {
        int lastPosition = disk.phase.count & 7;
        int direction = position_to_direction[lastPosition][position];
    
        disk.phase.count += direction;
        if( disk.phase.count < minDiskPhaseNum ) {
            disk.phase.count = minDiskPhaseNum;
            spkr_play_disk_ioerr();
        }
        else
        if( disk.phase.count > maxDiskPhaseNum ) {
            disk.phase.count = maxDiskPhaseNum;
            spkr_play_disk_ioerr();
        }
        else {
            spkr_play_disk_arm();
        }
        
        // TODO: Add track positioning sfx
//        spkr_toggle();
        
//        printf("Head Position: p:%d d:%d l:%d: ph:%u)\n", position, direction, lastPosition, disk.phase.count);
                
        disk.clk_last_access = m6502.clktime;
        disk_accelerator_speedup();
    }
    else {
        // invalid magnet config
    }
    

    
//    printf("\n");
}


void disk_phase_on( uint8_t currentMagnet ) {
    disk.phase.magnet |= 1 << currentMagnet;
    disk_phase();
}


void disk_phase_off( uint8_t currentMagnet ) {
    disk.phase.magnet &= ~(1 << currentMagnet);
    disk_phase();
}

void disk_motor_on() {
    spkr_play_disk_motor();
    spkr_stop_disk_motor( -1 );
}

void disk_motor_off() {
    spkr_stop_disk_motor( 3 * fps ); // 3 second delay
}

uint8_t disk_read() {
    dbgPrintf("io_DISK_READ (S%u)\n", 6);
    
    disk.clk_last_access = m6502.clktime;
    disk_accelerator_speedup();

    // Debug disk read
//    spkr_toggle();

    if ( disk.drive ) {
        return rand();
    }
    
    return woz_read();
}

