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

Copyright (c) 2006, Colin Granville

All rights reserved.

Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following 
conditions are met:

   * Redistributions of source code must retain the above copyright 
     notice, this list of conditions and the following disclaimer.

   * The name Colin Granville may not be used to endorse or promote 
     products derived from this software without specific prior 
     written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
OF THE POSSIBILITY OF SUCH DAMAGE.

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

#include "kernel.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "swis.h"

#define SpriteV 0x1f

typedef struct
{
  int              next;   

  char             name[12];
  int              width_in_words;
  int              height;
  int              lbit;
  int              rbit;
  int              sprite_offset;
  int              mask_offset; 
  unsigned long    mode;
} Sprite;

typedef struct
{
  int x;
  int y;
} Point;

int read_mode_variable(unsigned long mode,int num, int* val)
{
  unsigned long flags;
  if (!_swix(OS_ReadModeVariable,_INR(0,1)|_OUT(2) | _OUT(_FLAGS),mode,num,val,&flags) && 
     (flags & _C)==0 ) return 1;
  return 0;
}

// return !0 if mask has a single transparent point;
static int get_active_point_from_mask(const Sprite* spr, Point* pt)
{
   int            mask_bpp;
   unsigned long* mask_data;
   int            y;
   int            transparent_point_found;
   unsigned long  rmask;
   unsigned long  pix_mask;
   int            pix_per_word;
   int            width_in_words;

// sprite must have mask and no left hand wastage
   if (!spr || !pt || spr->mask_offset == spr->sprite_offset || spr->lbit) return 0;

   if (spr->mode > 256)
   { /* New style sprite */
     int lastpix;
     int log2bpp;
     if ((spr->mode & 1) == 0 || !read_mode_variable(spr->mode, 9, &log2bpp)) return 0;
     lastpix = (spr->width_in_words * 32 + spr->rbit) >> log2bpp;
     if (spr->mode & (1u<<31))
     {
       int rbit;
       pix_per_word   = 4;
       pix_mask       = 0xff;
       mask_bpp       = 8;
       width_in_words = lastpix >> 2;
       rbit=((lastpix-(width_in_words<<2))>>3)+7;
       rmask= (rbit != 31)?-1ul<<(rbit+1):0;
    }
     else
     {
       int rbit;
       pix_per_word = 32;
       pix_mask     = 0x01;
       mask_bpp     = 1;
       width_in_words = lastpix >> 5;
       rbit=lastpix-(width_in_words<<5);
       rmask= (rbit != 31)?-1ul<<(rbit+1):0;
     }
   }
   else
   {
     int log2bpp;
     if (!read_mode_variable(spr->mode, 9, &log2bpp) || log2bpp > 3) return 0;
     mask_bpp = 1<<log2bpp;
     pix_per_word = 32>>log2bpp;
     pix_mask = -1ul>>(32-mask_bpp);
     width_in_words = spr->width_in_words;
     rmask= (spr->rbit != 31)?(-1ul<<(spr->rbit+1)):0;
   }

   mask_data = (unsigned long *) ( ((char *)spr) + spr->mask_offset );
   transparent_point_found = 0;
   for (y=0; y<=spr->height; y++)
   {
     int xword;
     int x=0;
     for (xword=width_in_words;xword>=0; xword--)
     {
       unsigned long word = *mask_data++;
       if (xword==0) word |= rmask;
       if (word != 0xFFFFFFFF) /* something is masked in this word */
       { 
         int bit;
         if (transparent_point_found) return 0;
         for (bit=0;bit<32;bit+=mask_bpp,word>>=mask_bpp,x++)
         {
           if ((word & pix_mask) != pix_mask)
           {
             if (transparent_point_found) return 0;
             pt->x=x;
             pt->y=y;
             transparent_point_found=1;
           }
         }
       }
       else
         x+=pix_per_word;
     }
   }

   return transparent_point_found;
}

extern int spritev(_kernel_swi_regs *r,void *pw);

int spritev_handler(_kernel_swi_regs *r,void *pw)
{
  if ((r->r[0] & 0xff) !=36) return 1;

  if (r->r[0]>>8==1)
  {
    int address;
    if (_swix(OS_SpriteOp,_INR(0,2) | _OUT(2),(24 | (r->r[0] &~ 0xFF)),r->r[1],r->r[2],&address) || address==r->r[2]) return 1;
    r->r[0]=0x200+36;
    r->r[2]=address;
  }

  if ((r->r[0]>>8)==2)
  {
    Point pt;
    Sprite* spr=(Sprite*) r->r[2];
    if (!spr) return 1;
    if (get_active_point_from_mask(spr,&pt))
    {
      r->r[4]=pt.x;
      r->r[5]=pt.y;
    }
    else if (!strcmp(spr->name,"ptr_write") &&
        !_swix(OS_SpriteOp,_INR(0,2) | _OUTR(3,4),0x200+40,r->r[1],r->r[2],&pt.x,&pt.y))
    {
      r->r[4]=(pt.x-1)>>1;
      r->r[5]=(pt.y-1)>>1;
    }
  }
  return 1;
}

_kernel_oserror* initialise(char *cmd_tail, int podule_base, void *pw)
{
  _swix(OS_Claim,_INR(0,2),SpriteV,spritev,pw);
  return 0;
}

_kernel_oserror* finalise(char *cmd_tail, int podule_base, void *pw)
{
  _swix(OS_Release,_INR(0,2),SpriteV,spritev,pw);
  return 0;
}
