/*
 * VF_Comp.c
 *
 *  Programmmed by Hirotsugu Kakugawa, Hiroshima University
 *  E-Mail:  h.kakugawa@computer.org
 *
 *  Edition History
 *   5 Nov 1993  
 *  20 Jan 1994  Added GetOutline2().
 *   8 Mar 1994  Fixed small bugs and removed dead code
 */


/* This file is part of VFlib
 *
 * Copyright (C) 1993-1998 Hirotsugu KAKUGAWA.   All rights reserved.
 *
 * This file is part of the VFlib Library.  This library is free
 * software; you can redistribute it and/or modify it under the terms of
 * the GNU Library General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  This library is distributed in the hope
 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU Library General Public License for more details.
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


/*
  Capabilities interpretable by compound font objects::
  "kn"  (str)  --  Kana Font Entry
  "kj"  (str)  --  Kanji Font Entry
  "sy"  (str)  --  Symbol Font Entry
 */

#include  <stdio.h> 
#include  <stdlib.h> 
#include  <ctype.h> 
#include  "config.h"
#include  "defs.h"
#include  "_VF.h"
#include  "VF.h"
#include  "VFcap.h"

#ifndef MAX
#define MAX(a, b) ( ((a)>(b)) ? (a) : (b) )
#endif
#ifndef MIN
#define MIN(a, b) ( ((a)<(b)) ? (a) : (b) )
#endif

#define RANGE_SET_MAX 32
#define P_FONT_MAX 16

struct s_range {
    int start;
    int end;
};
typedef struct s_range  Range;
typedef struct s_range* RangeSet;
static Range     NULL_Range       = { -1, -1 };
static RangeSet NULL_RangeSet   = { &NULL_Range };

struct s_p_font {
  char      *name;
  int       FD;
  RangeSet range_set;
};
typedef struct s_p_font P_FONT;

struct s_font {
  int     fd;
  P_FONT  p_font_set[P_FONT_MAX];
};
typedef struct s_font  Font;


Private int    OpenFont();
Private int    CloseFont();
Private int    GetBitmap();
Private long*  GetOutline();
Private long*  GetOutline2();
Private int    DrawOutline();
Private int    FreeOutline();
Private int    Link();
Private int    Unlink();

Private  int ReadCapa();
Private  RangeSet parse_range_set();
Private  int is_in();



Public FontObj*
CreateFont_Comp2(ent)
  char *ent;
{
  Font    *font;
  FontObj *fobj;

  if ((font = (Font*) malloc(sizeof(Font))) == NULL)
    return NULL;  /* ERR: malloc err */
  font->fd = -1;
  if (ReadCapa(font, ent) < 0)
    return NULL;

  fobj = (FontObj*) malloc(sizeof(FontObj));
  fobj->ClassID     = VF_FONT_COMPOUND2;
  fobj->Self        = fobj;
  fobj->LinkCount   = 0;
  fobj->OpenFont    = OpenFont;
  fobj->CloseFont   = CloseFont;
  fobj->GetBitmap   = GetBitmap;
  fobj->GetOutline  = GetOutline;
  fobj->GetOutline2 = GetOutline2;
  fobj->DrawOutline = DrawOutline;
  fobj->FreeOutline = FreeOutline;
  fobj->GetCharSet  = NULL;
  fobj->GetEnc      = NULL;
  fobj->Link        = Link;
  fobj->Unlink      = Unlink;
  fobj->Locals      = (long) font;
  return fobj;
}
       

Private int
OpenFont(obj)
  FontObj* obj;
{
  Font  *font;
  int i;

  font = (Font*) obj->Locals;
  for (i = 0; i < P_FONT_MAX; i++) {
      if (font->p_font_set[i].name != NULL)
	  font->p_font_set[i].FD = VF_OpenFont(font->p_font_set[i].name);
  }
  return 0;
}


Private int
CloseFont(obj, fid)
  FontObj  *obj;
  int      fid;
{
  Font  *font;
  int i;

  font = (Font*) obj->Locals;
  for (i = 0; i < P_FONT_MAX; i++) {
      if (font->p_font_set[i].name != NULL)
	  font->p_font_set[i].FD = VF_CloseFont(font->p_font_set[i].FD);
  }
  return 0;
}

