/******************************************************************************
 *
 * COPYRIGHT: Unless otherwise noted, all files are Copyright (c) 1992, 1993
 * Commodore-Amiga, Inc.  All rights reserved.
 *
 * DISCLAIMER: This software is provided "as is".  No representations or
 * warranties are made with respect to the accuracy, reliability, performance,
 * currentness, or operation of this software, and all use is at your own risk.
 * Neither commodore nor the authors assume any responsibility or liability
 * whatsoever with respect to your use of this software.
 *
 ******************************************************************************
 * dispatch.c
 * Dispatch routine for a DataTypes class
 *
 * Modified for Be DataType by Edmund Vermeulen <edmundv@grafix.xs4all.nl>.
 *
 */

#include "classbase.h"

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

#include "bepalette.h"

#define B_COLOR_8_BIT 4
#define B_RGB_32_BIT 8

struct BeHeader
{
   LONG left;
   LONG top;
   LONG right;
   LONG bottom;
   LONG color_space;
   LONG size;
};

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

Class *initClass (struct ClassBase * cb)
{
   Class *cl;

   /* Create our class.  Note that this particular class doesn't have any instance data */
   if (cl = MakeClass (BEDTCLASS, PICTUREDTCLASS, NULL, NULL, 0L))
   {
      cl->cl_Dispatcher.h_Entry = (HOOKFUNC) Dispatch;
      cl->cl_UserData = (ULONG) cb;
      AddClass (cl);
   }

   return (cl);
}

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

ULONG ASM Dispatch (REG (a0) Class *cl, REG (a2) Object *o, REG (a1) Msg msg)
{
   struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
   ULONG retval;

   switch (msg->MethodID)
   {
      case OM_NEW:
         if (retval = DoSuperMethodA (cl, o, msg))
         {
            if (!GetPic (cb, cl, (Object *) retval, ((struct opSet *) msg)->ops_AttrList))
            {
               CoerceMethod (cl, (Object *) retval, OM_DISPOSE);
               return NULL;
            }
         }
         break;

         /* Let the superclass handle everything else */
      default:
         retval = (ULONG) DoSuperMethodA (cl, o, msg);
         break;
    }

    return (retval);
}

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

