/*
 * $Id: atariaes.trm,v 1.2 1995/06/19 14:27:58 drd Exp $
 *
 */

/* GNUPLOT - atari.trm */
/*
 * Copyright (C) 1992, 1993
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the modified code.  Modifications are to be distributed
 * as patches to released version.
 *
 * This software  is provided "as is" without express or implied warranty.
 *
 * This file is included by ../term.c.
 *
 * This terminal driver supports:
 *   Atari Screens working with the normal VDI
 *     (this should include TT and big screens)
 *
 * AUTHORS
 *  Alexander Lehmann
 *  HE Koechling
 *
 * send your comments or suggestions to (info-gnuplot@dartmouth.edu).
 *
 * ATARI-related comments please to alexlehm@iti.informatik.th-darmstadt.de
 *
 */

#ifndef GOT_DRIVER_H
#include "driver.h"
#endif

#ifdef TERM_REGISTER
register_term(atari)
#endif

#ifdef TERM_PROTO

/* function-prototypes */
TERM_PUBLIC void ATARI_options __P((void));
TERM_PUBLIC void ATARI_init __P((void));
TERM_PUBLIC void ATARI_reset __P((void));
TERM_PUBLIC void ATARI_graphics __P((void));
TERM_PUBLIC void ATARI_text __P((void));
TERM_PUBLIC void ATARI_move __P((unsigned int x, unsigned int y));
TERM_PUBLIC void ATARI_vector __P((unsigned int x, unsigned int y));
TERM_PUBLIC void ATARI_linetype __P((int lt));
TERM_PUBLIC void ATARI_put_text __P((unsigned int x, unsigned int y, char *str));
TERM_PUBLIC int  ATARI_text_angle __P((int ang));
TERM_PUBLIC int  ATARI_justify_text __P((enum JUSTIFY mode));
TERM_PUBLIC void ATARI_point __P((unsigned int x, unsigned int y, int number));

/* default to hi-res */
#define ATARI_XMAX 640
#define ATARI_YMAX 400
#define ATARI_VCHAR 16
#define ATARI_HCHAR 8
#define ATARI_HTIC (ATARI_XMAX/100)
#define ATARI_VTIC ATARI_HTIC

#define GOT_ATARI_PROTO
#endif /* TERM_PROTO */

#ifndef TERM_PROTO_ONLY
#ifdef TERM_BODY

#ifdef __PUREC__
/* why did they have to change these names ??? */
#include <aes.h>
#include <vdi.h>
#include <tos.h>
#else /* !__PUREC__ i.e. __GNUC__, maybe others */
#include <aesbind.h>
#include <vdibind.h>
#include <osbind.h>
extern short _global[];
#endif

#include <ctype.h>

static void open_window __P((void));
static void close_window __P((void));
static void text_mode __P((void));
static void mouse_mode __P((void));
static void application_exit __P((void));
void application_init __P((void));
static int intersect __P((int *x1, int *y1, int *w1, int *h1, int x2, int y2, int w2, int h2));
static void walk_rects __P((void (*draw)(int, int, int, int), int x1, int y1, int w1, int h1 ));
static void clear __P((int x, int y, int w, int h));
static void process_message __P((int *msg));
long poll_events __P((int waitkey));
static void f_line __P((int x, int y, int w, int h));
static void flush_line __P((void));
static void put_text __P((int x, int y, int w, int h));

static int multi_aes;
static int window_id=-1;
static int vdi_handle=-1;
static int win_xpos, win_ypos;
static int win_xsize,win_ysize;

#define ATARI_yc(y) (ATARI_maxycoord-(y))
static int ATARI_linetypes[] = {
        0xffff, 0x1111,
        0xffff, 0x5555, 0x3333, 0x7777,
        0x3f3f, 0x0f0f, 0x5f5f, 0xe4e4, 0x55f5};
