#include "defs.h"

#define EOL "\r"          /* End Of Line */

int
autodial(struct DevUnit *unit)
{
int rc = FALSE;

    if( unit->du_IntuitionBase = OpenLibrary("intuition.library", 37) ) {
        if( openwindow(unit) ) {
        struct timerequest *tr = &unit->du_TimeRequest;

            /* Initialise the timer request */
            tr->tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
            tr->tr_node.io_Message.mn_Length = sizeof(struct timerequest);
            tr->tr_node.io_Message.mn_ReplyPort = &unit->du_MsgPort;
            tr->tr_node.io_Command = TR_ADDREQUEST;

            OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)tr, 0);
            if( tr->tr_node.io_Error == 0 ) {
            struct IOExtSer *iob = unit->du_iob;
            struct MsgPort *tmp = iob->IOSer.io_Message.mn_ReplyPort;

                /* I'm using my own port for the Serial IO */
                iob->IOSer.io_Message.mn_ReplyPort = &unit->du_MsgPort;

                /* set the serial device up */
                if( unit->du_BaudRate ) {
                    iob->IOSer.io_Command = SDCMD_SETPARAMS;
                    iob->io_Baud = unit->du_BaudRate;

                    DoIO((struct IORequest *)iob);
                }

                /* clear out any crap that might be left around */
                pause(unit, 2);
                iob->IOSer.io_Command = CMD_CLEAR;
                DoIO((struct IORequest *)iob);

                if( !unit->du_Phone || initmodem(unit) ) {
                    if( !unit->du_Phone || connect(unit) ) {
                        if( login(unit) ) {
                            rc = TRUE;
                        }
                    }
                }

                /* restore original MsgPort */
                iob->IOSer.io_Message.mn_ReplyPort = tmp;

                CloseDevice((struct IORequest *)tr);
            }

            closewindow(unit);
        }

        CloseLibrary(unit->du_IntuitionBase);
    }

    return(rc);
}

int
initmodem(struct DevUnit *unit)
{
UBYTE buf[64];
int rc = FALSE;

    dprintf(unit, "Init Modem: %s\n", unit->du_InitString);
    send(unit, unit->du_InitString);
    send(unit, EOL);

    set_timer(unit, 5);

    while( rline(unit, buf, sizeof(buf)) ) {
        if( !strncmp(buf, "OK", 2) ) {
            rc = TRUE;
            break;
        }

        if( !strncmp(buf, "ERROR", 5) ) {
            break;
        }
    }

    clr_timer(unit);
    return( check_abort(unit) ? FALSE : rc );
}

int
connect(struct DevUnit *unit)
{
int connected = FALSE, done = FALSE, aborting = FALSE, timed_out = FALSE;

    while( !done ) {
    char *number = unit->du_Phone;

        while( !done && number ) {
        UBYTE buf[128];

            pause(unit, 2);
            dprintf(unit, "Dialling: %s%s\n", unit->du_DialString, number);
            send(unit, unit->du_DialString);
            send(unit, number);
            send(unit, EOL);

            set_timer(unit, unit->du_DialTimeout);

            while( !done ) {
                if( !rline(unit, buf, sizeof(buf)) ) {
                    send(unit, EOL);
                    done += timed_out;
                    aborting += check_abort(unit);
                    unit->du_Aborting = 0;
                    timed_out += check_timer(unit);
                    clr_timer(unit);
                    set_timer(unit, 5);
                    continue;
                }

                if( !strncmp(buf, "CONNECT", 7) ) {
                    connected = TRUE;
                    done = TRUE;
                    break;
                }

                if( !strncmp(buf, "BUSY", 4) || !strncmp(buf, "NO CARRIER", 10) || !strncmp(buf, "NO ANSWER", 9) ) {
                    done += aborting;
                    break;
                }

                if( !strncmp(buf, "ERROR", 5) || !strncmp(buf, "NO DIALTONE", 11) ) {
                    done = TRUE;
                    break;
                }
            }

            clr_timer(unit);
            number = next_string(number);
        }
    }

    return(connected);
}

