/* #define DEBUG /* */

#include <obdefs.h>
#include <gemdefs.h>
#include <osbind.h>
#include <stdio.h>
#include <fcntl.h>

#define TRUE 1
#define FALSE 0
#define WI_KIND		(SIZER|MOVER|FULLER|CLOSER|NAME|INFO)

#define NO_WINDOW (-1)

#define MIN_WIDTH  (2*gl_wbox)
#define MIN_HEIGHT (3*gl_hbox)

extern int	gl_apid;

int	gl_hchar;
int	gl_wchar;
int	gl_wbox;
int	gl_hbox;	/* system sizes */

int 	phys_handle;	/* physical workstation handle */
int 	handle;		/* virtual workstation handle */
int	wi_handle;	/* window handle */
int	top_window;	/* handle of topped window */

int	xdesk,ydesk,hdesk,wdesk;
int	xold,yold,hold,wold;
int	xwork,ywork,hwork,wwork;	/* desktop and work areas */

int	msgbuff[8];	/* event message buffer */
int	butstate;	/* keycode returned by event-mouse*/
int	keycode;	/* keycode returned by event-keyboard */
int	mx,my;		/* mouse x and y pos. */
int	butdown;	/* button state tested for, UP/DOWN */
int	ret;		/* dummy return variable */

int	hidden;		/* current state of cursor */

int	fulled;		/* current state of window */

int	contrl[12];
int	intin[128];
int	ptsin[128];
int	intout[128];
int	ptsout[128];	/* storage wasted for idiotic bindings */

int work_in[11];	/* Input to GSX parameter array */
int work_out[57];	/* Output from GSX parameter array */
int pxyarray[10];	/* input point array */

/****************************************************************/
/*  GSX UTILITY ROUTINES.					*/
/****************************************************************/

hide_mouse()
{
	if(! hidden){
		graf_mouse(M_OFF,0x0L);
		hidden=TRUE;
	}
}

show_mouse()
{
	if(hidden){
		graf_mouse(M_ON,0x0L);
		hidden=FALSE;
	}
}

/****************************************************************/
/* open virtual workstation					*/
/****************************************************************/
open_vwork()
{
int i;
	for(i=0;i<10;work_in[i++]=1);
	work_in[10]=2;
	handle=phys_handle;
	v_opnvwk(work_in,&handle,work_out);
}

/****************************************************************/
/* set clipping rectangle					*/
/****************************************************************/
set_clip(x,y,w,h)
int x,y,w,h;
{
int clip[4];
	clip[0]=x;
	clip[1]=y;
	clip[2]=x+w;
	clip[3]=y+h;
	vs_clip(handle,1,clip);
}

/****************************************************************/
/* open window							*/
/****************************************************************/
open_window()
{
	wi_handle=wind_create(WI_KIND,xdesk,ydesk,wdesk,hdesk);
	wind_set(wi_handle, WF_NAME," Unix Floppy Converter ",0,0);
	graf_growbox(xdesk+wdesk/2,ydesk+hdesk/2,gl_wbox,gl_hbox,xdesk,
				ydesk,wdesk,hdesk);
	wind_open(wi_handle,xdesk,ydesk,wdesk,hdesk);
	wind_get(wi_handle,WF_WORKXYWH,&xwork,&ywork,&wwork,&hwork);
}

/****************************************************************/
/* find and redraw all clipping rectangles			*/
/****************************************************************/
do_redraw(xc,yc,wc,hc)
int xc,yc,wc,hc;
{
GRECT t1,t2;

	hide_mouse();
	wind_update(TRUE);
	t2.g_x=xc;
	t2.g_y=yc;
	t2.g_w=wc;
	t2.g_h=hc;
	wind_get(wi_handle,WF_FIRSTXYWH,&t1.g_x,&t1.g_y,&t1.g_w,&t1.g_h);
	while (t1.g_w && t1.g_h) {
	  if (rect_intersect(t2,t1,&t1)) {
	    set_clip(t1.g_x,t1.g_y,t1.g_w,t1.g_h);
	    draw_sample();
	  }
	  wind_get(wi_handle,WF_NEXTXYWH,&t1.g_x,&t1.g_y,&t1.g_w,&t1.g_h);
	}
	wind_update(FALSE);
	show_mouse();
}

/****************************************************************/
/*		Accessory Init. Until First Event_Multi		*/
/****************************************************************/

char *buffer, *endBuffer; /* memory for data */
int drive;
long freeMem;
char *index(), *rindex();

