// Scanner gio-file per Photogenics
// usa lo scanner.device

// Un esempio di supporto di uno scanner a tripla passata, che salva // più
memoria dell'esempio "scan&show".

// Non usa tutte le caratteristiche di Photogenics 1.2 e ha quindi
// un problema: se non c'è abbastanza memoria per scansire l'immagine
// la scansione non viene annullata, perchè GioRead() non viene 
// eseguito in  questo caso.  
// (Questo problema è stato risolto nell'attuale versione di GIO.)

// © 1996 by Andreas Günther
// © 1997 Versione italiana a cura della Fractal Minds - Roma
// Tel. 06 - 4457035 - E-mail: m.kohler@agora.stm.it

#include "pgs_protos.h"
#include "pgs_pragmas.h"
#include "gio.h"
#include "devices/scanner.h"



/* prototipi */
__asm ULONG __saveds GioExamine(register __a0 struct GIOData *giodata);
__asm ULONG __saveds GioRead(register __a0 struct GIOData *giodata);
__asm ULONG __saveds GioWrite(register __a0 struct GIOData *giodata);
/* fine dei prototipi */

#define PgsBase giodata->PgsBase
#define IntuitionBase giodata->IntuitionBase


char *ver="$VER:Scanner.gio 1.1 (22.01.96) by Andreas Günther";

__asm ULONG __saveds GioInfo(void)
{
  return(GIOF_LOADER24);
}


static ULONG Memory(ULONG width, ULONG height, ULONG scanmode)
{
  return(width*height*3);   // Ogni pixel vuole 3 byte per ogni modo di scansione
}

__asm ULONG __saveds GioExamine(register __a0 struct GIOData *giodata)
{
  struct MsgPort *port,*progressport; // Port per IO e avanzamento dei report
  struct IOScanRequest *req;
  UBYTE passes;

  struct TagItem select_tags[]={
    {SCAN_Screen, NULL},
    {SCAN_NotWithOne, TRUE},
    {TAG_DONE, 0}};

  struct TagItem setparams_tags[]={
    {SCAN_Screen, NULL},
    {SCAN_IncludeModes, NULL},
    {SCAN_GetPasses, NULL},
    {SCAN_MemoryFunc, (ULONG)Memory},
    {TAG_DONE, 0}};

// supported scanmodes (for SCAN_IncludeModes):
  ULONG modes[]={4, SCM_TXT, SCM_HT, SCM_GREY8, SCM_COLOR8};
  
  setparams_tags[0].ti_Data=select_tags[0].ti_Data=(ULONG)giodata->PgsScreen;
  setparams_tags[1].ti_Data=(ULONG)modes;
  setparams_tags[2].ti_Data=(ULONG)&passes;
  giodata->Error = LOAD_OK;

  if(NULL==(port=CreateMsgPort()))
   {
    giodata->Error=LOAD_RAMERR;
    return(giodata->Error);
   }

  if(NULL==(progressport=CreateMsgPort()))
   {
    DeleteMsgPort(port);
    giodata->Error=LOAD_RAMERR;
    return(giodata->Error);
   }

  if(NULL==(req=CreateIORequest(port,sizeof(struct IOScanRequest))))
   {
    DeleteMsgPort(progressport);
    DeleteMsgPort(port);
    giodata->Error=LOAD_RAMERR;
    return(giodata->Error);
   }

  if(0!=OpenDevice("scanner.device",0,(struct IORequest *)req,0))
   {
    DeleteIORequest(req);
    DeleteMsgPort(progressport);
    DeleteMsgPort(port);
    giodata->Error=LOAD_UNAVAILABLE;
    return(giodata->Error);
   }

  giodata->UserData=(ULONG)port;
  giodata->UserData2=(ULONG)progressport;
  giodata->UserData3=(ULONG)req;

  req->Req.io_Command=SCCMD_SELECT;  
  req->Tags=select_tags;
  DoIO((struct IORequest *)req);

  if(req->Req.io_Error!=ScanErr_OK)
   {
    CloseDevice((struct IORequest *)req);
    DeleteIORequest(req);
    DeleteMsgPort(progressport);
    DeleteMsgPort(port);
    giodata->Error=LOAD_RAMERR;
    return(giodata->Error);
   }

  req->Req.io_Command=SCCMD_SETPARAMS;
  req->Tags=setparams_tags;
  DoIO((struct IORequest *)req);

  req->DataFormat=passes;  // just to keep it for GioRead()

  if(req->Req.io_Error!=ScanErr_OK)
   {
    switch(req->Req.io_Error)
     {
      case ScanErr_Cancel:
      case IOERR_ABORTED:   giodata->Error=LOAD_ABORTED;  break;
      case IOERR_OPENFAIL:
        Error("Scanner could not be accessed !");
        giodata->Error=LOAD_UNAVAILABLE;
        break; 
      case IOERR_UNITBUSY:
        Error("Lo Scanner è usato da un altro programma!");
        giodata->Error=LOAD_UNAVAILABLE;
        break; 
      case ScanErr_NoMemory:giodata->Error=LOAD_RAMERR;    break;
      default:              giodata->Error=LOAD_ABORTED;  break;
     }
    CloseDevice((struct IORequest *)req);
    DeleteIORequest(req);
    DeleteMsgPort(progressport);
    DeleteMsgPort(port);
    return(giodata->Error);
   }

  giodata->Depth=24;
  giodata->Height=req->AreaHeight;
  giodata->Width=req->AreaWidth;

  giodata->Error = LOAD_OK;

  return(giodata->Error);
}