#define ATARI_LINETYPES_MAX     (sizeof(ATARI_linetypes)/sizeof(int))
static int ATARI_lt;
static int ATARI_maxycoord;
static int ATARI_rotation;
static int ATARI_numcolors;
static int pxy[128];            /* Maximum of 64 pixels per v_pline */
static int pxy_index;
static int ATARI_colors[16];
static int ATARI_savecolors[16][3];
static int ATARI_numpalette;
#define ATARI_c_height_default 6 /* well, well ...               */
static int ATARI_c_height = ATARI_c_height_default;

static int cursor_is_on=FALSE;
static int put_text_x;
static int put_text_y;
static char *put_text_str;

/* don't change this without changing process_message() below */
#define WINTYPE (NAME|CLOSER|FULLER|MOVER|SIZER)

static void open_window(void)
{
  int x,y,w,h;

  if(window_id==-1) {
    wind_calc(WC_BORDER, WINTYPE, win_xpos, win_ypos,
                win_xsize, win_ysize, &x, &y, &w, &h);
    window_id=wind_create(WINTYPE, x, y, w, h);

    if(window_id<0) {
        window_id=-1;
        int_error("Can't create window", NO_CARET);
    }

    wind_set(window_id, WF_NAME, "gnuplot output");
    wind_open(window_id, x, y, w, h);
  }
}

static void close_window(void)
{
  if(window_id!=-1) {
    wind_close(window_id);
    wind_delete(window_id);
    window_id=-1;
  }
}

static void text_mode(void)
{
  if(!multi_aes) {
    if(!cursor_is_on) {
      cursor_is_on=TRUE;
      graf_mouse(M_OFF, NULL);
      fprintf(stderr, "\033e");
    }
    fprintf(stderr, "\033E");
  }
}

static void mouse_mode(void)
{
  if(!multi_aes) {
    if(cursor_is_on) {
      cursor_is_on=FALSE;
      graf_mouse(M_ON, NULL);
      fprintf(stderr, "\033f");
    }
  }
}

static void application_exit(void)
{
  close_window();

  if (vdi_handle!=-1) {
    v_clsvwk(vdi_handle);
  }
  mouse_mode();
}

void application_init(void)
{
  if (aesid < 0) {
    if ((aesid = appl_init()) < 0)
      int_error("APPL_INIT failed !",NO_CARET);
  }

#ifdef __PUREC__
  multi_aes = _GemParBlk.global[1] == -1;
#else
  /* tested with gcc only */
  multi_aes = _global[1] == -1;
#endif

  if(!multi_aes)
    graf_mouse(ARROW, NULL);
  else
    menu_register(aesid,"  Terminal: atari");

  text_mode();
  atexit(application_exit);
}

static int intersect(int *x1, int *y1, int *w1, int *h1, int x2, int y2, int w2, int h2)
{
  if(*x1>x2) {
    w2-=*x1-x2;
    x2=*x1;
  } else {
    (*w1)-=x2-*x1;
    *x1=x2;
  }
  if(*y1>y2) {
    h2-=*y1-y2;
    y2=*y1;
  } else {
    (*h1)-=y2-*y1;
    *y1=y2;
  }
  if (*w1>w2) {
    *w1=w2;
  } else {
    w2=*w1;
  }
  if (*h1>h2) {
    *h1=h2;
  } else {
    h2=*h1;
  }

  return (*w1)>0 && (*h1)>0;
}

static void walk_rects(void (*draw)(int,int,int,int),int x1, int y1, int w1, int h1)
{
  int x,y,w,h;
  int pxy[4];

  wind_update(BEG_UPDATE);
  graf_mouse(M_OFF, NULL);

  wind_get(window_id, WF_FIRSTXYWH, &x, &y, &w, &h);
  while(w>0 && h>0) {
    if(intersect(&x, &y, &w, &h, x1, y1, w1, h1)) {
      pxy[0]=x;
      pxy[1]=y;
      pxy[2]=x+w-1;
      pxy[3]=y+h-1;
      vs_clip(vdi_handle, 1, pxy);
      (*draw)(x, y, w, h);
    }
    wind_get(window_id, WF_NEXTXYWH, &x, &y, &w, &h);
  }
  vs_clip(vdi_handle, 0, pxy);
  graf_mouse(M_ON, NULL);
  wind_update(END_UPDATE);
}

