/************************************************************
**
** Application: Snapper
**
** Title:       c.convert
**
*************************************************************/

/*
*
* Copyright (c) 2017, David Pilling and Chris Johnson
* 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.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may 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
* HOLDER 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 files */


#include "h.includes"

#include <ctype.h>

/* from TaskLib */

#include "TaskLib:wimpt.h"
#include "TaskLib:flex.h"
#include "TaskLib:transform.h"
#include "TaskLib:fsx.h"
#include "TaskLib:etc.h"
#include "TaskLib:colour.h"
#include "TaskLib:h.hourglass"
#include "TaskLib:bf.h"
#include "TaskLib:sprite.h"
#include "TaskLib:alloc.h"


/* from application */

#include "xdeb.h"
#include "reslink.h"
#include "constants.h"
#include "str.h"
#include "im.h"
#include "jpegc.h"
#include "pngim.h"
#include "mask.h"
#include "ram.h"
#include "config.h"
#include "convert.h"


static filestr xfile;
static filestr * loadfile = &xfile;

static buffer  bf;
static buffer *bfp = &bf;

static int      newformat;    // force to 1bpp mask for all bpp




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

static void setup_buffer ( char *data, int buf_size, int data_size)
{
  /* init buffer */
  memset(bfp, 0, sizeof(buffer));
  /* set as ram buffer */
  bf.flags = BUFFRAM;
  /* data details */
  bf.data = data;
  bf.size = buf_size;
  bf.length = data_size;
  return;
}





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

#define FFCHUNK 8

/* add a new frame to this image                   */
/* size is sizeof corresponding private data array */

os_error * addframe(filestr * file, int * fn, int size)
{
 os_error * err;

 if (file->frames) err = schunk((flex_ptr)&file->frames,
                (file->framen+1)*sizeof(framestr),FFCHUNK*sizeof(framestr));
 else             err=salloc((flex_ptr)&file->frames,FFCHUNK*sizeof(framestr));


 if (!err)
 {
  if (size)
  {
   if (file->data) err = schunk((flex_ptr)&file->data,
                                  (file->framen+1)*size,FFCHUNK*size);
   else           err = salloc((flex_ptr)&file->data,FFCHUNK*size);
  }

  if(!err)
  {
   *fn=file->framen;
   file->framen++;
  }
 }
 return(err);
}



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

os_error * trashall (filestr * file)
{
  int i;

  for (i = 0; i < file->framen; i++)
  {
    ximtrash(&(file->frames[i].xim));
  }
  sfree ((flex_ptr)&file->frames);
  file->framen = 0;
  return(NULL);
}





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

static os_error * getpal(buffer * bf,imagestr * im,newsprite_header * sph)
{
 os_error * err;
 int        palentries;
 int        paltab[257];
 int        i;
 int        c1;

 err=NULL;


 palentries=(sph->image-sizeof(sprite_header))/8;
 if(palentries<0) palentries = 0;

 if(palentries==0)
 {
  if(im->bpp>8)
  {
   im->ipal.ncolours=0;
   memset(&im->ipal,0,sizeof(ipalstr));
   im->ipal.palclass=(sph->selector.type.type==7)?CL_CMYK:CL_RGB;
   return(err);
  }

  /* if there is no palette, we'd like it to look like wimp cols */

  if(im->bpp==8)
  {
   palentries=16;
   paltab[0]=0x0;
   paltab[1]=0x10101000;
   paltab[2]=0x20202000;
   paltab[3]=0x30303000;
   paltab[4]=0x4000;
   paltab[5]=0x10105000;
   paltab[6]=0x20206000;
   paltab[7]=0x30307000;
   paltab[8]=0x40000000;
   paltab[9]=0x50101000;
   paltab[10]=0x60202000;
   paltab[11]=0x70303000;
   paltab[12]=0x40004000;
   paltab[13]=0x50105000;
   paltab[14]=0x60206000;
   paltab[15]=0x70307000;
   palentries=16;
  }
  else
  if(im->bpp==4)
  {
   paltab[0]=0xffffff00;
   paltab[1]=0xdddddd00;
   paltab[2]=0xbbbbbb00;
   paltab[3]=0x99999900;
   paltab[4]=0x77777700;
   paltab[5]=0x55555500;
   paltab[6]=0x33333300;
   paltab[7]=0x00000000;
   paltab[8]=0x99440000;
   paltab[9]=0x00eeee00;
   paltab[10]=0x00cc0000;
   paltab[11]=0x0000dd00;
   paltab[12]=0xbbeeee00;
   paltab[13]=0x00885500;
   paltab[14]=0x00bbff00;
   paltab[15]=0xffbb0000;
   palentries=16;
  }
  else
  if(im->bpp==2)
  {
   paltab[0]=0xffffff00;
   paltab[1]=0xdddddd00;
   paltab[2]=0x99999900;
   paltab[3]=0x00000000;
   palentries=4;
  }
  else
  if(im->bpp==1)
  {
   paltab[0]=0xffffff00;
   paltab[1]=0x00000000;
   palentries=2;
  }
 }
 else
 {
  for(i=0;i<palentries;i++)
  {
   if(i<256) bf_read(bf,paltab+i,2*sizeof(int));
   else      bf_read(bf,paltab+256,2*sizeof(int));
  }
 }

 if(im->bpp==8 && (palentries==64 || palentries==16))
 {

  if(palentries==64)
  {
   for(i=0;i<16;i++) paltab[i]=paltab[i+48];
  }

  for(i=255;i>=0;i--)
  {
   c1=paltab[(i & 0xF)];

   if(i & 0x10) c1|=0x8000;
   else         c1&=~0x8000;

   if(i & 0x20) c1|=0x400000;
   else         c1&=~0x400000;

   if(i & 0x40) c1|=0x800000;
   else         c1&=~0x800000;

   if(i & 0x80) c1|=0x80000000;
   else         c1&=~0x80000000;

   c1&=0xF0F0F000;
   c1|=(c1>>4)&0xF0F0F00;

   paltab[i]=c1;
  }
  palentries=256;
 }

 im->ipal.ncolours=palentries;
 for(i=0;i<palentries;i++) im->ipal.word[i]=paltab[i];

 return(err);
}






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

