/*
** $VER: personal_cel_io.c 5.0
** Written by Michal Durys, based on example code suppiled by Cloanto.
*/

#include <string.h>

#include <proto/exec.h>
#include <exec/types.h>
#include <exec/memory.h>

#include <libraries/personal_io.h>

/* Kiss stuff */
#include "kiss_files.h"

/* Useful macro */
#define pcword(var) (((var & 0x00ff)<<8) | ((var & 0xff00)>>8))


/*
** Pragma aliases
*/
#define SysBase picinfo->SysBase
#define DOSBase picinfo->DOSBase
#define GfxBase picinfo->GfxBase
#define IntuitionBase picinfo->IntuitionBase
#define IconBase picinfo->IconBase

static UBYTE VersionStr[] = "$VER:Personal CEL IO Library 5.0 (4.1.99)";

/*
**  The CEL library just supports one picture type
*/
#define DATA_CEL  0
#define DATA_NUM  1

static struct DataInfoEntry DInfo[DATA_NUM] =
{
   {
    DATA_CEL,        /* CEL picture type code */
    DIF_LOAD         /* attr: planar images images can be loaded */
//    | DIF_LOAD_24    /* attr: 24bit chunky images can be loaded */
    | DIF_SAVE       /* attr: planar images can be saved (converted to 24bit PBM data, if required) */
    | DIF_LOADFILE   /* attr: the data is read from file */
    | DIF_SAVEFILE   /* attr: the data is written to file */
    | DIF_RECOGNIZE, /* attr: CEL files can be recognized */
    PCDT_FORMAT      /* data: image format */
    | PCDT_HANDLE    /* data: image handle */
    | PCDT_IMAGE,    /* data: image bitmap */
    455,             /* sort priority */
    "CEL",           /* full name */
    "CEL",           /* short name */
    {".cel",NULL,NULL }  /* file name suffixes */
   }
};

static LONG DataCount;  /* counter used by query functions */

#define CEL_OPTIONS_NUM 0

static struct DataOptionEntry PbmOptions[CEL_OPTIONS_NUM];

/* data used by the option query functions */

static struct DataOptionEntry *Options[DATA_NUM] = { PbmOptions };
static LONG DataOptionMax[DATA_NUM] = { CEL_OPTIONS_NUM-1 };
static LONG DataOptionCount[DATA_NUM];

/*
** Library interface protos
*/
__asm BOOL __saveds GetFirstDataInfo(register __a0 struct DataInfo *);
__asm BOOL __saveds GetNextDataInfo(register __a0 struct DataInfo *);
__asm WORD __saveds GetPictureInfo(register __a0 struct PictureInfo *);
__asm LONG __saveds GetProgressBumps(register __a0 struct PictureInfo *);
__asm WORD __saveds ReadPicture(register __a0 struct PictureInfo *);
__asm WORD __saveds WritePicture(register __a0 struct PictureInfo *);
__asm void __saveds IOLibCleanup(register __a0 struct PictureInfo *);
__asm BOOL __saveds GetFirstOption(register __a0 struct DataInfo *, register __a1  struct DataOption *);
__asm BOOL __saveds GetNextOption(register __a0 struct DataInfo *, register __a1  struct DataOption *);
__asm void __saveds SetOption(register __a0 struct DataInfo *, register __a1  struct DataOption *);
__asm void __saveds ResetOptions(register __a0 struct DataInfo *);
__asm WORD __saveds PersonalIOPrivate1(void);
__asm WORD __saveds PersonalIOPrivate2(void);
__asm WORD __saveds PersonalIOPrivate3(void);

/*
** Library's standard static protos
*/
static void GetDataInfo(struct DataInfo *, LONG);
static void GetDataOption(struct DataOption *, struct DataOptionEntry *);
static BOOL Handshake(struct DataInfo *);

/*
 *  Standard query dispatcher:
 *  the n'th DataInfoEntry is copied to 'dinfo'.
 *
 *  This function does not usually need to be changed,
 *  unless the library supports run-time defined picture types.
 */
static void GetDataInfo(struct DataInfo *dinfo, LONG n)
{
    struct DataInfoEntry *de = &DInfo[n];
    LONG s;

    dinfo->Code = de->Code;
    dinfo->Flags = de->Flags;
    dinfo->ProcessData = de->ProcessData;
    dinfo->Priority = de->Priority;
    strcpy(dinfo->Name, de->Name);
    strcpy(dinfo->ShortName, de->ShortName);

    for (s = 3; --s >= 0; )
    {
        if (de->FileSuffix[s])
            strcpy(&dinfo->FileSuffix[s][0], de->FileSuffix[s]);
        else
            dinfo->FileSuffix[s][0] = 0;
    }
    memset(dinfo->pad, 0, sizeof(dinfo->pad));
}