main()
{
	appl_init();
	phys_handle=graf_handle(&gl_wchar,&gl_hchar,&gl_wbox,&gl_hbox);
	wind_get(0, WF_WORKXYWH, &xdesk, &ydesk, &wdesk, &hdesk);
	open_vwork();
        open_window();
	graf_mouse(ARROW,0x0L);

	hidden=FALSE;
	fulled=FALSE;
	butdown=TRUE;

	freeMem = Malloc(-1L);
	freeMem -= 10240;
	if (freeMem < 20480) {
	    form_alert(1, "[3][Not enough memory][Ok]");
	    GemExit();
	}
	buffer = lmalloc(freeMem);

	multi(); 
}

/* colas  --- */

#define safe_malloc_body(func) \
        char *ptr = func(s); \
	if (!ptr) {free(buffer); \
	    form_alert(1, "[3][Out of memory|quitting program][Ok]"); \
	    GemExit();} \
	return ptr

char *lsafe_malloc(s) long s;{safe_malloc_body(lmalloc);}
char *safe_malloc(s) int s;{safe_malloc_body(malloc);}

char *mprintf1(), *mprintf2(), *mprintf3();
jmp_buf start;

info(s) char *s;{wind_set(wi_handle, WF_INFO,s ,0,0);}

multi()
{
int event;

      setjmp(start);
      do {
	event = evnt_multi(MU_MESAG | MU_BUTTON | MU_KEYBD,
			1,3,butdown,
			0,0,0,0,0,
			0,0,0,0,0,
			msgbuff,0,0,&mx,&my,&butstate,&ret,&keycode,&ret);

	info("Click in window to begin, close window or Undo to quit");
	wind_update(TRUE);

	if (event & MU_MESAG)
	  switch (msgbuff[0]) {

	  case WM_REDRAW:
	    do_redraw(msgbuff[4],msgbuff[5],msgbuff[6],msgbuff[7]);
	    break;

	  case WM_NEWTOP:
	  case WM_TOPPED:
	    wind_set(wi_handle,WF_TOP,0,0,0,0);
	    break;

	  case WM_SIZED:
	  case WM_MOVED:
	    if(msgbuff[6]<MIN_WIDTH)msgbuff[6]=MIN_WIDTH;
	    if(msgbuff[7]<MIN_HEIGHT)msgbuff[7]=MIN_HEIGHT;
	    wind_set(wi_handle,WF_CURRXYWH,msgbuff[4],msgbuff[5],
				msgbuff[6],msgbuff[7]);
	    wind_get(wi_handle,WF_WORKXYWH,&xwork,&ywork,&wwork,&hwork);
	    break;

	  case WM_FULLED:
	    if(fulled){
		wind_calc(WC_WORK,WI_KIND,xold,yold,wold,hold,
				&xwork,&ywork,&wwork,&hwork);
		wind_set(wi_handle,WF_CURRXYWH,xold,yold,wold,hold);}
	    else{
		wind_calc(WC_BORDER,WI_KIND,xwork,ywork,wwork,hwork,
				&xold,&yold,&wold,&hold);
		wind_calc(WC_WORK,WI_KIND,xdesk,ydesk,wdesk,hdesk,
				&xwork,&ywork,&wwork,&hwork);
		wind_set(wi_handle,WF_CURRXYWH,xdesk,ydesk,wdesk,hdesk);
	    }
	    fulled ^= TRUE;
	    break;

	  } /* switch (msgbuff[0]) */

	if (event & MU_BUTTON) {
	  if(butdown) butdown = FALSE;
	  else butdown = TRUE;

	  if (!butstate) do_main_alert();
	}
	  if(event & MU_KEYBD){
	     do_redraw(xwork,ywork,wwork,hwork);
	     switch ((unsigned) keycode) {
	        case 0x6200: /* Help */
	        help();break;
	        case 0x6100: /* Undo */
	        GemExit();break;
	        case 0x7700: /* Home: resets button state */
	        butdown =~ 1;
	        break;
	        default:
	        do_main_alert();
	     }
	  }
	
	wind_update(FALSE); 

      }while(!((event & MU_MESAG) && (msgbuff[0] == WM_CLOSED)));
      GemExit();
}
      
GemExit() {
      wind_close(wi_handle);
      graf_shrinkbox(xwork+wwork/2,ywork+hwork/2,gl_wbox,gl_hbox,
					xwork,ywork,wwork,hwork);
      wind_delete(wi_handle);
      v_clsvwk(handle);
      appl_exit();
      exit(0);
}

