/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/****************************************************************************
 * This source file was written by Acorn Computers Limited. It is part of   *
 * the "DrawFile" library for rendering RISCOS Draw files from applications *
 * in C. It may be used freely in the creation of programs for Archimedes.  *
 * It should be used with Acorn's C Compiler Release 2 or later.            *
 *                                                                          *
 * No support can be given to programmers using this code and, while we     *
 * believe that it is correct, no correspondence can be entered into        *
 * concerning behaviour or bugs.                                            *
 *                                                                          *
 * Upgrades of this code may or may not appear, and while every effort will *
 * be made to keep such upgrades upwards compatible, no guarantees can be   *
 * given.                                                                   *
 ***************************************************************************/

/* -> c.DrawFiles
 *
 * DrawFile module, internal code
 * History:
 * Version 0.1: 15 Feb 89, DAHE: created
 *              13 May 91, ECN: Use SWI names instead of numbers
 *                              Stack checking off on selected functions
 *
 * This file consists of code taken from the !Draw version of files c.draw
 * and c.drawDispl for use in the DrawFile module (release 0.2).
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "h.os"
#include "h.bbc"
#include "h.sprite"
#include "h.font"
#include "h.swis"

#define scalefactor dr_scalefactor /* Name equivalence */
#include "h.DrawIntern.drawfile1"
#include "h.drawfdiag"
#include "h.DrawIntern.drawfile2"

/*=========================================================================*/
/* Code from c.draw */

/* The following are needed to shut the preprocessor up */

/* Return the address of the first path element in a path object */
/* this starts either after the path header (no dash pattern)    */
/* or after the dash pattern                                     */

path_movestr *address_pathstart(draw_objptr hdrptr)
{
  if (hdrptr.pathp->pathstyle.joincapwind & packmask_dashed)
    return (path_movestr*)
           &(hdrptr.pathp->data.dashelements[hdrptr.pathp->data.dashcount]);
  else
    return (path_movestr*)&hdrptr.pathp->data;
}


/* Return the address of the dash pattern of a path object */
/* or zero                                                 */

draw_dashstr *address_dashstart(draw_objptr hdrptr)
{
  if (hdrptr.pathp->pathstyle.joincapwind & packmask_dashed)
    return &hdrptr.pathp->data;
  else
    return 0;
}


/*=========================================================================*/
/* Code from c.drawDispl */


#define visible(A)  ((A.objhdrp->bbox.x0<=clip_x1) &&   \
                     (A.objhdrp->bbox.y0<=clip_y1) &&   \
                     (A.objhdrp->bbox.x1>=clip_x0) &&   \
                     (A.objhdrp->bbox.y1>=clip_y0)      \
                    )

/* ScaleT - for text position coords ('font_paint' takes graphics coords)  */
/* ScaleS - for sprite position coords ('put_sprite takes graphics coords) */
/* ScaleF - T.B.A                                                          */

#define scaleT(A,B) (((A)+ (int)(scalefactor*(double)(B)) ) >> 8)
#define scaleS(A,B) (((A)+ (int)(scalefactor*(double)(B)) ) >> 8)
#define scaleF(A)        ( (int)(scalefactor*(double)(A)/40) )

#pragma -s1

/* GCol(action,colour) where colour is BBGGRRxx (foreground colour) */

static os_error *displ_settruecol(int action,draw_coltyp colour)
{
  os_regset r;

  r.r[0] = colour;    /* BBGGRRxx    */
  r.r[3] = 0;         /* foreground  */
  r.r[4] = action;    /* GCol action */

  return(os_swix(ColourTrans_SetGCOL, &r)) ;
}


static os_error *displ_settruefontcol(font fonth, draw_coltyp foregrd,
                                           draw_coltyp backgrd
                              )
{
  os_regset r;

  r.r[0] = (int)fonth;
  r.r[1] = backgrd;   /* background BBGGRRxx (usually white) */
  r.r[2] = foregrd;   /* foreground BBGGRRxx                 */
  r.r[3] = 14;        /* max offset (magic number)  */

  return(os_swix(0x4074F/*ColourTrans_SetFontColour*/, &r)) ;
}

#pragma -s0


/* ------------------------------------------------------------------------- */

double scalefactor;

os_error *do_objpath(draw_objptr, int,int);
os_error *do_objsprite(draw_objptr hdrptr, int orgx,int orgy);
os_error *do_objtext(draw_objptr hdrptr, int orgx,int orgy);

/* Font catalogue - type defined in drawMenu */
static fontcatstr *fontcat;
static BOOL fontcatseen;