__asm ULONG __saveds GioRead(register __a0 struct GIOData *giodata)
{
  struct MsgPort *port,*progressport; // Port for IO and progress reports
  struct IOScanRequest *req;
  struct ProgressMsg *msg;
  BOOL end=FALSE;     // for input loop
  int line=0;
  UBYTE passes, pass_cnt=1;
  UBYTE *linebuf=NULL;  
// buffer per una linea di scansione (necessario per le scansioni a tripla passata)

  struct TagItem read_tags[]={
    {SCAN_OpenWindow, FALSE},
    {SCAN_ProgressMsgPort, NULL},
    {TAG_DONE, 0}};

  struct TagItem abort_tags[]={
    {SCAN_Abort, TRUE},
    {TAG_DONE, 0}};

  port=(struct MsgPort *)giodata->UserData;
  progressport=(struct MsgPort *)giodata->UserData2;
  req=(struct IOScanRequest *)giodata->UserData3;

  passes=req->DataFormat;   // ripristina il valore delle passate
  if(passes!=0)
    if(NULL==(linebuf=AllocVec(req->AreaWidth, MEMF_PUBLIC)))
      passes=0;

  giodata->Error = LOAD_ABORTED;

  read_tags[1].ti_Data=(ULONG)progressport;
  req->Req.io_Command=CMD_READ;
  req->Tags=read_tags;
  if(passes==0)           // singola passata
   {
    req->DataFormat=FORMAT_RGB8;
    req->Req.io_Length=3*req->AreaWidth;
    req->Req.io_Data=GetLine(giodata,line);
   }
  else
   {
    req->DataFormat=FORMAT_TRIPASS8;
    req->Req.io_Length=req->AreaWidth;
    req->Req.io_Data=linebuf;
   }

  SendIO((struct IORequest *)req);
  SetProgress(NULL,0);      // apre la finestra del progresso

  do
   {
    Wait((1<<port->mp_SigBit) | (1<<progressport->mp_SigBit));

    while(NULL!=(msg=(struct ProgressMsg *)GetMsg(progressport)))
     {
      if(SetProgress(NULL,msg->Complete)!=1)
       {
        if(!CheckIO((struct IORequest *)req))
          AbortIO((struct IORequest *)req);
        else
          req->Tags=abort_tags;
       }
      ReplyMsg((struct Message *)msg);
     }

    while(NULL!=GetMsg(port))
     {
      if(passes==0)
        ReleaseLine(giodata,line);
      switch(req->Req.io_Error)
       {
        case ScanErr_OK:
        case ScanErr_ScanEnd:
          if(passes==0)   // singola passata
           {
            if(req->Req.io_Error!=ScanErr_ScanEnd)
             {
              line++;
              req->Req.io_Data=GetLine(giodata,line);  
	// scansione della successiva linea
              SendIO((struct IORequest *)req);
             }
            else
             {
              giodata->Error=LOAD_OK;
              end=TRUE;
             }
           }
          else            // tripla passata
           {
            UBYTE *from, *to;
            UWORD i;

            from=linebuf;
            to=GetLine(giodata,line)+SCANPASS(passes,pass_cnt)-1;
              // "to" contiene il puntatore al primo byte del colore corretto
            for(i=req->AreaWidth; i>0; i--)
             {
              *to=*(from++);
              to+=3;
             }
            ReleaseLine(giodata,line);

            if(req->Req.io_Error!=ScanErr_ScanEnd)
             {
              line++;
              if(line==req->AreaHeight)
               {
                line=0;
                pass_cnt++;
               }
              SendIO((struct IORequest *)req);
             }
            else
             {
              giodata->Error=LOAD_OK;
              end=TRUE;
             }
           }
          break;
        case IOERR_UNITBUSY:
        case IOERR_OPENFAIL:
          Error("Lo Scanner è usato da un altro programma!");
          giodata->Error=LOAD_UNAVAILABLE;
          break; 
        case ScanErr_WrongFormat:
          giodata->Error=LOAD_UNAVAILABLE;
          end=TRUE;
          break;
        case ScanErr_NoMemory:
          giodata->Error=LOAD_RAMERR;
          end=TRUE;
          break;
        case ScanErr_Cancel:
        case IOERR_ABORTED:
        default:
          giodata->Error=LOAD_ABORTED;
          end=TRUE;
          break;
       }
     }
   }
  while(!end);

  CloseProgress();

  CloseDevice((struct IORequest *)req);
  FreeVec(linebuf);
  DeleteIORequest(req); 
  DeleteMsgPort(progressport);
  DeleteMsgPort(port);

  return(giodata->Error);
}


__asm ULONG __saveds GioWrite(register __a0 struct GIOData *giodata)
{
	giodata->Error=LOAD_WRONGTYPE;
	return(giodata->Error);
}