draw_sample()
{
int temp[4];
	vsf_interior(handle,2);
	vsf_style(handle,8);
	vsf_color(handle,0);
	temp[0]=xwork;
	temp[1]=ywork;
	temp[2]=xwork+wwork-1;
	temp[3]=ywork+hwork-1;
	v_bar(handle,temp);		/* blank the interior */
}

do_main_alert()
{
	char *s;
	int res = form_alert(1, s=mprintf1(
	"[2][Select transfer:|Buffer size: %ldK][Unix -> ST|St -> Unix|Help]",
	freeMem/1024));
	free(s);
	switch(res) {
		case 1: raw2tar(); break;
		case 2: tar2raw(); break;
		default: help(); break;
	}	
}

help()
{
        /* max text is 5lines of 32 chars */
	switch(form_alert(1,"[1][\
Converts  disks made on an unix|\
machine by a tar cf /dev/floppy|\
(double density, 2 sides, 80trk|\
9sect) to/from atari st files. |\
     (c) Colas Nahaboo May 1990]\
[Ok]")) {
	case 1: break;
	}
}

int size,s,s_max,track, side, block;
char *buf;
init_data() {
	size=s=track=side=block=0;
	s_max=512 * 9 * 2 * 80;
	buf=buffer;
}

raw2tar()
{
    init_data();
    switch(form_alert(1,"[1][Insert raw unix disk|to read from in drive:][ A | B |Cancel]")) {
        case 1: drive = 0;break;
        case 2: drive = 1;break;
	case 3: longjmp(start,1);
    }
    for (;;) {
            if(convert_track_r2t(1, 0)) break;
	    if(convert_track_r2t(0, 1)) break;
    }
    switch(form_alert(1,"[3][Insert disk to write|files to in drive:][ A | B |Cancel]")) {
        case 1: drive = 0;break;
        case 2: drive = 1;break;
	case 3: longjmp(start,1);
    }
    detar(buffer, endBuffer);
    form_alert(1,"[1][Files written][Ok]");
}

convert_track_r2t(next_side, incr_track)
int next_side, incr_track;
{
    char mess[80];

    if ((buf - buffer) > (freeMem - 512*9)) 
    	fatal_error(0, "Disk does not fit in memory");
    if (Floprd(buf, 0L, drive, 1, track, side, 9)) {
        fatal_error(1, mprintf2("Read error|track %ld|side %ld",
		(long) track, (long) side));
    }
    block = print_names(block, s, s +9 < s_max ? s+9 : s_max);
    side = next_side; s += 9; track += incr_track;
    buf += 9*512;
    if (s >= s_max) {
    	return 1;
    } else {
       	return 0;
    }
}

int print_names(block, s, s_end)
int block, s, s_end;
{
    char *tmp;
    while (block >= s && block < s_end) {
    	if (buf[(block-s) * 512] == '\0') {
    	/* end of tape reached */
    	    info(tmp=mprintf1("End of file at block %ld", (long) block));
	    free(tmp);
    	    s_max = block;
    	    endBuffer = buf + (block-s) * 512;
    	    block = -1;
    	} else {
    	    long size;
    	    sscanf(buf + (block-s) * 512 + 124, " %lo", &size);
    	    info(tmp=mprintf3("Block %5ld: %.100s  -  %ldK", (long) block, 
	    	buf + (block-s) * 512, size/1024));
	    free(tmp);
	    if (size)
    	    	block = block + 2 + (int) ((size - 1)/ 512);
	    else
		block++;
     	}
    }
    return block;
}

detar(block, end)
char *block, *end;
{
    long size;
    int fd;
    char path[100], name[100], *s;

    info("Reading unix floppy in memory...");
    Dsetdrv(drive);
    Dsetpath("\\");
    while (block < end) {
    	sscanf(block + 124, " %lo", &size);
        FixPath(block, path, name);
        info(s=mprintf3("Creating %s\\%s (%ldK)", path, name,
            (size+1023)/1024));
	free(s);
	CreateDir(path);
        if (name[0]) { /* create normal file */
	    fd = Fcreate(s=mprintf2("%s\\%s", path, name), 0);
	    free(s);
	    if (!fd || Fwrite(fd, size, block + 512) != size)
	        fatal_error(1, mprintf1("cannot create file|%s", name));
            Fclose(fd);      
	    block = block + (2 + ((size - 1)/ 512)) *512;
	} else { /* create directory */
	    if(path[0] && Dcreate(path))
	        fatal_error(1, mprintf1("cannot create directory|%s", name));
	    block += 512;
	}
    }        
}