Private int
GetBitmap(obj, jiscode, w, h, bw, bo, bm_buf)
  FontObj  *obj;
  int   jiscode;
  int   w;
  int   h;
  int   bw;
  int   bo;
  char  *bm_buf;
{
  int   val = -1;
  Font  *font;
  int i;
  
  font = (Font*) obj->Locals;
  for (i = 0; i < P_FONT_MAX; i++) {
      if (is_in(font->p_font_set[i].range_set, jiscode)) {
	  if (font->p_font_set[i].name != NULL) {
	      val = VF_GetBitmap(jiscode, font->p_font_set[i].FD,
				 w, h, bw, bo, bm_buf);
	  }
	  break;
      }
  }
  return val;
}


Private long*
GetOutline(obj, jiscode)
  FontObj  *obj;
  int      jiscode;
{
  long  *outline = NULL;
  Font  *font;
  int i;
  
  font = (Font*) obj->Locals;
  for (i = 0; i < P_FONT_MAX; i++) {
      if (is_in(font->p_font_set[i].range_set, jiscode)) {
	  if (font->p_font_set[i].name != NULL) {
	      outline = VF_GetOutline(jiscode, font->p_font_set[i].FD);
	  }
	  break;
      }
  }
  return outline;
}


Private long*
GetOutline2(obj, jiscode)
  FontObj  *obj;
  int      jiscode;
{
  long  *outline = NULL;
  Font  *font;
  int i;
  
  font = (Font*) obj->Locals;
  for (i = 0; i < P_FONT_MAX; i++) {
      if (is_in(font->p_font_set[i].range_set, jiscode)) {
	  if (font->p_font_set[i].name != NULL) {
	      outline = VF_GetOutline2(jiscode, font->p_font_set[i].FD);
	  }
	  break;
      }
  }
  return outline;
}


Private int
DrawOutline(obj, vfdata, w, h, bw, bo, bm_buf)
  FontObj  *obj;
  long     *vfdata;
  int   w;
  int   h;
  int   bw;
  int   bo;
  char  *bm_buf;
{
  int   jiscode;
  Font  *font;
  int   val = -1;
  int i;
  
  font = (Font*) obj->Locals;
  jiscode = vfdata[0];
  for (i = 0; i < P_FONT_MAX; i++) {
      if (is_in(font->p_font_set[i].range_set, jiscode)) {
	  if (font->p_font_set[i].name != NULL) {
	      val = VF_DrawOutline(vfdata, font->p_font_set[i].FD,
				   w, h, bw, bo, bm_buf);
	  }
	  break;
      }
  }
  return val;
}


Private int
FreeOutline(obj, vfdata)
  FontObj  *obj;
  long*    vfdata;
{
  Font  *font;
  int   val = -1;
  int i;

  font = (Font*) obj->Locals;
  for (i = 0; i < P_FONT_MAX; i++) {
      if (is_in(font->p_font_set[i].range_set, vfdata[0])) {
	  if (font->p_font_set[i].name != NULL) {
	      val = VF_FreeOutline(vfdata, font->p_font_set[i].FD);
	  }
	  break;
      }
  }
  return val;
}



Private int
Link(obj)
  FontObj  *obj;
{
  obj->LinkCount = obj->LinkCount + 1;
  return obj->LinkCount;
}

Private int
Unlink(obj)
  FontObj  *obj;
{
  obj->LinkCount = obj->LinkCount - 1;
  return obj->LinkCount;
}