/*
** Standard handshake function:
** FALSE is returned if handshaking fails,
** TRUE if it succeeds.
*/
static BOOL Handshake(struct DataInfo *dinfo)
{
    /*
     * Cloanto is a registered trademark of Cloanto Italia srl;
     * use of the following handshaking protocol requires a license,
     * which is normally available at no charge for non-commercial use.
         */
    static UBYTE hs_text[] = "CloantoŽ";

    if (dinfo->Version >= 5)  /* V5 of client interface feature */
    {
        if (dinfo->HandshakeText == NULL)
            return(FALSE);
        if (strcmp(dinfo->HandshakeText, hs_text) != 0)
            return(FALSE);
        /*
         * the library internal string is passed
         */
        dinfo->HandshakeText = hs_text;
    }
    return(TRUE);
}

/*
 *  Standard query function
 *  (support for multiple picture types is included)
 */
__asm BOOL __saveds GetFirstDataInfo(register __a0 struct DataInfo *dinfo)
{
    if (Handshake(dinfo))
    {
        DataCount = 0;
        if (DataCount < DATA_NUM)
        {
            GetDataInfo(dinfo, DataCount++);
            return(TRUE);
        }
    }
    return(FALSE);
}

/*
 *  Standard query function
 *  (support for multiple picture types is included)
 */
__asm BOOL __saveds GetNextDataInfo(register __a0 struct DataInfo *dinfo)
{
    if (Handshake(dinfo))
    {
        if (DataCount < DATA_NUM)
        {
            GetDataInfo(dinfo, DataCount++);
            return(TRUE);
        }
    }
    return(FALSE);
}


/*
** File recognition function:
** when the file has been successfully identified,
** the image format must be returned.
*/
__asm WORD __saveds GetPictureInfo(register __a0 struct PictureInfo *picinfo)
{
  struct Kiss_CelFileHeader *celheader;

  if (picinfo->Version<3) return(PIOERR_BADVER);

  celheader=(struct Kiss_CelFileHeader *)picinfo->FileHead;
  if (!(celheader->id==KISS_ID && celheader->mark==FILEMARK_CEL)) return(PIOERR_BADTYPE);

  /* For now we can't handle Cherry Kiss cels */
  if (celheader->bitsperpixel>8) return(PIOERR_UIMGTYP);

  GetDataInfo(&picinfo->Data,DATA_CEL);

  /* Give full information about this picture type */
  picinfo->Width=pcword(celheader->width);
  picinfo->Height=pcword(celheader->height);
  picinfo->Depth=celheader->bitsperpixel;
  picinfo->HandleX=celheader->xoffset;
  picinfo->HandleY=celheader->yoffset;

  /* Store offset to the beginning of image data */
  picinfo->User1=(APTR)(sizeof(struct Kiss_CelFileHeader));

  /* True color images can be previewed */
  picinfo->PreviewDepth=(picinfo->Depth==24) ? 24 : 0;

  /* Image format information has been stored */
  picinfo->ProcessedData=PCDT_FORMAT;
  return(PIOERR_OK);
}

/*
 *  Progress requester information function:
 *  the load and save operations will issue <picture height> bumps.
 */
__asm LONG __saveds GetProgressBumps(register __a0 struct PictureInfo *picinfo)
{
    return((LONG)picinfo->Height);
}