/* Record font catalogue */
void note_fontcat(draw_objptr hdrptr)
{
  int ptr = sizeof(draw_fontliststrhdr);
  int fontNum;
  int size = hdrptr.fontlistp->size;

  /* malloc fontcat to save static space for shared library */
  if (fontcat == 0) fontcat = (fontcatstr *)malloc(sizeof(fontcatstr));

  if (hdrptr.bytep == 0)
  {
    fontcatseen = FALSE;
    return;
  }

  fontcat->list_size = 0;
  while (ptr < size)
  {
    if ((fontNum = (int)hdrptr.bytep[ptr++]) == 0) break;
    fontcat->name[fontNum] = hdrptr.bytep + ptr;
    ptr += strlen(fontcat->name[fontNum]) + 1;
    if (fontNum > fontcat->list_size) fontcat->list_size = fontNum;
  }
  fontcat->menu_size = fontcat->list_size;
  fontcatseen = TRUE;
}

/* ------------------------------------------------------------------- */
/*                                                                     */
/* Draw the object 'hdrptr-> ' whose origin is (orgx,orgy)             */
/*                                                                     */
/*  The object is:                                                     */
/*    a PATH                                                           */
/*    a line of TEXT                                                   */
/*    a GROUPing of objects                                            */
/*                                                                     */
/*    (orgx,orgy) takes into acount window and scroll bar positions.   */
/*    (orgx,orgy) & clip(x0,y0,x1,y1) are in dBase coordinates.        */
/*                                                                     */
/* This version is modified from the one in c.drawDispl, to use a      */
/* different sort of error block, and to call the unknown object       */
/* handler if it has been defined. -> TRUE if no error                 */
/*                                                                     */
/* ------------------------------------------------------------------- */


BOOL  do_objects(draw_objptr hdrptr,draw_objptr limit, int orgx,int orgy,
                 int clip_x0,int clip_y0,int clip_x1,int clip_y1,
                 draw_error *err)
{ os_error *oserr = 0;
  BOOL     ok     = TRUE;

  for ( ; ok && hdrptr.bytep < limit.bytep;
          hdrptr.bytep += hdrptr.objhdrp->size)
    { switch (hdrptr.objhdrp->tag)
      {
        case draw_OBJTEXT:
          if (visible(hdrptr)) oserr = do_objtext(hdrptr, orgx,orgy);
          break;

        case draw_OBJPATH:
          if (visible(hdrptr)) oserr = do_objpath(hdrptr, orgx,orgy);
          break;

        case draw_OBJSPRITE:
          if (visible(hdrptr)) oserr = do_objsprite(hdrptr, orgx,orgy);
          break;

        case draw_OBJGROUP:
          if (visible(hdrptr))
            { draw_objptr objptr,objlim;

              objptr.bytep = hdrptr.bytep + sizeof(draw_groustr);
              objlim.bytep = hdrptr.bytep + hdrptr.objhdrp->size;

              /* Render the objects in the group */
              ok = do_objects(objptr,objlim, orgx,orgy,
                              clip_x0,clip_y0, clip_x1,clip_y1, err);
            }
          break;

        case draw_OBJTAGG:
          if (visible(hdrptr))
            { draw_objptr objptr,objlim;

              objptr.bytep = hdrptr.bytep + sizeof(draw_taggstrhdr);
              objlim.bytep = objptr.bytep + objptr.objhdrp->size;

              /* Render the (one) object within the tagged object */
              ok = do_objects(objptr,objlim, orgx,orgy,
                              clip_x0,clip_y0, clip_x1,clip_y1, err);
            }
          break;

        case draw_OBJTEXTCOL:
        {
          draw_bboxtyp clipBox;
          clipBox.x0 = clip_x0;
          clipBox.y0 = clip_y0;
          clipBox.x1 = clip_x1;
          clipBox.y1 = clip_y1;

          if (visible(hdrptr)) oserr = do_objtextcol(hdrptr, orgx, orgy,
                                                             &clipBox);
          break;
        }

        case draw_OBJTEXTAREA:
        {
          draw_bboxtyp clipBox;
          clipBox.x0 = clip_x0;
          clipBox.y0 = clip_y0;
          clipBox.x1 = clip_x1;
          clipBox.y1 = clip_y1;

          if (visible(hdrptr)) oserr = do_objtextarea(hdrptr, orgx, orgy,
                                                               &clipBox);
          break;
        }

        case draw_OBJFONTLIST:
          note_fontcat(hdrptr);
          break;

        default: /* Call unknown object handler, if there is one */
          if (visible(hdrptr) && dr_unknown_handler != NULL)
            ok = dr_unknown_handler((void*)hdrptr.bytep,
                                    dr_unknown_handle, err);
          break;
      }

      if (oserr)
      {
        DrawFile_OSError(err, *oserr);
        ok = FALSE;
        break;
      }
    }
  return(ok);
}


/* ------------------------------------------------------------------- */


