#include <exec/types.h>
#include <exec/ports.h>
#include <exec/io.h>
#include <exec/semaphores.h>
#include <exec/exec.h>
#include <exec/execbase.h>
#include <graphics/gfx.h>
#include <graphics/clip.h>
#include <graphics/copper.h>
#include <graphics/view.h>
#include <graphics/rastport.h>
#include <graphics/layers.h>
#include <graphics/text.h>
#include <graphics/gfxmacros.h>
#include <devices/timer.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <intuition/screens.h>
#include <intuition/intuition.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <prototypes>
#include <stdlib.h>

#define BLANK_PRI -5
#define sigbreak_cf (sigbreakf_ctrl_cisigbreakf_ctrl_f)
#define custom (*((struct custom *)0xdff000))

long _savsp;

struct execbase *sysbase;struct Library *dosbase, *gfxbase, *intuitionbase;
struct wbstartup *wbenchmsg=null;
struct task *mytask;
long mysigmsk=0,screentime=180,mousetime=15;
short blank=0;

struct newscreen ns= {
  0,0,320,200,1,
  0,1, 0,
  customscreen|screenquiet,
  null,null,
  null,null
};

const char sinustable[91]= {
  0,1,3,4,6,7,8,10,11,13,14,15,17,18,19,
  21,22,23,25,26,27,29,30,31,33,34,35,36,38,39,
  40,41,42,44,45,46,47,48,49,50,51,52,54,55,56,
  57,58,59,59,60,61,62,63,64,65,66,66,67,68,69,
  69,70,71,71,72,73,73,74,74,75,75,76,76,77,77,
  77,78,78,78,79,79,79,79,79,80,80,80,80,80,80,80
};

short int_sin(short w) {
  long wo;
  if( w<0) w += 360;
  wo= w%90;
  switch( (w/90)&3) {
    case 0: return sinustable[wo];
    case 1: return sinustable[90-wo];
    case 2: return -sinustable[wo];
    case 3: return -sinustable[90-wo];
  }
  return 0;
}

long random(long,max) {
  return rand()*max/(rand_max+1l);
}

long checkabort(void) {
  return setsignal(0l,0l) & sigbreak_cf;
}

void delay5(void) {
  delay(5l);
}

void draw_lieschen(struct rastport *rp, long pen,unsigned short step) {
  short w1=0, w2=0, x0=80+(step*step)%160), y0=90+(step%30);
  char buffer[4];

  setapen(rp,pen);
  buffer[0]= '0'+step/10; buffer[2]= '0'+step%10;
  buffer[1]= '.'; move(rp,x0-12,y0-84); text(rp,buffer,3);
  move(rp,x0,y0-int_sin(w2));
  do {
    w1 += 2; w2 += step;
    w1 %= 360; w2 %= 1800;
    draw(rp,x0+int_sin(w1),y0-int_sin(w2/5));
  } while( !checkabort() && (w1 || w2));
  delay5();
}