static void clear(int x, int y, int w, int h)
{
  static MFDB mfdb={NULL};
  int pxy[8];

  pxy[0]=pxy[4]=x;
  pxy[1]=pxy[5]=y;
  pxy[2]=pxy[6]=x+w-1;
  pxy[3]=pxy[7]=y+h-1;

  vro_cpyfm( vdi_handle, ALL_WHITE /*0*/, pxy, &mfdb, &mfdb );
}

static void process_message(int *msg)
{
  static int is_fulled=FALSE;
  static int small_xpos, small_ypos, small_xsize, small_ysize;
  int x,y,w,h;

  if(window_id==-1 || msg[3]!=window_id) return;

  switch(msg[0]) {
    case WM_REDRAW:
      walk_rects(clear, msg[4], msg[5], msg[6], msg[7]);
      break;

    case WM_CLOSED:
      close_window();
      break;

    case WM_TOPPED:
      wind_set(window_id, WF_TOP, window_id);
      break;

    case WM_MOVED:
    case WM_SIZED:
      is_fulled=0;
      wind_calc(WC_WORK, WINTYPE, msg[4], msg[5], msg[6], msg[7],
                &win_xpos, &win_ypos, &win_xsize, &win_ysize);
      wind_set(window_id, WF_CURRXYWH, msg[4], msg[5], msg[6], msg[7]);
      break;

    case WM_FULLED:
      if (!is_fulled) {
        is_fulled=TRUE;
        small_xpos=win_xpos;
        small_ypos=win_ypos;
        small_xsize=win_xsize;
        small_ysize=win_ysize;

        wind_get(0, WF_WORKXYWH, &x, &y, &w, &h);
        wind_calc(WC_WORK, WINTYPE, x, y, w, h, &win_xpos, &win_ypos,
                  &win_xsize, &win_ysize);
      } else {
        is_fulled=FALSE;
        win_xpos=small_xpos;
        win_ypos=small_ypos;
        win_xsize=small_xsize;
        win_ysize=small_ysize;
      }
      wind_calc(WC_BORDER, WINTYPE, win_xpos, win_ypos, win_xsize,
                win_ysize, &x, &y, &w, &h);
      wind_set(window_id, WF_CURRXYWH, x, y, w, h);
      break;
  }
}

long poll_events(int waitkey)
{
  int msg[8];
  int dummy;
  int which;
  int key=0;
  unsigned long time=(waitkey ? 100L : 1L); /* 1/10th second when waiting for key,
                                               poll otherwise                      */

  do {
    which=evnt_multi(
        (waitkey ? (MU_MESAG|MU_KEYBD|MU_TIMER) : (MU_MESAG|MU_TIMER)),
                        0,0,0,
                        0,0,0,0,0,
                        0,0,0,0,0,
                        msg,
#ifdef __PUREC__
                        time,0,
#else
                        time,
#endif
                        &dummy,&dummy,&dummy,&dummy,&key,&dummy);
                 
    if(which&MU_MESAG) {
      process_message(msg);
    }
  } while(which&MU_MESAG);

  if(which&MU_KEYBD) {
    return ((Kbshift(-1)&7)<<24)|((long)(key&0xff00)<<8)|(key&0xff);
  } else {
    return 0;
  }
}

static void f_line(int x, int y, int w, int h)
{
  v_pline( vdi_handle, pxy_index, pxy );
}