/* parses path in  dir & name. dir: name ="" */
FixPath(string, path, name)
char *string, *path, *name;
{
    char *p;
    if (index(string, ' ') || index(string, '\n'))
        fatal_error(1, mprintf1("Bad file name: %s", string));
    if (string[0] == '.' && string[1] == '/')
        strcpy(path, string+2);
    else
        strcpy(path, string);
    p = index(path, '/');
    while(p) {
        *p = '\\';
	p = index(p, '/');
    }

    p = rindex(path, '\\');
    if(p) {
        *p='\0';
        strcpy(name, p+1);
    } else {
        strcpy(name, path);
        path[0] = '\0';
    }
#ifdef DEBUG
    p=mprintf3("[0][FixPath: string path name|(%s)|(%s)|(%s)][Continue]",
        string, path, name);
    form_alert(1, p);
    free(p);
    p=mprintf3("[0][(adresses: string path name|(%lx)|(%lx)|(%lx)][Continue]",
        string, path, name);
    form_alert(1, p);
    free(p);
#endif
    
}

CreateDir(path)
char *path;
{
    if(path[0] && !(Fattrib(path,0,0) & 0x10)) {
        CreateParentDir(path);
	if(Dcreate(path))
	    fatal_error(1, mprintf1("cannot create directory|%s", path)); 
    }
}

CreateParentDir(path)
char *path;
{
    char *p = rindex(path, '\\');
    char *parent;
#ifdef DEBUG
    parent=mprintf1("[0][CreateParentDir(%s)][Continue]", path);
    form_alert(1, parent);
    free(parent);
#endif
    if(p && p != path){
        parent = safe_malloc(p - path + 1);
	strncpy(parent, path, p-path);
	parent[p-path] = 0;
	CreateDir(parent);
	free(parent);
    }
}

fatal_error(free_string, s)
int free_string;
char *s;
{
	char *tmp;
	form_alert(1, tmp=mprintf1("[3][Error|%s][Abort]", s));
	free(tmp);
	if (free_string) free(s);
	longjmp(start, 1);
}

/****************************************************************************/

tar2raw()
{
	static char default_path[80];
	static char default_name[80];
	int  button;

	if (!default_path[0]) {
		strcpy(default_path, "A:\\*.*");
		strcpy(default_name, "");
	}
	fsel_input(default_path, default_name, &button);
	if (button)
		printf("You have selected the file <%s>.\n", default_name);
	else
		printf("You have cancelled the file selection.\n");
    for (;;) {
        if(convert_track_t2r(1, 0)) break;
	if(convert_track_t2r(0, 1)) break;
    }
}

convert_track_t2r(next_side, incr_track)
int next_side, incr_track;
{
int fd;
	static long total_size;
	int read_size = read(fd, buf, 512 * 9);
	total_size += read_size;
	if (read_size != 512 * 9) {
	    s_max=total_size/512/9;
	    bzero(buf+read_size, 512*9 - read_size);
	}
	if (Flopwr(buf, 0L, 0, 1, track, side, 9)) {
	    char mess[80];
	    sprintf(mess, "error writing track %d, side %d\n", track, side);
	    fatal_error(mess);
	}
	block = print_names(block, s, s +9 < s_max ? s+9 : s_max);
	side = next_side; s += 9; track += incr_track;
	if (read_size != 512 * 9) {
	    long pos = lseek(fd, 0L, 2);
	    if (pos == -1) {
	        fatal_error("could not reach end of file\n");
	    } else if (pos != total_size) {
	    	fatal_error("cannot read whole file\n");
	    } else if (read_size > 512 * 7) {
	    	return 0;
	    } else {
	    	return 1;
	    }
	} else
	    return 0;
}

/* strings ops */

char mprintfBuf[256];

char *
mprintf1(format, arg1)
char *format, *arg1;
{
	char *string;
	sprintf(mprintfBuf, format, arg1);
	string = safe_malloc(strlen(mprintfBuf) + 1);
	strcpy(string, mprintfBuf);
	return string;
}

char *
mprintf2(format, arg1, arg2)
char *format, *arg1, *arg2;
{
	char *string;
	sprintf(mprintfBuf, format, arg1, arg2);
	string = safe_malloc(strlen(mprintfBuf) + 1);
	strcpy(string, mprintfBuf);
	return string;
}

char *
mprintf3(format, arg1, arg2, arg3)
char *format, *arg1, *arg2, *arg3;
{
	char *string;
	sprintf(mprintfBuf, format, arg1, arg2, arg3);
	string = safe_malloc(strlen(mprintfBuf) + 1);
	strcpy(string, mprintfBuf);
	return string;
}