/*
** Image loading function
*/
__asm WORD __saveds ReadPicture(register __a0 struct PictureInfo *picinfo)
{
//  struct Color *col;
  struct BitMap *bmap;
//  UBYTE *r_plane, *g_plane, *b_plane, *rpl, *gpl, *bpl;
//  LONG  bmap_mod;
//  int r, g, b, maxval;
//  float scale;
//  WORD format;
  UWORD w, h, x, y, c;
//  UBYTE byte_buff;
  UBYTE *linebuf;

  /* Position the file at the beginning of image data */
  if (picinfo->SeekFile(SKF_ABSOLUTE,(LONG)picinfo->User1)<0) return(PIOERR_FILE_ERR);

  if ((picinfo->ProcessData & PCDT_IMAGE) && picinfo->BMap)
  {
    /* PCDT_IMAGE bit is set here to flag the presence
    ** of even partially loaded image data
    ** (see PIOERR_PARTIAL return code below)
    */
    picinfo->ProcessedData|=PCDT_IMAGE;

    w=picinfo->Width;
    h=picinfo->Height;
    bmap=picinfo->BMap;

    if (picinfo->Depth==24)   /* read PBM color data */
    {
/*
      r_plane=bmap->Planes[0];
      g_plane=bmap->Planes[1];
      b_plane=bmap->Planes[2];
      bmap_mod = bmap->BytesPerRow;

      for (y=0; y<h; y++)
      {
        rpl = r_plane;
        gpl = g_plane;
        bpl = b_plane;

        for (x = 0; x < w; x++, rpl++, gpl++, bpl++)
        {
          *rpl = r;
          *gpl = g;
          *bpl = b;
        }

        if (picinfo->BumpProgress)
        {
          if (picinfo->BumpProgress()) return(PIOERR_CANCEL);
        }
        if (picinfo->PreviewDepth) picinfo->PreviewRow(bmap, y);

        r_plane += bmap_mod;
        g_plane += bmap_mod;
        b_plane += bmap_mod;
      }
*/
    }

    /* Read 8 bits per pixel cel image */
    else if (picinfo->Depth==8)
    {
      /* Allocate memory for line buffer */
      if (!(linebuf=AllocMem(w,MEMF_ANY))) return(PIOERR_NOMEMORY);
      /* Planar bitmap writing initialization */
      picinfo->FastWritePix(bmap,FBMP_INIT,0,0);

      for (y=0; y<h; y++)
      {
        /* Read one line of chunky bytes to buffer */
        if (picinfo->ReadFile(linebuf,w)==FALSE) return(PIOERR_FILE_ERR);
        /* Write pixels from line buffer to bitmap */
        for (x=0; x<w; x++)
        {
          picinfo->FastWritePix(bmap,x,y,linebuf[x]);
        }
        /* Flush pixel row buffer */
        picinfo->FastWritePix(bmap,FBMP_FLUSH,0,0);
        /* One more progress requester step */
        if (picinfo->BumpProgress)  
        {
          if (picinfo->BumpProgress()) return(PIOERR_CANCEL);  /* user cancelled */
        }
      }
      /* Free memory allocated for line buffer */
      FreeMem(linebuf,w);
    }

    /* Read 4 bits per pixel cel image */
    else if (picinfo->Depth==4)
    {
      /* Allocate memory for line buffer */
      if (!(linebuf=AllocMem((w+1)/2,MEMF_ANY | MEMF_CLEAR))) return(PIOERR_NOMEMORY);
      /* Planar bitmap writing initialization */
      picinfo->FastWritePix(bmap,FBMP_INIT,0,0);

      for (y=0; y<h; y++)
      {
        /* Read one line of chunky bytes to buffer */
        if (picinfo->ReadFile(linebuf,(w+1)/2)==FALSE) return(PIOERR_FILE_ERR);
        /* Write pixels from line buffer to bitmap */
        for (x=0, c=0; x<w; x=x+2, c++)
        {
          /* First pixel: upper 4 bits */
          picinfo->FastWritePix(bmap,x,y,((linebuf[c]>>4) & 0x0f));
          /* Second pixel: lower 4 bits */
          picinfo->FastWritePix(bmap,x+1,y,(linebuf[c] & 0x0f));
        }
        /* Flush pixel row buffer */
        picinfo->FastWritePix(bmap,FBMP_FLUSH,0,0);
        /* One more progress requester step */
        if (picinfo->BumpProgress)
        {
          if (picinfo->BumpProgress()) return(PIOERR_CANCEL);  /* user cancelled */
        }
      }
      /* Free memory allocated for line buffer */
      FreeMem(linebuf,(w+1)/2);
    }
  }
  return(PIOERR_OK);
}

/*
** Image saving function
*/
__asm WORD __saveds WritePicture(register __a0 struct PictureInfo *picinfo)
{
  WORD w, h, x, y;
  struct BitMap *bmap;
  struct Kiss_CelFileHeader *celheader;
  UBYTE hi, lo, pen;

  if (picinfo->Version<3) return(PIOERR_BADVER);

  if ((picinfo->ProcessData & PCDT_IMAGE)==0 ||
    picinfo->BMap==NULL ||
    picinfo->Palette==NULL)
    return(PIOERR_OK);


  /* Write file header */
  celheader=(struct Kiss_CelFileHeader *)AllocMem(sizeof(struct Kiss_CelFileHeader),MEMF_PUBLIC | MEMF_CLEAR);
  celheader->id=KISS_ID;
  celheader->mark=FILEMARK_CEL;
  if (picinfo->Depth>5)
    celheader->bitsperpixel=8;
  else
    celheader->bitsperpixel=4;
  celheader->width=pcword(picinfo->Width);
  celheader->height=pcword(picinfo->Height);
  celheader->xoffset=pcword(picinfo->HandleX);
  celheader->yoffset=pcword(picinfo->HandleY);
  if (!picinfo->WriteFile((UBYTE *)celheader,sizeof(struct Kiss_CelFileHeader))) return(PIOERR_FILE_ERR);
  FreeMem(celheader,sizeof(struct Kiss_CelFileHeader));

  w=picinfo->Width;
  h=picinfo->Height;
  bmap=picinfo->BMap;

  /* Planar bitmap reading initialization */
  picinfo->FastReadPix(bmap,FBMP_INIT,0);

  /* Write 8 bits per pixel cel */
  if (picinfo->Depth>5)
  {
    for (y=0; y<h; y++)
    {
      for (x=0; x<w; x++)
      {
        pen=picinfo->FastReadPix(bmap,x,y);
        if (!picinfo->WriteFile(&pen,1)) return(PIOERR_FILE_ERR);
      }
      if (picinfo->BumpProgress)  /* one more progress requester step */
      {
        if (picinfo->BumpProgress()) return(PIOERR_CANCEL);  /* user cancelled */
      }
    }
  }
  /* Write 4 bits per pixel cel */
  else
  {
    for (y=0; y<h; y++)
    {
      for (x=0; x<w; x=x+2)
      {
        hi=picinfo->FastReadPix(bmap,x,y);
        lo=picinfo->FastReadPix(bmap,x+1,y);
        pen=(((hi & 0x0f)<<4) | (lo & 0x0f));
        if (!picinfo->WriteFile(&pen,1)) return(PIOERR_FILE_ERR);
      }
      if (picinfo->BumpProgress)  /* one more progress requester step */
      {
        if (picinfo->BumpProgress()) return(PIOERR_CANCEL);  /* user cancelled */
      }
    }
  }

  /* Image data successfully saved */
  picinfo->ProcessedData |= PCDT_IMAGE;
  return(PIOERR_OK);
}