static void flush_line( )
{
    int line_type;
    int color_index;
    int i;

    if( pxy_index>=2 ) {
        if( ATARI_numcolors==2 ) { /* Monochrome */
            color_index=1;
            line_type=ATARI_lt;
            if (line_type >= 0)
                line_type %= (ATARI_LINETYPES_MAX - 2);
        } else {                   /* Color */
            if (ATARI_lt<0) {
                color_index=1;
                line_type= ATARI_lt;
            }
            else {
                color_index=2+ATARI_lt%(ATARI_numcolors-2);
                line_type=(ATARI_lt/(ATARI_numcolors-2)) % (ATARI_LINETYPES_MAX - 2);
            }
        }

        vswr_mode( vdi_handle, MD_TRANS);
        vsl_color( vdi_handle, color_index );

        vsl_type( vdi_handle, 7 );
        vsl_udsty( vdi_handle, ATARI_linetypes[line_type + 2] );
        walk_rects(f_line, win_xpos, win_ypos, win_xsize, win_ysize);
    }

    if( pxy_index>=1 ) {
        pxy[0]=pxy[2*(pxy_index-1)];
        pxy[1]=pxy[2*(pxy_index-1)+1];
        pxy_index=1;
    }
}

static void put_text(int x, int y,int w, int h)
{
  v_gtext(vdi_handle, put_text_x, put_text_y, put_text_str);
}

TERM_PUBLIC void ATARI_options( )
{
#define ATARIHEXERROR "palette values 3 hex digits, please"
#define ATARIHEIGHTERROR "expecting a character height"
    char opt[6]; /* maximum: 'fff'\0 */
    int i;
    char *tok_end;

    term_options[0]='\0';
    ATARI_c_height=ATARI_c_height_default;

    for( i=0 ; i<17 ; i++ ) {
        if(END_OF_COMMAND) break;
        if (token[c_token].length>5) {
            ATARI_numpalette=0;
            ATARI_c_height=ATARI_c_height_default;
            term_options[0]='\0';
            int_error(ATARIHEXERROR, c_token);
        }

        capture( opt, c_token, c_token, 6 );
        if (!i) {
            ATARI_c_height = strtoul( opt, &tok_end, 10 );
            if( *tok_end!='\0' ) {
                ATARI_numpalette=0;
                ATARI_c_height=ATARI_c_height_default;
                term_options[0]='\0';
                int_error(ATARIHEIGHTERROR, c_token);
            }
            if( ATARI_c_height>999 )
                ATARI_c_height=999;  /* avoid opt length overflow */
            sprintf( opt, "%d ", ATARI_c_height );
        }
        else {
            if (*opt=='"' || *opt =='\'') {
                opt[strlen(opt)-1]='\0';
                strcpy(opt, opt+1);
            }
            ATARI_colors[i-1]=strtoul( opt, &tok_end, 16 );
            if( *tok_end!='\0' ) {
                ATARI_numpalette=0;
                ATARI_c_height=ATARI_c_height_default;
                term_options[0]='\0';
                int_error(ATARIHEXERROR, c_token);
            }

            sprintf( opt, "%X ", ATARI_colors[i-1] );

            /* do we need to quote? */
            if (isdigit(*opt) && strpbrk(opt, "ABCDEF")) {
                sprintf( opt, "\"%X\" ", ATARI_colors[i-1] );
            }
        }
        strcat( term_options, opt );
        c_token++;
    }
    ATARI_numpalette=(i==0 ? 0 : i-1);
    /* printf("Number of linetypes:%d\n", ATARI_LINETYPES_MAX); */
}