/* Fill and/or outline a path */


static void displ_unpackpathstyle(draw_objptr hdrptr, draw_jointspec *jspecp)
{
  int joincapwind    = hdrptr.pathp->pathstyle.joincapwind;

  jspecp->join       = (joincapwind & packmask_join);
  jspecp->endcap     = (joincapwind & packmask_endcap) >> packshft_endcap;
  jspecp->startcap   = (joincapwind & packmask_startcap) >> packshft_startcap;
  jspecp->reserved   = 0;
  jspecp->mitrelimit = 0xA0000;  /* Mitre limit=10.0 (post script default) */
  jspecp->endtricapwid =
  jspecp->starttricapwid = (hdrptr.pathp->pathstyle.tricapwid << 4);
  jspecp->endtricaphei =
  jspecp->starttricaphei = (hdrptr.pathp->pathstyle.tricaphei << 4);
}



os_error *do_objpath(draw_objptr objhdr, int orgx, int orgy)
{ os_error *err = 0;
  os_regset r;

  draw_coltyp fillcol = objhdr.pathp->fillcolour; /* BBGGRRxx / 0xFFFFFFFF */
  draw_coltyp linecol = objhdr.pathp->pathcolour;
  int linewid         = objhdr.pathp->pathwidth;
  int joincapwind     = objhdr.pathp->pathstyle.joincapwind;

  int matrix[6];

  matrix[0] = matrix[3] = (int) (scalefactor*65536);
  matrix[1] = matrix[2] = 0;
  matrix[4] = orgx;
  matrix[5] = orgy;

  if (fillcol != TRANSPARENT)
  {                                                         /* fill the path */
    err = displ_settruecol(0,fillcol);
    if (err) return(err);

    r.r[0] = (int)address_pathstart(objhdr); /*&(objhdr.pathp->path);*/
    r.r[1] = 0x30 | ((joincapwind & packmask_windrule) >> 5);  /*winding rule*/
    r.r[2] = (int)&matrix[0];                       /* transformation matrix */
    r.r[3] = (int)(200/scalefactor);                             /* flatness */

    err = os_swix(Draw_Fill, &r);
    if (err) return(err);
  }

  if (linecol != TRANSPARENT)
  {                                                    /* stroke the outline */
    draw_jointspec jointspec;

    err = displ_settruecol(0,linecol);
    if (err) return(err);

    displ_unpackpathstyle(objhdr, &jointspec);

    r.r[0] = (int)address_pathstart(objhdr); /*&(objhdr.pathp->path);*/
    r.r[1] = 0x38;                                  /* fill (BBC) style      */
    r.r[2] = (int)&matrix[0];                       /* transformation matrix */
    r.r[3] = (int)(200/scalefactor);                /* flatness              */
    r.r[4] = linewid;                               /* line thickness        */
    r.r[5] = (int)&jointspec;                       /* line cap & join spec. */
    r.r[6] = (int)address_dashstart(objhdr);        /* dash pattern          */

    err = os_swix(Draw_Stroke, &r);
    if (err) return(err);
  }
  return(0);
}


/* ------------------------------------------------------------------- */


/* Set size & spacing of VDU5 characters, units are pixels */

os_error *displ_setVDU5charsize(int xsize,int ysize, int xspace,int yspace)
{ os_error *e;
  currentmode.gcharaltered = 1; /* so we restore before next pollwimp */

  e = bbc_vduq(23,17,7,2,xsize,xsize>>8,ysize,ysize>>8,0,0);
  if (!e) bbc_vduq(23,17,7,4,xspace,xspace>>8,yspace,yspace>>8,0,0);
  return(e);
}

/* Draw a line of text */

/* If the font can't be found (ie 'font disc not present' or font name  */
/* came from a fontlist object), use the system font. This seems kinder */
/* than aborting, or poping up an error box.                            */
/* Any other errors are passed back.                                    */