static os_error * spreadhdr(int fn,buffer * bf,newsprite_header * sph)
{
 os_error * err;
 char     * p;
 char     * q;
 int        spdx;
 int        spdy;
 int        spbpp;
 int        spbitmask;
 int        bitwidth;
 int        xdpi;
 int        ydpi;
 int        xpix;
 int        ypix;

 err=NULL;

 /* nb name is not zero terminated */

  /* to remember sprite type */
  loadfile->frames[fn].sptype = sp_info.type;
  loadfile->frames[fn].spcolorder = sp_info.colour_order;
  loadfile->frames[fn].spformat = sp_info.format;

 /* Need to deal with the different sprite types */
 if (sp_info.format == SPRITE_FORMAT_ROS_50)
 {

    xdpi = 180 >> sph->selector.type5.xeigen;
    ydpi = 180 >> sph->selector.type5.yeigen;

    spbpp=bppmap[sph->selector.type5.type];

    bitwidth=32*(sph->width+1)-(31-sph->rbit);
    xpix=bitwidth/spbpp;
    ypix=sph->height+1;
 }
 else
 {

   if(sph->selector.type.type)  /* it is a new risc os 3.5 sprite */
   {
    xdpi=sph->selector.type.xdpi;
    ydpi=sph->selector.type.ydpi;
    spbpp=bppmap[sph->selector.type.type];

  ////////////////////////////////////////
  #if 0
    dprintf(5,"xdpi=%d ydpi=%d bpp=%d type=%d",
               sph->selector.type.xdpi,sph->selector.type.ydpi,
               spbpp,sph->selector.type.type);
  #endif
  ////////////////////////////////////////

    bitwidth=32*(sph->width+1)-(31-sph->rbit);
    xpix=bitwidth/spbpp;
    ypix=sph->height+1;

   }
   else
   {
    err=getdeltas(sph->selector.mode.mode,&spdx,&spdy);
    if(!err)
    {
     getbpp(sph->selector.mode.mode,&spbpp,&spbitmask, NULL);
     spbpp=(1<<spbpp);

      loadfile->frames[fn].sptype = 0;
    }

    bitwidth=32*(sph->width+1)-sph->lbit-(31-sph->rbit);

    xpix=bitwidth/spbpp;
    ypix=sph->height+1;

    xdpi=180/spdx;
    ydpi=180/spdy;
   }
 }

 p=sph->name;
 q=loadfile->frames[fn].name;
 while(*p>=32) *q++=*p++;
 *q=0;


 loadfile->frames[fn].fbpp=spbpp;
 loadfile->frames[fn].fxdpi=xdpi;
 loadfile->frames[fn].fydpi=ydpi;
 loadfile->frames[fn].fxpix=xpix;
 loadfile->frames[fn].fypix=ypix;

 return(err);
 USE(bf);
}





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