TERM_PUBLIC void ATARI_init( )
{
    int work_in[11];
    int work_out[57];
    int i;
    int hchar, wchar, dummy;
    int rgb[3];
    int num_save;
    char *colors,*tok_end;
    int x,y,w,h;
    static int have_win_size=0;

    application_init();

    if( ATARI_numpalette==0 && (colors=getenv("GNUCOLORS")) && *colors ) {
        for( i=0 ; i<17 ; i++ ) {
            if (!i) {
                ATARI_c_height = strtoul( colors, &tok_end, 10 );
                 if( colors==tok_end ) {
                    i=0;
                    ATARI_c_height=ATARI_c_height_default;
                    break;
                }
            }
            else {
                if( *colors=='\0' ) break;
                ATARI_colors[i]=strtoul( colors, &tok_end, 16 );
                if( colors==tok_end || (unsigned)ATARI_colors[i]>0xfff ) {
                    i=0;
                    break;
                }
            }
            colors=tok_end;

            while( *colors==' ' ) colors++;
        }
        ATARI_numpalette=(i==0 ? 0 : i-1);
    }
    vdi_handle=graf_handle( &dummy, &dummy, &dummy, &dummy );
    
    if( !vdi_handle )
        int_error("Fatal error opening virtual workstation", NO_CARET);

    for( i=0 ; i<10 ; work_in[i++]=1 );
        work_in[10]=2;                  /* use raster coordinates */
    v_opnvwk( work_in, &vdi_handle, work_out );
    
    if( !vdi_handle )
        int_error("Fatal error opening virtual workstation", NO_CARET);

    if(!have_win_size) {
        wind_get(0,WF_WORKXYWH, &x, &y, &w, &h);
        wind_calc(WC_WORK, WINTYPE, x, y, w, h, &win_xpos, &win_ypos,
                  &win_xsize, &win_ysize);
        have_win_size=1;
    }

    term->xmax=win_xsize;
    term->ymax=win_ysize;

    vst_height(vdi_handle, ATARI_c_height, &dummy, &dummy, &wchar, &hchar);

    term->h_char=wchar;
    term->v_char=hchar;    /* hchar stands for height this time */
    term->h_tic=win_xsize/100;
    term->v_tic=term->h_tic;

    ATARI_maxycoord=win_ysize-1;
    ATARI_numcolors=work_out[13];

    for( i=0 ; i<ATARI_numpalette ; i++ ) {
        vq_color( vdi_handle, i, 1, ATARI_savecolors[i] );

        rgb[0]=1000*(ATARI_colors[i]>>8);
        rgb[0]/=15;
        rgb[1]=1000*((ATARI_colors[i]>>4)&15);
        rgb[1]/=15;
        rgb[2]=1000*(ATARI_colors[i]&15);
        rgb[2]/=15;
        vs_color(vdi_handle, i, rgb );
    }
    pxy_index=0;
}

TERM_PUBLIC void ATARI_reset( )
{
    int i;

    close_window();
    if( vdi_handle!=-1 ) {
        for( i=0 ; i<ATARI_numpalette ; i++ ) {
            vs_color(vdi_handle, i, ATARI_savecolors[i] );
        }
        v_clsvwk( vdi_handle );
        vdi_handle=-1;
    }
}

TERM_PUBLIC void ATARI_graphics( )
{
    ATARI_maxycoord=win_ysize-1;

    term->xmax=win_xsize;
    term->ymax=win_ysize;

    mouse_mode();
    open_window();
    poll_events(0);
    walk_rects(clear, win_xpos, win_ypos, win_xsize, win_ysize);

    pxy_index=0;
}

TERM_PUBLIC void ATARI_text( )
{
    flush_line( );
    if(!multi_aes) {
        while(window_id!=-1 && !poll_events(1));
        close_window();
    }
    text_mode();
}

TERM_PUBLIC void ATARI_move( unsigned int x, unsigned int y )
{
    flush_line( );

    pxy[0]=x+win_xpos;
    pxy[1]=ATARI_yc(y)+win_ypos;
    pxy_index=1;
}

TERM_PUBLIC void ATARI_vector( unsigned int x, unsigned int y )
{
    pxy[2*pxy_index]=x+win_xpos;
    pxy[2*pxy_index+1]=ATARI_yc(y)+win_ypos;
    pxy_index++;

    if( pxy_index==64 ) {   /* we're all full */
        flush_line( );
    }
}