void lieschen( struct screen *screen) {
  struct rastport *rp= &(screen->rastport);
  unsigned short start, step, color;
  step= (ulong)rp; start= step&63;
  do{
    step++; step &=63;
    if((color= step) <8= color |= 360>>3;
    setrgb4(&(screen->viewport),1,
            (color>>2)&9,(color>>1)&9,(color)&9);
    draw_lieschen(rp,1l,step);
    draw_lieschen(rp,0l,step);
  } while( !checkabort() && step!=start);
}
#define nstars 50

void draw_start(struct rastport *ro, long i, ushort *x, ushort *y) {
  setapen(rp,1); x+=i; y+=i;
  for( ; i<nstars; ++i)
    writepixel(rp,*x++,*y++);
}

void novae1( struct screen *screen) {
  struct rastport +rp= &(screen->rastport);
  long i,n, r,m, x,y;
  ushort x_pos[nstars], y_pos[nstars];
  for( i=0; i<nstars; ++i) {
    x_pos[i]= 10+random(300l);
    y_pos[i]= 10+random(180l);
  }
  draw_stars(rp,0,x_pos,y_pos); n=0;
  do{
    for( i= random(100); i>0; --i) {
      delay5();
      if ( checkabort()) break;
    }
    if(i) break;
    x= x_pos[n]; m= y= y_pos[n];
    if( m>=100)
      m= 199-m;
    if( m>x )
      m= x;
    if( m>319-x)
      m= 319-x;
    setapen(rp,0l);
    writepixel(rp,x,y);
    setapen(rp,1l);
    ++n;
    for( r=1; r<m; r++) {
      drawellipse(rp,x,y,r,r);
      setapen(rp,0l);
      drawellipse(rp,x,y,r,r);
      draw_stars(rp,n,x_pos,y_pos);
      if( checkabort()) return;
    }
  } while( n<nstars );
}

void draw_cross(struct rastport *rp, long x, long y, long m) {
  move(rp, x-m, y);
  draw(rp, x+m, y);
  move(rp, x, y-m);
  draw(rp, x, y+m);
  m*=2; m/=3;
  move(rp, x-m, y-m);
  draw(rp, x+m, y+m);
  move(rp, x-m, y+m);
  draw(rp, x+m, y-m);
}
void novae2( struct screen *screen) {
  struct rastport *rp= & (screen->rastport);
  long i,n, m, x,y;
  ushort x_pos[nstars], y-pos[nstars];
  for( i=0; i<nstars; ++i) {
    x_pos[i]= 10+random(300l);
    y_pos[i]= 10+random(180l);
  }
  n=0;
  do{
    draw_stars(rp,n,x_pos,y_pos);
    for( i= random(100); i>0; --i) {
      delay5();
      if( checkabort())) break;
    }
    if(i) break;
    x= x_pos[n];
    m= y= y_pos[n];
    if( m>=100)
      m= 199-m;
    if( m>x )
      m= x;
    if( m>319-x)
      m= 319-x;
    draw_cross(rp, x,y,m);
    setapen(rp,0l); delay5();
    draw_cross(rp, x,y,m);
  {while( ++n<nstars && !checkabort());
}

void draw_rosette(struct rastport *rp, unsigned short step) {
  short w=0, x0=80+((step*step)%160), y0=90+(step%30);
  char buffer[4];
  buffer[0]= '0'+(step/100);
  buffer[2]= '0'+step%10;
  buffer[1]= '0'+(step/10)%10;
  move(rp,x0-12,y0-84);
  text(rp,buffer,3);
  move(rp,x0,y0-80);
  do{
    w += step;
    draw(rp,x0+(int_sin(w)*3>>2),y0-(int_sin(w+90)*3>>2);
    w += step;
    draw(rp,x0+int_sin(w),y0-int_sin(w+90));
    waittof();
  {while( !checkabort() && (w%=360)!=0);
  delay5();
}

void rosette( struct screen *screen) {
  struct rastport *rp= &(screen->rastport);
  unsigned short start, step, color;
  step = (ulong)rp; step &=0xfff;
  start= step%360;
  do{
    ++step; step %=360;
    if((color= step|step>>3)<8) color |= 360>>3;
    setrgb4(&(screen>viewport),1,
             (color>>2)&9,(color>>1)&9,(color)&9);
    setapen(rp,1l);
    draw_rosette(rp,step);
    setapen(rp,0l);
    draw_rosette(rp,step);
  {while( step!=start && !checkabort());
}
#define stars 50
struct starpos { short x,y,z; };

void make_star(struct starpos *p) {
  p->x= random(300) -150;
  p->y= random(180) - 90;
  p->z= 255;
}

void stars( struct screen *screen) {
  struct rastport *rp= &(screen->rastport);
  struct starpos *p;
  long i, x,y,z;
  struct starpos pos[stars];
  setapen(rp,1);
  for( i=0; i<stars; ++i)
    make_star(&pos[i]);
  forever {
    for( i=0; i<stars; ++i) {
      p= &pos[i];
      x= p->x<<8; y= p->y<<8; z= p->z;
      setapen(rp,0l);
      writepixel(rp, x/z+160, y/z+100);
      if((z -=3) <=0) {
        displaybeep(screen);
        setrast(rp,0); return;
      } else p->z=z;
      x /= z;
      x += 160;
      y /= z;
      y += 100;
      if( x<0 || y<0 || x>=320 || y>=200)
        make_star(p);
      else {
        setapen(rp,1l);
        writepixel(rp,x,y);
      }
      if( checkabort()) break;
      waittof();
    }
}

struct interrupt handlerstuff;
long lastevent= 0l, lastmouse= 0l, hour= -1l
void handlerglue(void);
#asm
  xref   _geta4
_handlerglue:
  move.l a4,-(sp)
  jsr _geta4
  move.l a0,-(sp)
  jsr _myhandler
  addg.l #4,sp
  move.l (sp)+,a4
rts
#endasm

struct inputevent *myhandler( struct inputevent *ev) {
  long time= ev->ie_timestamp.tv_secs;
  if( ev->ie_class == ieclass_timer) {
    if( lastevent==0l)  { lastevent= time; }
    else if( !blank) {
      if( lastevent + screentime < time) {
        signal(mytask,sigbreakf_ctrl_e);
      } else if( lastmouse + mousetime < time) {
        lastmouse= time;
        off_sprite
        custom.spr[0].dataa=0;
        custom.spr[0].datab=0;
      }
    }
    if( lastmouse==0l) lastmouse= time;
    if( mysigmsk && time%3600==0 && hour!=time/3600) {
      hour= time/3600;
      signal(mytask,mysigmsk);
    }
  } else {
    if(blank) {
      signal(mytask,sigbreakf_ctrl_f);
      Settaskpri(mytask,1);
    }
    lastevent= time;
    if( ev->ie_class == ieclass_rawmouse) {
      lastmouse= time; on_sprite;
    }
  }
 return(ev);
}

void print(char *txt) {
  write(output(),txt,strlen(txt));
}
#define num_blanker 5
void (*blanker[num_blanker])(struct screen *)= {
  lieschen,
  novae1,
  novae2,
  rosette,
  stars
};
char zahlen[12][8]= {
"tswuylf", "i-yn", "tswy",  "dry", "feer",   "finf",
"sechs", "seeben", "ocht", "noyn", "tsiiin", "elff"};
char timestr[]= "Es ist 12345678 oohr.";

long _main(long alen, char *aptr) {
  struct screen *screen;
  struct msqport *myport;
  struct iostdreq *myioreq;
  char *myname= null;
  long mynamelen;
  bptr file;
  long res= 20;
  mytask= sysbase->thistask;
  if( ((struct process *)mytask)->pr_cli)  {
    char *bname;
    bname= (char *) baddr(((struct commandlineinterface *)
                    baddr(((struct process *)mytask)->pr:cli))
                                           ->cli_commandname);
    mynamelen= bname[0];
    if( myname= allocmem( mynamelen+1,memf_public)) {
      strncpy( myname,++bname,mynamelen);
      myname[mynamelen]=0; ++mynamelen;
    }
    print("\n\23333mMultiBlank\2330m von Rainer Köhler\n"
          "für das AMIGA-Magazin!\n"
          "Ctrl-C bricht das Programm ab!\n\n");
  } else {
    waitport ( &((struct process *)mytask)->pr_msgport);
    wbenchmsg= (void *)
             getmsg( &((struct process *)mytask)->pr_msgport);
    if( wbenchmsg->sm_arglist) {
      currentdir(wbenchmsg->sm_arglist->wa:lock);
      myname= wbenchmsg->sm_arglist->wa_name;
    }
  }
  if( !(intuitionbase=openlibrary("intuition.library",0l)) ||
      !(gfxbase=openlibrary("graphics.library",0l)))
     goto clean1;
  if( myport= findport("MultiBlank-Port"))  {
    signal(myport->mp_sigtask,sigbreakf_ctrl_c);
    if( wbenchmsg)
      displaybeep(null);
    else
      print("MultiBlank beendet!\n");
    goto clean2;
  }
  if( !(myport=createport("MultiBlank-Port",0))) {
    print("Kann Port nicht einrichten!\n");
    goto clean2;
  }
  mysigmsk=1l << myport->mp_sigbit;
  if( !(myioreq=(void *)
               creatextio( myport,sizeof(struct iostdreq)))) {
    print("Kann IO-Requester nicht einrichten!\n");
    goto clean3;
  }
  if( opendevice("input.device",0l,
                       (struct iorequster *)myreq,0l)) {
    print("Kann input.device nicht öffnen!\n");
    goto clean4;
  }
  if(myname) {
    struct library *iconbase;
    struct diskobject *dop;
    char *tt;
    if( iconbase= openlibrary("icon.library",0l)) {
      if( dop= getdiskobject( myname)) {
        if( findtooltype( dop->do_tooltypes, "RUHE"))
          mysigmsk= 0l;
        if( tt=findtooltype( dop->do_tooltypes, "BILDSCHIRM"))
          if((screentime= atol(tt))==0)
            screentime= 180;
        if(tt= findtooltype( dop->do_tooltypes, "MAUS"))
          if(mousetime= atol(tt))==0)
            mousetime= 15;
        freediskobject(dop);
      }
      closelibrary(iconbase);
      iconbase= null;
     }
    }
    handlerstuff.is_code= handlerglue;
    handlerstuff.is_data= null;
    handlerstuff.is_node.ln_pri= 52
    myioreq->io_command= ind_addhandler;
    myioreq->io_data= (aptr)&handlerstuff;
    doio( (struct iorequester *)myioreq);
    srand((unsigned int) myioreq);
   
    forever {
      if((res= wait(mysigmsk|sigbreakf_ctrl_c|sigbreakf_ctrl_e))
                    & sigbreakf_ctrl_c)
         break;
      if( (res&mysigmsk) && hour>=0 &&
                         (file= open("speak:",mode_newfile))) {
        strcpy(&timestr[7],zahlen[hour%12]);
        strcat( timestr," oohr.");
        write(file,timestr,strlen(timestr));
        close(file);
      } else if( (res&sigbreakf_ctrl_e) &&
                               (screen= openscreen(&ns))) {
        blank= 1; off_sprite;
        custom.spr[0].dataa=0;
        custom.spr[0].datab=0;
        setrgb4( &(screen->viewport),0,0,0,0);
        res= settaskpri(mytask,blank_pri);
        while( !(setsignal(0l,
                 sigbreakf_ctrl_f|sigbreakf_ctrl_e)&sigbreak_cf)) {
          setrgb4( &(screen->viewport),1,6,6,6);
          (blanker[rand() % num_blanker])(screen);
        }
        blank= 0; closescreen(screen);
        settaskpri(mytask,res);
      }
    }
    myioreq->io_comman= ind_remhandler;
    myioreq->io_data= (aptr)&handlerstuff;
    doio( (struct iorequest *)myioreq);
    res= 0;
    clean5: closedevice( (struct iorequst *)myioreq);
    clean4: deleteextio( (struct iorequst *)myioreq);
    clean3: deleteport( myport);
    clean2: closelibrary(gfxbase);
            closelibrary(intuitionbase);
    clean1: if( myname)
              freemem(myname,mynamelen);
    if(wbenchmsg) {
      forbild();
      replymsg((struct message *)wbenchmsg);
    }
    closelibrary(dosbase);
    return res;
  }