static os_error * sploadframe(filestr * file, buffer * bf,
                                                newsprite_header * sph, int fn)
{
 os_error * err;
 imagestr * loadimage;
 int        y;
 int      * idata;
 int        mbpp;
 int        mask;
 int        mwidth;
 int      * buff;


 if(sph->mask!=sph->image)
 {
  if(sp_info.type)
  {
   mbpp=1;
   mwidth=((file->frames[fn].fxpix+0x1F) & (~0x1F))/8;
  }
  else
  {
   mbpp=file->frames[fn].fbpp;
   mwidth=(sph->width+1)*sizeof(int);
  }
 }
 else
 {
  mbpp=mwidth=0;
 }


 err=bf_tell(bf,&mask);
 mask-=sizeof(sprite_header);
 mask+=sph->mask;


 err=ximnew2(file->frames[fn].fxpix,file->frames[fn].fypix,
            file->frames[fn].fbpp,(mbpp>0)?1:0,&file->frames[fn].xim);

 if(!err)
 {
  loadimage=((file->frames[fn].xim)->sim[IM]);
  loadimage->xdpi=file->frames[fn].fxdpi;
  loadimage->ydpi=file->frames[fn].fydpi;

  getpal(bf,loadimage,sph);

  for(y=0;y<file->frames[fn].fypix;y++)
  {
   err=imfind1w(loadimage,y,&idata);
   if(err) break;

   err=bf_read(bf,idata,(sph->width+1)*sizeof(int));
   if(err) break;
   if(sph->lbit) shiftline(idata,file->frames[fn].fxpix,
                                 file->frames[fn].fbpp,sph->lbit);
  }

  if(mbpp)
  {
   bf_seek(bf,mask);

   loadimage=((file->frames[fn].xim)->sim[AL]);

   imsetgreypal(loadimage,1);

   if(mbpp==1)
   {
    for(y=0;y<file->frames[fn].fypix;y++)
    {
     err=imfind1w(loadimage,y,&idata);
     if(err) break;
     err=bf_read(bf,idata,mwidth);
     if(err) break;
     if(sph->lbit) shiftline(idata,file->frames[fn].fxpix,
                                  1,(sph->lbit*mbpp)/file->frames[fn].fbpp);
    }
   }
   else
   if(mbpp!=0)
   {
    err=salloc((flex_ptr)&buff,mwidth);
    if(!err)
    {
     for(y=0;y<file->frames[fn].fypix;y++)
     {
      err=bf_read(bf,buff,mwidth);
      if(err) break;

      if(sph->lbit) shiftline(idata,file->frames[fn].fxpix,
                                  mbpp,(sph->lbit*mbpp)/file->frames[fn].fbpp);

      err=imfind1w(loadimage,y,&idata);
      if(err) break;

      maskcut(buff,mbpp,idata,1,file->frames[fn].fxpix);
     }
     sfree((flex_ptr)&buff);
    }
   }
  }
 }
 return(err);
}







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

static os_error *loadasprite( void )
{
  os_error    * err;
  sprite_area   spa;
  newsprite_header sph;
  int           i;
  int           next;
  int           fn;


  err = bf_read(bfp, &spa.number, sizeof(sprite_area)-sizeof(int));


  if(!err) err = bf_seek(bfp, spa.sproff-sizeof(int));
  if(!err)
  {
    for(i = 0; i < spa.number; i++)
    {
      err = bf_tell(bfp, &next);
      if(err) break;

      err = bf_read(bfp, &sph, sizeof(sprite_header));
      if(err) break;

      err = addframe(loadfile, &fn, 0);
      if(err) break;

      loadfile->frames[fn].offset = next;
      loadfile->frames[fn].xim = NULL;
      next += sph.next;

      err = spreadhdr(fn, bfp, &sph);
      if(err) break;

      if(i == 0)
      {
        err = sploadframe(loadfile, bfp, &sph, fn);
        if(err) break;
      }

      err = bf_seek(bfp, next);
      if(err) break;
    }

    /* now reset the ram variables */
    ram_resetmemoryread();
  }


  return(err);

}

////////////////////////////////////////////////////////////////////////////



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

static os_error * setup_convert_buffer (char *spr_data, int buf_size, int data_size)
{
  os_error    * err;

  setup_buffer (spr_data, buf_size, data_size);
  ramnextreadfilememoryt (spr_data, data_size);
  err = loadasprite ();

  return(err);
}




#ifdef NEVER
/************************************************************/

static void add_extension (char *name, int type)
{
  char *p, *q;

  p = fs_leaf (name);
  /* remove any existing extension */
  q = strrchr (p, '/');
  if (q) *q = 0;
  /* add required ext */
  if (type == PNG)
  {
    strcat (p, "/png");
  }
  else
  {
    strcat (p, "/jpg");
  }
  return;
}
#endif






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

os_error * convert_spr_to_png ( char *fname, char *spr_data, int buf_size, int data_size)
{
  os_error    * err;
//////  char name[256];

  hourglasson();
//////  strcpy (name, fname);
  err = setup_convert_buffer (spr_data, buf_size, data_size);
/////  if (add_ftype_ext) add_extension (name, PNG);
  if (!err) err = savepng (loadfile, 0, fname, PNG);
  trashall (loadfile);
  hourglassoff();

  return (err);
}




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

os_error * convert_spr_to_jpeg ( char *fname,
                                 char *spr_data,
                                 int buf_size,
                                 int data_size,
                                 int quality )
{
  os_error    * err;
//////  char name[256];

  hourglasson();
//////  strcpy (name, fname);
  err = setup_convert_buffer (spr_data, buf_size, data_size);
/////  if (add_ftype_ext) add_extension (name, JPEG);
  if (!err) err = savejpeg (fname, loadfile, quality);
  trashall (loadfile);
  hourglassoff();

  return (err);
}










/*************  End of c.convert  ***********************/