TERM_PUBLIC void ATARI_linetype( int lt )
{
    flush_line( );

    ATARI_lt=lt;
}

TERM_PUBLIC void ATARI_put_text( unsigned int x, unsigned int y, char *str )
{
    int vchar=term->v_char;
    int dummy;

    if( !strlen(str) ) return;

    if( x<0 ) x=0;
    if( y<0 ) y=0;

    /* align text left and to middle of char height */
    vst_alignment( vdi_handle, 0, 5, &dummy, &dummy );
    vst_rotation( vdi_handle, (ATARI_rotation ? 900 : 0) );
    if( ATARI_rotation ) {
        put_text_x=x-vchar/2+1+win_xpos;
        put_text_y=ATARI_yc(y)-1+win_ypos;
        put_text_str=str;
    } else {
        put_text_x=x+1+win_xpos;
        put_text_y=ATARI_yc(y)-vchar/2+1+win_ypos;
        put_text_str=str;
    }
    walk_rects(put_text, win_xpos, win_ypos, win_xsize, win_ysize);
}

TERM_PUBLIC int ATARI_text_angle( int ang )
{
    ATARI_rotation=ang;

    return TRUE;
}

TERM_PUBLIC int ATARI_justify_text( enum JUSTIFY mode )
{
    return FALSE;
}

TERM_PUBLIC void ATARI_point( unsigned int x, unsigned int y, int number )
{
    int old_linetype;

    if( ATARI_numcolors==2 ) {
        line_and_point( x, y, number ); /* monochrome */
    } else {
        /* we map colors that exceed our limit to dotted lines, but we can't do
           that with the markers (sortof a generalized line_and_point) */
        old_linetype=ATARI_lt;
        if( ATARI_lt>ATARI_numcolors-2 )
            ATARI_linetype(ATARI_lt%(ATARI_numcolors-2)); /* same color, but no dots */
        do_point( x, y, number );
        ATARI_linetype(old_linetype);
    }
}

#endif /* TERM_BODY */

#ifdef TERM_TABLE

TERM_TABLE_START(atari_driver)
	"atari", "Atari AES-Terminal",
	ATARI_XMAX, ATARI_YMAX, ATARI_VCHAR, ATARI_HCHAR, 
	ATARI_VTIC, ATARI_HTIC, ATARI_options, ATARI_init, ATARI_reset, 
	ATARI_text, null_scale, ATARI_graphics, ATARI_move, ATARI_vector, 
	ATARI_linetype, ATARI_put_text, ATARI_text_angle, 
	ATARI_justify_text, ATARI_point, do_arrow, set_font_null,
	0, TERM_CAN_MULTIPLOT, 0, 0
TERM_TABLE_END(atari_driver)


#undef LAST_TERM
#define LAST_TERM atari_driver

#endif /* TERM_TABLE */

#endif /* TERM_PROTO_ONLY */

#ifdef TERM_HELP
START_HELP(atari)
"1 atari ST (via AES)",
"?set terminal atari",
"?atari",
" The `atari` terminal has options to set the character size and the screen",
" colors.  The driver expects a space-separated list containing the character",
" size and up to 16 three-digit hex numbers, where each digit represents RED,",
" GREEN and BLUE (in that order).  The range of 0--15 is scaled to whatever",
" color range the screen actually has.  On a normal ST screen, odd and even",
" intensities are the same.",
"",
" Examples:",
"",
"        set terminal atari 4 # (use small (6x6) font)",
"        set terminal atari 6 0 # (set monochrome screen to white on black)",
"        set terminal atari 13 0 fff f00 f0 f ff f0f ff0",
"        # (set first eight colors to black, white, green, blue, cyan,",
"           purple, and yellow and use large font (8x16).)",
"",
" Additionally, if an environment variable GNUCOLORS exists, its contents are",
" interpreted as an options string, but an explicit terminal option takes",
" precedence."
END_HELP(atari) 
#endif /* TERM_HELP */
