#include <stdio.h>

#include <exec/types.h>
#include <exec/ports.h>
#include <exec/io.h>

#include <devices/trackdisk.h>

#include <proto/exec.h>

/* #include "diskstruct.h" */
#include "read.h"

#define CACHESIZE 32

struct IOStdReq *ior;
struct MsgPort *p;
char __chip mybuf[512*11];      /* Track-buffer */

struct trackbuffer {
    short sectormask;
    short tracknumber;
    UBYTE *trackdata;
} tbuf[CACHESIZE] = {
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
    { 0x07FF, -1, NULL },
};

int D_Init()
{
    p = CreatePort( 0, 0 );

    if( p == NULL ) return 1;

    ior = CreateStdIO( p );

    if( ior == NULL ) {
        DeletePort( p );
        return 1;
    }

    if( !OpenDevice( "trackdisk.device", 0, (struct IORequest *)ior, 0 ) ) {

        ior->io_Command = TD_MOTOR;
        ior->io_Length = 1;
        DoIO( (struct IORequest *)ior );
    }

    return 0;
}

void D_Clean()
{
    int i;

    ior->io_Command = TD_MOTOR;
    ior->io_Length = 0;
    DoIO( (struct IORequest *)ior );

    CloseDevice( (struct IORequest *)ior );

    DeleteStdIO( ior );
    DeletePort( p );

    for( i = 0; i < CACHESIZE; i++ ) {
        if( tbuf[i].trackdata != NULL ) {
            free( tbuf[i].trackdata );
        }
    }
}

char map[11*160];

UBYTE *ReadBlock( int i, int flag )
{
    int trk, k, save;
    struct trackbuffer *tb;
    UBYTE *b, *bx;

    if( flag & MAPON ) {
        if( map[i] ) {
            printf( "\nDuplicate Block (%d)\n", i );
            return NULL;
        }

        map[i] = 1;
    }

    trk = i/11;
    i %= 11;
    bx = (UBYTE*)malloc( 512 );

    if( bx == NULL ) {
        return NULL;
    }

    save = -1;
    tb = NULL;

    for( k = 0; k < CACHESIZE; k++ ) {
        if( tbuf[k].tracknumber == trk ) {
            tb = &tbuf[k];
        }
        if( tbuf[k].tracknumber < 0 ) {
            save = k;
        }
    }

    if( tb == NULL ) {
        ior->io_Command = CMD_READ;
        ior->io_Length = 512*11;
        ior->io_Data = (APTR)mybuf;
        ior->io_Offset = trk*11*512;
        if( DoIO( (struct IORequest *)ior ) ) {
            free( bx );
            return NULL;
        }
        if( save >= 0 ) {
            tb = &tbuf[save];
            b = (UBYTE*)malloc( 11*512 );
            if( b != NULL ) {
                tb->trackdata = b;
                tb->tracknumber = trk;
                tb->sectormask = 0x07FF;
                memcpy( b, mybuf, 11*512 );
            } else {
                b = mybuf;
                tb = NULL;
            }
        } else {
            b = mybuf;
        }
    } else {
        b = tb->trackdata;
    }

    memcpy( bx, &b[i*512], 512 );

    if( tb != NULL ) {
        tb->sectormask &= ~(1<<i);
        if( tb->sectormask == 0 ) {
            free( b );
            tb->tracknumber = -1;
        }
    }
    return bx;
}

UBYTE __chip wbuf[11*512];
int cursec = 0;
int curtrk = 0;

int WriteBlock( UBYTE *b )
{
    memcpy( &wbuf[cursec*512], b, 512 );

    if( ++cursec >= 11 ) {
        cursec = 0;
        ior->io_Command = CMD_WRITE;
        ior->io_Length = 512*11;
        ior->io_Data = (APTR)wbuf;
        ior->io_Offset = curtrk*11*512;
        if( DoIO( (struct IORequest *)ior ) ) {
            return -1;
        }
        curtrk++;
    }

    return 0;
}

int FillBlock()
{
    UBYTE local[512];

    memset( local, 0, 512 );

    return WriteBlock( local );
}