static int
ReadCapa(font, ent)
  Font *font;
  char *ent;
{
  char *p;
  int i;
  RangeSet range_set;
  static char f_ent_str[] = "f?";
  static char r_ent_str[] = "r?";

  VFC_GetEntry(ent);

  for (i = 0; i < P_FONT_MAX; i++) {
      f_ent_str[1] = ('A' + i);
      r_ent_str[1] = ('A' + i);
      if ((p = VFC_GetString(f_ent_str)) == NULL)
	  font->p_font_set[i].name = NULL;
      else {
	  if ((font->p_font_set[i].name = malloc(strlen(p)+1)) == NULL)
	      return -1;  /* ERR: malloc err */
	  strcpy(font->p_font_set[i].name, p);
      }
      if ((p = VFC_GetString(r_ent_str)) == NULL)
	  font->p_font_set[i].range_set = NULL_RangeSet;
      else {
	  if ((range_set = parse_range_set(p)) == (RangeSet)NULL)
	      font->p_font_set[i].range_set = NULL_RangeSet;
	  else
	      font->p_font_set[i].range_set = range_set;
      }
  }
  return 0;
}


/*
 * is_in()
 *
 * val が RangeSet の範囲にあるかを調べる関数
 *
 * 範囲にあれば、 TRUE を返す
 *       無ければ  FALSE を返す
 *
 */

Private int
is_in(range_set, val)
    RangeSet range_set;
    int val;
{
    int start, end;
    while ((range_set->start >= 0) && (range_set->end >= 0)) {
	start = range_set->start;
	end = range_set->end;
	if ((start <= val) && (val <= end))
	    return TRUE;
	range_set++;
    }
    return FALSE;
}

/*
 * parse_range_set()
 *
 * 文字列が下の文法に従っていれば、RangeSet を返す。
 * そうでなければ、(RangeSet)NULL を返す
 *
 * (注) malloc() でメモリを割り当てるので、呼出した側で解放すること
 *
 * rangeset: range
 *            | rangeset COMMA range
 * range: num
 *        | num BAR num
 * num: XDIGIT+
 * XDIGIT: [0-9a-fA-F]
 * COMMA:  ','
 * BAR:    '-'
 *
 * example
 *   "1,b-9,1af-25" "ff-5,a,7"          --- OK
 *   "3.5-to" "-2" "2-" "1-2, 3-5"      --- BAD
 *     ^  ^^  ^       ^      ^ 
 */

Private RangeSet 
parse_range_set(str)
    char *str;
{
    int i;
    char *p, *p2;
    int val1, val2;

    Range tmp_set[RANGE_SET_MAX];
    RangeSet set;
    
    int range_no;
    int str_len;

    str_len = strlen(str);
    
    for (i = 0; i < str_len; i++) {
	p = (str+i);
	if (*p == ',')
	    *p = '\0';
	else if (!isxdigit((char)*p) && (*p != '-')) {
	    /* printf("warning: wrong char '%c' found.\n", *p); */
	    return (RangeSet)NULL;
	}
    }

    range_no = 0;
    i = 0;
    while (i < str_len) {
	if (range_no > RANGE_SET_MAX) {
	    /* printf("warning: too many range.\n"); */
	    return (RangeSet)NULL;
	}
	p = (str+i);
	if (*p == '\0') {
	    i++;
	    continue;
	}
	if (*p == '-') {
	    /* printf("warning: no start number.\n"); */
	    return (RangeSet)NULL;
	}
	val1 = strtol(p, &p2, 16);
	if (*p2 != '-') {
	    val2 = val1;
	} else {
	    if (*(p2+1) == '\0') {
		/* printf("warning: no last number.\n"); */
		return (RangeSet)NULL;
	    }
	    val2 = strtol((p2+1), &p2, 16);
	}
	if (*p2 != '\0') {
	    /* printf("warning: wrong format.\n"); */
	    return (RangeSet)NULL;
	}
	tmp_set[range_no].start = MIN(val1, val2);
	tmp_set[range_no].end = MAX(val1, val2);
	range_no++;
	i = i + strlen(p);
    }
    tmp_set[range_no].start = -1;
    tmp_set[range_no].end = -1;

    if (range_no < 1)
	return (RangeSet)NULL;
    set = (RangeSet)malloc((sizeof(Range) * (range_no+1)));
    if (!set)
	return (RangeSet)NULL;
    
    memcpy(set, tmp_set, (sizeof(Range) * (range_no+1)));
    return set;
}