__asm void __saveds IOLibCleanup(register __a0 struct PictureInfo *picinfo)
{
    /* no cleanup needed */
}

/*
 *  Standard option dispatcher.
 *
 *  This function does not usually need to be changed,
 *  unless the library supports run-time defined options.
 */
static void GetDataOption(struct DataOption *option,
              struct DataOptionEntry *from)
{
    option->Type = from->Type;
    option->UITextStr = from->UITextStr;
    option->Flags = from->Flags;
    option->Value = from->Value;
    option->Min = from->Min;
    option->Max = from->Max;
    option->Default = from->Default;

    memcpy(option->String, from->String, sizeof(option->String));
    memset(option->Name, 0, sizeof(option->Name));
    if (from->Name)
        strcpy(option->Name, from->Name);

    memset(option->pad, 0, sizeof(option->pad));
}

/*
 *  Standard option query function
 *  (support for multiple picture types is included)
 */
__asm BOOL __saveds GetFirstOption(register __a0 struct DataInfo *dinfo,
                   register __a1 struct DataOption *option)
{
    LONG c = dinfo->Code;

    if (c < DATA_NUM) {
        DataOptionCount[c] = 0;
        if (DataOptionCount[c] <= DataOptionMax[c])
        {
            GetDataOption(option, Options[c] + DataOptionCount[c]);
            return(TRUE);
        }
    }
    return(FALSE);
}

/*
 *  Standard option query function
 *  (support for multiple picture types is included)
 */
__asm BOOL __saveds GetNextOption(register __a0 struct DataInfo *dinfo,
                  register __a1 struct DataOption *option)
{
    LONG c = dinfo->Code;

    if (c < DATA_NUM)
    {
        if (DataOptionCount[c] < DataOptionMax[c])
        {
            DataOptionCount[c] += 1;
            GetDataOption(option, Options[c] + DataOptionCount[c]);
            return(TRUE);
        }
    }
    return(FALSE);
}

/*
 *  Standard option set function
 *  (support for multiple picture types is included)
 */
__asm void __saveds SetOption(register __a0 struct DataInfo *dinfo,
                  register __a1 struct DataOption *option)
{
    struct DataOptionEntry *opt;
    LONG n, max, c;

    c = dinfo->Code;
    if (c < DATA_NUM && option)
    {
        max = DataOptionMax[c];
        /*
         *  find an option with the given name
         */
        for (n = 0, opt = Options[c]; n <= max; n++, opt++)
        {
            if (strcmp(option->Name, opt->Name) == 0) /* found */
            {
                opt->Value = option->Value;
                memcpy(opt->String, option->String, sizeof(opt->String));
                break;
            }
        }
    }
}

/*
 *  Standard option reset function
 *  (support for multiple picture types is included)
 */
__asm void __saveds ResetOptions(register __a0 struct DataInfo *dinfo)
{
    struct DataOptionEntry *opt;
    LONG n, max, c;

    c = dinfo->Code;
    if (c < DATA_NUM)
    {
        max = DataOptionMax[c];
        for (n = 0, opt = Options[c]; n <= max; n++, opt++)
            opt->Value = opt->Default;
    }
}

/*
 *  Private PersonalIO functions
 */
__asm WORD __saveds PersonalIOPrivate1()
{
    return(PIOERR_CANCEL);
}

__asm WORD __saveds PersonalIOPrivate2()
{
    return(PIOERR_CANCEL);
}

__asm WORD __saveds PersonalIOPrivate3()
{
    return(PIOERR_CANCEL);
}