int
login(struct DevUnit *unit)
{
char *str = unit->du_SendExpect;
int toggle = FALSE;

    while( str ) {
        if( toggle ) {          /* send */
            dprintf(unit, "Send: \"%s\"\n", str);
            send(unit, str);
            send(unit, EOL);
        }
        else {                  /* expect */
        char *ptr = str;

            dprintf(unit, "Expect: \"%s\"\n", str);

            set_timer(unit, unit->du_Timeout);

            while( *ptr ) {
            int c = rchar(unit);

                if( check_abort(unit) ) {
                    dprintf(unit, "Abort!\n");
                    clr_timer(unit);
                    return(FALSE);      /* aborted */
                }

                if( check_timer(unit) ) {
                    clr_timer(unit);
                    dprintf(unit, "Timeout!\n");
                    display_string(unit, "\nTimed Out!\n");
                    pause(unit, 2);
                    return(FALSE);      /* timed out */
                }

                if( c == *ptr )
                    ptr++;
                else {
                    ptr = str;
                    if( c == *ptr ) ptr++;
                }
            }

            clr_timer(unit);
            dprintf(unit, "Found!\n");
        }

        toggle = !toggle;
        str = next_string(str);       /* next string */
    }

    pause(unit, 2);

    return(TRUE);
}

/*
 *  serial port routines
 *
 */
int
send(struct DevUnit *unit, char *string)
{
struct IOExtSer *iob = unit->du_iob;

    while( *string ) {
        iob->IOSer.io_Command = CMD_WRITE;
        iob->IOSer.io_Length = 1;
        iob->IOSer.io_Data = string++;

        DoIO((struct IORequest *)iob);
    }
}

int
rline(struct DevUnit *unit, UBYTE *buf, int size)
{
int count = 0;

    while( count < size ) {
    int c = rchar(unit);

        if( check_abort(unit) || check_timer(unit) ) return(0);
        buf[count++] = (UBYTE)c;
        if( c == '\n' || c == '\r' ) break;
    }

    return(count);
}

int
rchar(struct DevUnit *unit)
{
UBYTE buf = '\0';
struct IOExtSer *iob = unit->du_iob;

    iob->IOSer.io_Length = 1;
    iob->IOSer.io_Data = &buf;
    iob->IOSer.io_Command = CMD_READ;

    SendIO((struct IORequest *)iob);

    Wait( 1L << unit->du_MsgPort.mp_SigBit | 1L << unit->du_Window->UserPort->mp_SigBit );

    if( !CheckIO((struct IORequest *)iob) )
        AbortIO((struct IORequest *)iob);

    WaitIO((struct IORequest *)iob);
    SetSignal(0L, 1L << unit->du_MsgPort.mp_SigBit);

    /*
    dprintf(unit, "recvd %ld, '%lc' ($%02lx)\n", iob->IOSer.io_Actual, buf, buf);
    */
    display_char(unit, buf);
    return(buf);
}

/*
 *  Timer routines
 *
 */
void
set_timer(struct DevUnit *unit, int seconds)
{
struct timerequest *tr = &unit->du_TimeRequest;

    /* start the timer */
    tr->tr_time.tv_secs = seconds;
    tr->tr_time.tv_micro = 0;
    SendIO((struct IORequest *)tr);

    dprintf(unit, "Timer started for %ld seconds\n", seconds);
}

int
check_timer(struct DevUnit *unit)
{
struct timerequest *tr = &unit->du_TimeRequest;

    return( CheckIO((struct IORequest *)tr) );
}

void
clr_timer(struct DevUnit *unit)
{
struct timerequest *tr = &unit->du_TimeRequest;

    if( !CheckIO((struct IORequest *)tr) ) {
        dprintf(unit, "Aborting Timer Request..\n");
        AbortIO((struct IORequest *)tr);
    }
    else
        dprintf(unit, "Time was Finished!\n");

    WaitIO((struct IORequest *)tr);
    SetSignal(0L, 1L << unit->du_MsgPort.mp_SigBit);
}

void
pause(struct DevUnit *unit, int seconds)
{
struct timerequest *tr = &unit->du_TimeRequest;

    tr->tr_time.tv_secs = seconds;
    tr->tr_time.tv_micro = 0;
    DoIO((struct IORequest *)tr);
}