BOOL ASM GetPic (REG (a6) struct ClassBase * cb, REG (a0) Class * cl, REG (a2) Object * o, REG (a1) struct TagItem * attrs)
{
   struct BitMapHeader *bmhd;
   BOOL success = FALSE;
   struct RastPort trp;
   struct BitMap *tbm;
   struct BitMap *bm;
   struct RastPort rp;
   ULONG modeid;
   STRPTR title;
   BPTR fh;
   struct BeHeader hdr;
   UBYTE *linebuffer, swap;
   LONG y, linelen;
   struct ColorRegister *cmap;
   LONG *cregs;
   WORD n, ncolors;
   UBYTE red, green, blue;
   ULONG *MethodArray, *MethodArrayPtr;
   LONG ErrorLevel = 0, ErrorNumber = 0;
   BOOL V43 = FALSE;

   /* Get the supported Method Array Ptr from the picture.datatype */
   GetDTAttrs(o, DTA_Methods, &MethodArray, TAG_DONE);

   /* Now we search for the new DTM_BLITPIXELARRAY method to determine
      if it's the new V43 picture.datatype */

   MethodArrayPtr = MethodArray;
   while (*MethodArrayPtr != 0xffffffff)
   {
     if (*MethodArrayPtr == DTM_WRITEPIXELARRAY)
     {
        V43 = TRUE;
        break;
     }
     MethodArrayPtr++;
   }

   /* Get the default title */
   title = (STRPTR) GetTagData (DTA_Name, NULL, attrs);

   /* Get the file handle to read from and the BitMapHeader to write to */
   if ((GetDTAttrs (o, DTA_Handle, &fh, PDTA_BitMapHeader, &bmhd, TAG_DONE) == 2) && fh)
   {
      /* Make sure we are at the beginning of the file */
      Seek (fh, 0L, OFFSET_BEGINNING);

      /* Read the file header */
      Read (fh, &hdr, sizeof (struct BeHeader));

      /* No negative sizes allowed */
      if (hdr.right >= hdr.left && hdr.bottom >= hdr.top)
      {
         /* Fill in the size information */
         bmhd->bmh_Width  = bmhd->bmh_PageWidth  = hdr.right  - hdr.left + 1;
         bmhd->bmh_Height = bmhd->bmh_PageHeight = hdr.bottom - hdr.top  + 1;

         switch (hdr.color_space)
         {
            case B_COLOR_8_BIT:

               bmhd->bmh_Depth  = 8;

               /* Compute the mode ID */
               modeid = BestModeID (BIDTAG_NominalWidth,  640,
                                    BIDTAG_NominalHeight, 480,
                                    BIDTAG_DesiredWidth,  bmhd->bmh_Width,
                                    BIDTAG_DesiredHeight, bmhd->bmh_Height,
                                    BIDTAG_Depth,         bmhd->bmh_Depth,
                                    TAG_DONE);

               if (modeid == INVALID_ID)
                  modeid = HIRESLACE_KEY;

               /* Set the number of colors */
               ncolors = 1 << bmhd->bmh_Depth;
               SetDTAttrs (o, NULL, NULL, PDTA_NumColors, ncolors, TAG_DONE);

               /* Get the destination for the color information */
               GetDTAttrs (o,
                           PDTA_ColorRegisters, (ULONG) &cmap,
                           PDTA_CRegs,          &cregs,
                           TAG_DONE);

               /* Set the color information */
               for (n = 0; n < ncolors; n++)
               {
                  red   = bepalette[n][0];
                  green = bepalette[n][1];
                  blue  = bepalette[n][2];

                  /* Set the master color table */
                  cmap->red   = red;
                  cmap->green = green;
                  cmap->blue  = blue;
                  cmap++;

                  /* Set the color table used for remapping */
                  cregs[n * 3 + 0] = (red   << 24) | (red   << 16) | (red   << 8) | red;
                  cregs[n * 3 + 1] = (green << 24) | (green << 16) | (green << 8) | green;
                  cregs[n * 3 + 2] = (blue  << 24) | (blue  << 16) | (blue  << 8) | blue;
               }

               /* Line length is long word aligned */
               linelen = ((bmhd->bmh_Width + 3) >> 2) << 2;

               if (V43)  /* Use V43 mode if available */
               {
                  /* Let picture.datatype V43 allocate bitmap */
                  SetDTAttrs (o, NULL, NULL,
                              DTA_ObjName,      title,
                              DTA_NominalHoriz, bmhd->bmh_Width,
                              DTA_NominalVert,  bmhd->bmh_Height,
                              PDTA_SourceMode,  MODE_V43,
                              PDTA_ModeID,      modeid,
                              DTA_ErrorLevel,   &ErrorLevel,
                              DTA_ErrorNumber,  &ErrorNumber,
                              TAG_DONE);

                  /* Check if allocation was succesful */
                  if (ErrorLevel)
                     SetIoErr (ErrorNumber);
                  else
                  {
                     /* Allocate a temporary line buffer */
                     if (linebuffer = AllocVec (linelen, 0L))
                     {
                        /* Read image data line by line */
                        for (y = 0; y < bmhd->bmh_Height; y++)
                        {
                           Read (fh, linebuffer, linelen);

                           /* Copy image data to target bitmap */
                           DoSuperMethod (cl, o,
                                          DTM_WRITEPIXELARRAY,
                                          (ULONG) linebuffer,
                                          RECTFMT_LUT8,         /* Pixel format */
                                          bmhd->bmh_Width,      /* Modula to the next pixel line */
                                          0,                    /* X=0 */
                                          y,                    /* Y=Loop counter */
                                          bmhd->bmh_Width,      /* Line Width in Pixels */
                                          1);                   /* Lines you want to transfer each time */
                        }
                        success = TRUE;
                        FreeVec (linebuffer);
                     }
                     else
                        SetIoErr (ERROR_NO_FREE_STORE);
                  }
               }
               else  /* Old picture datatype */
               {
                  /* Allocate the bitmap */
                  if (bm = AllocBitMap (bmhd->bmh_Width, bmhd->bmh_Height, bmhd->bmh_Depth, BMF_CLEAR, NULL))
                  {
                     InitRastPort (&rp);
                     rp.BitMap = bm;

                     /* Initialize the temporary bitmap */
                     if (tbm = AllocBitMap (bmhd->bmh_Width, 1, bmhd->bmh_Depth, BMF_CLEAR, NULL))
                     {
                        InitRastPort (&trp);
                        trp.BitMap = tbm;

                        /* Allocate a temporary line buffer */
                        if (linebuffer = AllocVec (((bmhd->bmh_Width + 15) >> 4) << 4, 0L))
                        {
                           /* Read image data line by line */
                           for (y = 0; y < bmhd->bmh_Height; y++)
                           {
                              Read (fh, linebuffer, linelen);
                              WritePixelLine8 (&rp, 0, y, bmhd->bmh_Width, linebuffer, &trp);
                           }

                           /* Set the attributes */
                           SetDTAttrs (o, NULL, NULL,
                                       DTA_ObjName,      title,
                                       DTA_NominalHoriz, bmhd->bmh_Width,
                                       DTA_NominalVert,  bmhd->bmh_Height,
                                       PDTA_BitMap,      bm,
                                       PDTA_ModeID,      modeid,
                                       TAG_DONE);

                           success = TRUE;
                           FreeVec (linebuffer);
                        }
                        else
                           SetIoErr (ERROR_NO_FREE_STORE);
                        FreeBitMap (tbm);
                     }
                     else
                        SetIoErr (ERROR_NO_FREE_STORE);

                     if (!success)
                        FreeBitMap (bm);
                  }
                  else
                     SetIoErr (ERROR_NO_FREE_STORE);
               }

               break;

            case B_RGB_32_BIT:

               if (V43)
               {
                  bmhd->bmh_Depth  = 24;

                  /* Let picture.datatype V43 allocate bitmap */
                  SetDTAttrs (o, NULL, NULL,
                              DTA_ObjName,      title,
                              DTA_NominalHoriz, bmhd->bmh_Width,
                              DTA_NominalVert,  bmhd->bmh_Height,
                              PDTA_SourceMode,  MODE_V43,
                              PDTA_ModeID,      HIRESLACE_KEY,
                              DTA_ErrorLevel,   &ErrorLevel,
                              DTA_ErrorNumber,  &ErrorNumber,
                              TAG_DONE);

                  /* Check if allocation was succesful */
                  if (ErrorLevel)
                     SetIoErr (ErrorNumber);
                  else
                  {
                     /* Allocate a temporary line buffer */
                     linelen = bmhd->bmh_Width * 4;
                     if (linebuffer = AllocVec (linelen, 0L))
                     {
                        /* Read image data line by line */
                        for (y = 0; y < bmhd->bmh_Height; y++)
                        {
                           Read (fh, linebuffer, linelen);

                           /* Swap red and green component */
                           for (n = 0; n < linelen; n += 4)
                           {
                              swap = linebuffer[n];
                              linebuffer[n] = linebuffer[n+2];
                              linebuffer[n+2] = swap;
                           }

                           /* Copy image data to target bitmap */
                           DoSuperMethod (cl, o,
                                          DTM_WRITEPIXELARRAY,
                                          (ULONG) linebuffer,
                                          RECTFMT_RGBA,         /* Pixel format */
                                          linelen,              /* Modula to the next pixel line */
                                          0,                    /* X=0 */
                                          y,                    /* Y=Loop counter */
                                          bmhd->bmh_Width,      /* Line Width in Pixels */
                                          1);                   /* Lines you want to transfer each time */
                        }
                        success = TRUE;
                        FreeVec (linebuffer);
                     }
                     else
                        SetIoErr (ERROR_NO_FREE_STORE);
                  }
               }
               else
                  SetIoErr (ERROR_OBJECT_WRONG_TYPE);

               break;

            default:

               SetIoErr (ERROR_OBJECT_WRONG_TYPE);
               break;
         }
      }
      else
         SetIoErr (ERROR_OBJECT_WRONG_TYPE);
   }
   return (success);
}
