/******************************************************************************
 *
 * 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;

   /* 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));

      /* 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;
            }

            /* 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))
                  {
                     /* Line length is long word aligned */
                     linelen = ((bmhd->bmh_Width + 3) >> 2) << 2;

                     /* 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:

            /* 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)
                 break;
              MethodArrayPtr++;
            }

            if (*MethodArrayPtr == DTM_WRITEPIXELARRAY)  /* Do your V43 stuff */
            {
               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;
      }
   }
   return (success);
}