os_error *do_objtext(draw_objptr hdrptr, int orgx,int orgy)
{ os_error *err = 0;
  int textcol = hdrptr.textp->textcolour;
  int backgrd = hdrptr.textp->background; /* a hint (kludge) to font munger */

  if (textcol == TRANSPARENT) return(0); /* nothing to plot */

  if (fontcatseen && hdrptr.textp->textstyle.fontref)
    { font fonth;
      err = font_find(fontcat->name[hdrptr.textp->textstyle.fontref],
                      scaleF(hdrptr.textp->fsizex),
                      scaleF(hdrptr.textp->fsizey),
                      0,0,
                      &fonth
                     );
      if (!err)
        {
          err = font_setfont(fonth);

          if (!err)
            { err = displ_settruefontcol(fonth,textcol,backgrd);

              if (!err)
                err = font_paint(&hdrptr.textp->text[0],
                                 font_OSCOORDS | font_ABS,
                                 scaleT(orgx, hdrptr.textp->coord.x),
                                 scaleT(orgy, hdrptr.textp->coord.y)
                                );
            }
          (void) font_lose(fonth);
          return(err);
        }
    }


/* either system font specified, or fancy font could not be found */
/* so render in the system font (ie scaled VDU5 characters)       */
  { int xsize = (int)((scalefactor * hdrptr.textp->fsizex) /
                       currentmode.pixsizex);
    int ysize = (int)((scalefactor * hdrptr.textp->fsizey) /
                       currentmode.pixsizey);
    /* xsize,ysize in pixels. pixsizex,pixsizey in dBase coords per pixel */
    err = displ_settruecol(0,textcol);
    if (err) return(err);
    /* assume char base line is row 7 (of 8) */
    err = bbc_move(scaleT(orgx, hdrptr.textp->coord.x),
                   scaleT(orgy, hdrptr.textp->coord.y +
                                 7*(int)hdrptr.textp->fsizey/8)
                  );
    if (err) return(err);
    err = displ_setVDU5charsize(xsize,ysize, xsize,ysize);
    if (err) return(err);
    err = bbc_stringprint(&hdrptr.textp->text[0]);
  }
  return(err);
}



/*********************************************
 * Call the SWI to build a translation table *
 *********************************************/

#pragma -s1

static os_error *selecttable(int sourcemode, int *sourcepal, int destmode,
                      int *destpal, char *transtab)
{
 os_regset regs ;

 regs.r[0] = sourcemode ;
 regs.r[1] = (int) sourcepal ;
 regs.r[2] = destmode ;
 regs.r[3] = (int) destpal ;
 regs.r[4] = (int) transtab ;

 return (os_swix(ColourTrans_SelectTable, &regs));
}

#pragma -s0

/* plot a sprite */

os_error *do_objsprite(draw_objptr hdrptr, int orgx,int orgy)
{ os_error *err = 0;

  sprite_id      id;
  sprite_factors factors;

  int  palentries;
  int  *sourcepal;                /* Points to paltab OR =0 (no palette) */
  int  paltab[256];               /* for 1st flash colours in sprite     */
  char pixtrans[256];             /* pixel conversion tab                */

  id.tag    = sprite_id_addr;
  id.s.addr = &hdrptr.spritep->sprite;

  palentries = (hdrptr.spritep->sprite.image - sizeof(sprite_header))/8;
  if ((palentries > 256) || (palentries < 0)) palentries = 0;

  if (palentries == 0)
    sourcepal = 0;                /* no palette, use default for sprite.mode */
  else
    { int *from = hdrptr.spritep->palette;  /* sprites palette entries       */
      int *to   = paltab;                   /* have 1st & 2nd flash colours  */
      int i;                                /* extract ONLY 1st flash colour */

      for (i=palentries; i>0; i--) { *to++ = *from; from += 2; }

      sourcepal = paltab;    /* use sprite palette */
    }

  err = selecttable(hdrptr.spritep->sprite.mode, /* srce mode = sprite mode  */
                    sourcepal,             /* srce pal  = sprite/default pal */
                    -1,                    /* dest mode = current mode       */
                    (int*)-1,              /* dest pal  = current pal        */
                    &pixtrans[0]
                   );
  if (err) return(err);

  { sprite_id id;
    sprite_info info ;

    id.tag    = sprite_id_addr;
    id.s.addr = &hdrptr.spritep->sprite;

    (void)sprite_readsize((sprite_area*)0xFF, /* this op needs no spArea */
                          &id,                /* pass address of sprite  */
                          &info               /* result block            */
                         );

    factors.xmag = (int)((hdrptr.spritep->bbox.x1 - hdrptr.spritep->bbox.x0) *
                         scalefactor
                        );
    factors.xdiv = currentmode.pixsizex * info.width;
    factors.ymag = (int)((hdrptr.spritep->bbox.y1 - hdrptr.spritep->bbox.y0) *
                         scalefactor
                        );
    factors.ydiv = currentmode.pixsizey * info.height;
  }

  err = sprite_put_scaled((sprite_area*)0xFF, /* this op needs no spArea */
                          &id,                /* pass address of sprite  */
                          8,                  /* GcolAction=STORE thro mask */
                          scaleS(orgx, hdrptr.spritep->bbox.x0),
                          scaleS(orgy, hdrptr.spritep->bbox.y0),
                          &factors,
                          (sprite_pixtrans*)&pixtrans[0]
                        );
  return(err);
}

extern BOOL drawfiles_init(void);

BOOL drawfiles_init(void)
{
    return ((fontcat = malloc(sizeof(fontcatstr))) != 0);
}