/*
Copyright (c) 2000-2010, Dirk Krause
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 
  opyright 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 Dirk Krause nor the names of
  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 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.
*/



/**	@file	b2tool1.c
	Tool functions.
*/



/** Inside the b2tool1.c module. */
#define B2TOOL1_C	1
#include "bmepsi.h"




#line 52 "b2tool1.ctr"




/**	Find out how to handle image size.
	@param	bj	Bmeps job.
	@return	0= use width and height as is,
	1= use pHY chunk (resolution chunk) in image,
	2= use specified media size.
*/
int
bmeps_tool_how_to_handle_bb DK_P1(BJ *,bj)
{
  int back = 0;
  
  if(((bj->bo)->opt) & BMEPS_OPT_PAPER_SIZE) {
    back = 2;	
  } else {
    if(((bj->bo)->opt) & BMEPS_OPT_RESOLUTION) {
      back = 1;	
    }
  } 
  return back;
}



/**	Initialize areas.
	@param	bj	Bmeps job.
*/
static
void
initialize_areas DK_P1(BJ *,bj)
{
  (bj->trans).must_rotate = 0;
  (bj->trans).must_scale = 0;
  (bj->trans).sfx = 1.0;
  (bj->trans).sfy = 1.0;
  (bj->trans).hrbb.x0 = 0.0;
  (bj->trans).hrbb.x1 = 0.0;
  (bj->trans).hrbb.y0 = 0.0;
  (bj->trans).hrbb.y1 = 0.0;
  (bj->trans).ia.x0 = 0.0;
  (bj->trans).ia.x1 = 0.0;
  (bj->trans).ia.y0 = 0.0;
  (bj->trans).ia.y1 = 0.0;
  (bj->trans).iu.x0 = 0.0;
  (bj->trans).iu.x1 = 0.0;
  (bj->trans).iu.y0 = 0.0;
  (bj->trans).iu.y1 = 0.0;
  (bj->trans).bb.x0 = 0UL;
  (bj->trans).bb.x1 = 0UL;
  (bj->trans).bb.y0 = 0UL;
  (bj->trans).bb.y1 = 0UL;
}



/**	Get bounding box from a named paper size.
	@param	bj	Bmeps job.
*/
static
void
bb_from_paper_size DK_P1(BJ *,bj)
{
  unsigned long w, h, rotw, roth;
  double dw, dh, deltax, deltay, usedx, usedy;
  int me = 0;
  
  /* available page range */
  (bj->trans).hrbb.x0 = ((bj->bo)->ms).px0;
  (bj->trans).hrbb.x1 = ((bj->bo)->ms).px1;
  (bj->trans).hrbb.y0 = ((bj->bo)->ms).py0;
  (bj->trans).hrbb.y1 = ((bj->bo)->ms).py1;
  /* page range allowed for image placement */
  (bj->trans).ia.x0 = ((bj->bo)->ms).bx0;
  (bj->trans).ia.x1 = ((bj->bo)->ms).bx1;
  (bj->trans).ia.y0 = ((bj->bo)->ms).by0;
  (bj->trans).ia.y1 = ((bj->bo)->ms).by1;
  /* page range used for image placement, rot, scale */
  deltax = dkma_sub_double_ok(
    (bj->trans).ia.x1, (bj->trans).ia.x0, &me
  );
  deltay = dkma_sub_double_ok(
    (bj->trans).ia.y1, (bj->trans).ia.y0, &me
  );
  /* deltax = fabs(deltax); deltay = fabs(deltay); */
  w = dkbif_get_width(bj->bif);
  h = dkbif_get_height(bj->bif);
  rotw = w; roth = h;
  dw = dkma_ul_to_double(w);
  dh = dkma_ul_to_double(h);
  
  
  if(w > h) {
    if(deltay > deltax) {
      (bj->trans).must_rotate = 1;
      
    }
  } else {
    if(w < h) {
      if(deltay < deltax) {
        (bj->trans).must_rotate = 1;
	
      }
    }
  }
  if((bj->trans).must_rotate) {
    
    rotw = h; roth = w;
  }
  dw = dkma_ul_to_double(rotw);	
  dh = dkma_ul_to_double(roth);	
  (bj->trans).sfx = dkma_div_double_ok(deltax, rotw, &me);
  (bj->trans).sfy = dkma_div_double_ok(deltay, roth, &me);
  
  (bj->trans).sfx = dkma_double_restrict_downwards((bj->trans).sfx, 3);
  (bj->trans).sfy = dkma_double_restrict_downwards((bj->trans).sfy, 3);
  
  if((bj->trans).sfx > (bj->trans).sfy) {
    
    (bj->trans).sfx = (bj->trans).sfy;
  } else {
    
    (bj->trans).sfy = (bj->trans).sfx;
  }
  
  usedx = dkma_mul_double_ok(dw, (bj->trans).sfx, &me);
  usedy = dkma_mul_double_ok(dh, (bj->trans).sfy, &me);
  /* correct mathematical problem */
  if(usedx > deltax) { usedx = deltax; }
  if(usedy > deltay) { usedy = deltay; }
  
  
  (bj->trans).iu.x0 = (bj->trans).ia.x0
                       + 0.5 * dkma_sub_double_ok(deltax, usedx, &me);
  /* correct mathematical problem */
  if(fabs((bj->trans).iu.x0) < 0.001) { (bj->trans).iu.x0 = 0.0; }
  (bj->trans).iu.x1 = dkma_add_double_ok((bj->trans).iu.x0, usedx, &me);
  (bj->trans).iu.y0 = (bj->trans).ia.y0
                       + 0.5 * dkma_sub_double_ok(deltay, usedy, &me);
  /* correct mathematical problem */
  if(fabs((bj->trans).iu.y0) < 0.001) { (bj->trans).iu.y0 = 0.0; }
  (bj->trans).iu.y1 = dkma_add_double_ok((bj->trans).iu.y0, usedy, &me);
  /* real bounding box */
  /*
  (bj->trans).bb.x0 = dkma_double_to_ul_ok(floor((bj->trans).iu.x0), &me);
  (bj->trans).bb.y0 = dkma_double_to_ul_ok(floor((bj->trans).iu.y0), &me);
  (bj->trans).bb.x1 = dkma_double_to_ul_ok(ceil((bj->trans).iu.x1), &me);
  (bj->trans).bb.y1 = dkma_double_to_ul_ok(ceil((bj->trans).iu.y1), &me);
  */
  (bj->trans).bb.x0 = dkma_double_to_ul_ok(floor((bj->trans).hrbb.x0), &me);
  (bj->trans).bb.y0 = dkma_double_to_ul_ok(floor((bj->trans).hrbb.y0), &me);
  (bj->trans).bb.x1 = dkma_double_to_ul_ok(ceil((bj->trans).hrbb.x1), &me);
  (bj->trans).bb.y1 = dkma_double_to_ul_ok(ceil((bj->trans).hrbb.y1), &me);
  
  
  
  
  
}



/**	Complete bounding box calculation
	@param	bj	Bmeps job.
*/
static
void
bb_complete DK_P1(BJ *,bj)
{
  /* allowed area */
  (bj->trans).ia.x0 = (bj->trans).iu.x0;
  (bj->trans).ia.y0 = (bj->trans).iu.y0;
  (bj->trans).ia.x1 = (bj->trans).iu.x1;
  (bj->trans).ia.y1 = (bj->trans).iu.y1;
  /* paper size */
  (bj->trans).hrbb.x0 = dkma_ul_to_double((bj->trans).bb.x0);
  (bj->trans).hrbb.y0 = dkma_ul_to_double((bj->trans).bb.y0);
  (bj->trans).hrbb.x1 = dkma_ul_to_double((bj->trans).bb.x1);
  (bj->trans).hrbb.y1 = dkma_ul_to_double((bj->trans).bb.y1);
}



/**	Create bounding box, ignore resolution settings.
	@param	bj	Bmeps job.
*/
static
void
bb_ignore_resolution DK_P1(BJ *,bj)
{
  
  /* real bounding box */
  (bj->trans).bb.x0 = 0UL; (bj->trans).bb.y0 = 0UL;
  (bj->trans).bb.x1 = dkbif_get_width(bj->bif);
  
  (bj->trans).bb.y1 = dkbif_get_height(bj->bif);
  
  /* used area */
  (bj->trans).iu.x0 = 0.0; (bj->trans).iu.y0 = 0.0;
  (bj->trans).iu.x1 = dkma_ul_to_double((bj->trans).bb.x1);
  (bj->trans).iu.y1 = dkma_ul_to_double((bj->trans).bb.y1);
  bb_complete(bj);
  
}



/**	Create bounding box using resolution information.
	@param	bj	Bmeps job.
*/
static
void
bb_use_resolution DK_P1(BJ *,bj)
{
  int me = 0; double delta;
  (bj->trans).must_scale = dkbif_get_must_scale(bj->bif);
  if((bj->trans).must_scale) {
    (bj->trans).iu.x0 = 0.0; (bj->trans).iu.y0 = 0.0;
    (bj->trans).iu.x1 = dkbif_get_real_width(bj->bif);
    (bj->trans).iu.y1 = dkbif_get_real_height(bj->bif);
    (bj->trans).sfx = dkma_div_double_ok(
      (bj->trans).iu.x1,
      dkma_ul_to_double(dkbif_get_width(bj->bif)),
      &me
    );
    (bj->trans).sfx = dkma_double_restrict_downwards((bj->trans).sfx, 5);
    (bj->trans).iu.x1 = dkma_mul_double_ok(
      dkma_ul_to_double(dkbif_get_width(bj->bif)),
      (bj->trans).sfx,
      &me
    );
    (bj->trans).sfy = dkma_div_double_ok(
      (bj->trans).iu.y1,
      dkma_ul_to_double(dkbif_get_height(bj->bif)),
      &me
    );
    (bj->trans).sfy = dkma_double_restrict_downwards((bj->trans).sfy, 5);
    (bj->trans).iu.y1 = dkma_mul_double_ok(
      dkma_ul_to_double(dkbif_get_height(bj->bif)),
      (bj->trans).sfy,
      &me
    );
    (bj->trans).ia.x0 = 0.0;
    (bj->trans).ia.y0 = 0.0;
    (bj->trans).ia.x1 = ceil((bj->trans).iu.x1);
    (bj->trans).ia.y1 = ceil((bj->trans).iu.y1);
    delta = 0.5 * dkma_sub_double_ok(
      (bj->trans).ia.x1, (bj->trans).iu.x1, &me
    );
    (bj->trans).iu.x0 = delta;
    (bj->trans).iu.x1 = dkma_add_double_ok((bj->trans).iu.x1, delta, &me);
    delta = 0.5 * dkma_sub_double_ok(
      (bj->trans).ia.y1, (bj->trans).iu.y1, &me
    );
    (bj->trans).iu.y0 = delta;
    (bj->trans).iu.y1 = dkma_add_double_ok((bj->trans).iu.y1, delta, &me);
    (bj->trans).bb.x0 = 0UL; (bj->trans).bb.y0 = 0UL;
    (bj->trans).bb.x1 = dkma_double_to_ul((bj->trans).ia.x1);
    (bj->trans).bb.y1 = dkma_double_to_ul((bj->trans).ia.y1);
    (bj->trans).hrbb.x0 = dkma_ul_to_double((bj->trans).bb.x0);
    (bj->trans).hrbb.y0 = dkma_ul_to_double((bj->trans).bb.y0);
    (bj->trans).hrbb.x1 = dkma_ul_to_double((bj->trans).bb.x1);
    (bj->trans).hrbb.y1 = dkma_ul_to_double((bj->trans).bb.y1);
  } else {
    bb_ignore_resolution(bj);
  }
  if(me) {
    /* ##### ERROR: Math error. Not yet handled! */
  }
}



/**	Calculate used areas, shifting and rotation.
	@param	bj	Bmeps job.
*/
void
bmeps_tool_calculate_areas DK_P1(BJ *,bj)
{
  
  if(bj) {
    initialize_areas(bj);
    switch(bmeps_tool_how_to_handle_bb(bj)) {
      case 2: {		/* bb from paper size */
        bb_from_paper_size(bj);
      } break;
      case 1: {		/* bb from pHY chunk if available */
        bb_use_resolution(bj);
      } break;
      default: {	/* bb from image dimensions */
        bb_ignore_resolution(bj);
      } break;
    }
  }
  
}



/**	Get number of PDF bits per component for a specified
	input number of bits per component.
	@param	b	Bits per component from input file.
	@return	Bits per component for PDF output.
*/
unsigned short
bmeps_tool_pdf_output_bits DK_P1(unsigned short,b)
{
  unsigned short back = 1;
  if(b > 1) {
    back = 2;
    if(b > 2) {
      back = 4;
      if(b > 4) {
        back = 8;
      }
    }
  }
  return back;
}



/**	Get number of EPS bits per component for a specified
	input number of bits per component.
	@param	b	Bits per component from input file.
	@return	Bits per component for PS/EPS output.
*/
unsigned short
bmeps_tool_eps_output_bits DK_P1(unsigned short,b)
{
  unsigned short back = 1;
  if(b > 1) {
    back = 2;
    if(b > 2) {
      back = 4;
      if(b > 4) {
        back = 8;
	if(b > 8) {
	  back = 12;
	}
      }
    }
  }
  return back;
}



/**	Correct inconsitencies (if any) in bmeps options set.
	@param	bo	Bmeps options set.
*/
void
bmeps_tool_correct_bo DK_P1(BO *,bo)
{
  
  switch(bo->ot) {
    case BMEPS_OUTPUT_TYPE_EPS: {
      if(bo->l < BMEPS_PS_LEVEL_2) {
        bo->opt &= (~(BMEPS_OPT_OPERATOR_DICTIONARY));
	bo->opt &= (~(BMEPS_OPT_COLOR_OUTPUT));
	bo->opt &= (~(BMEPS_OPT_OPERATOR_DICTIONARY));
	bo->enc &= (~(BMEPS_ENCODING_ASCII85));
	bo->enc &= (~(BMEPS_ENCODING_RUNLENGTH));
	bo->enc &= (~(BMEPS_ENCODING_DCT));
	bo->enc &= (~(BMEPS_ENCODING_LZW));
      }
      if(bo->l < BMEPS_PS_LEVEL_3) {
        bo->opt &= (~(BMEPS_OPT_IMAGE_MASK));
	bo->enc &= (~(BMEPS_ENCODING_FLATE));
      } else {
        if(bo->opt & BMEPS_OPT_IMAGE_MASK) {
	  bo->opt |= BMEPS_OPT_OPERATOR_DICTIONARY;
	}
      }
      if(!(bo->opt & BMEPS_OPT_COLOR_OUTPUT)) {
        bo->opt &= (~(BMEPS_OPT_SEPARATED_DATA));
      }
      if(!(bo->opt & BMEPS_OPT_DICTIONARY)) {
        bo->opt &= (~(BMEPS_OPT_VMRECLAIM));
      }
      if(!(bo->opt & BMEPS_OPT_OPERATOR_DICTIONARY)) {
        bo->opt &= (~(BMEPS_OPT_INTERPOLATE));
      }
    } break;
    case BMEPS_OUTPUT_TYPE_PDF: {
      if(bo->l < BMEPS_PDF_LEVEL_14) {
	bo->opt &= (~(BMEPS_OPT_TRANSFER_ALPHA));
      }
      if(bo->opt & BMEPS_OPT_TRANSFER_ALPHA) {
        bo->opt &= (~(BMEPS_OPT_ALPHA_MIX));
	bo->opt &= (~(BMEPS_OPT_IMAGE_MASK));
      }
    } break;
    default: {
    } break;
  }
  
}



/**	Check whether interpolation flag must be enabled.
	@param	bj	Bmeps job.
	@return	Value of interpolation flag.
*/
int
bmeps_tool_interpolate DK_P1(BJ *,bj)
{
  int back = 0;
  if(bj) {
    switch(bj->it) {
      case DKBIF_TYPE_JPG: {
        if((bj->bo2)->opt & BMEPS_OPT_JPEG_INTERPOLATE) {
	  if((bj->bo2)->opt & BMEPS_OPT_INTERPOLATE) {
	    back = 1;
	  }
	}
      } break;
      default: {
        if((bj->bo2)->opt & BMEPS_OPT_INTERPOLATE) {
	  back = 1;
	}
      } break;
    }
  }
  return back;
}



/**	File name suffixes for output types.
*/
char *b2tool_ot_suffixes[] = {
  ".eps", ".pdf", ".bb", NULL
};



/**	Find file name suffix for output type.
	@param	t	Output type.
	@return	File name suffix.
*/
char *
bmeps_tool_suffix_for_output_type DK_P1(int,t)
{
  char *back = NULL;
  back = b2tool_ot_suffixes[0];
  switch(t) {
    case BMEPS_OUTPUT_TYPE_PDF: { back = b2tool_ot_suffixes[1]; } break;
    case BMEPS_OUTPUT_TYPE_BB : { back = b2tool_ot_suffixes[2]; } break;
  }
  return back;
}



/**	Calculate dimensions (line width, angle) for draft.
	@param	bj	Bmeps job.
	@param	dp1	Inner lines.
	@param	dp2	Outer lines.
	@param	f1	Inner lines factor.
	@param	f2	Border lines factor.
	@return	1 on success, 0 on error.
*/
int
bmeps_tool_calc_di DK_P5(BJ *,bj, DI *,dp1, DI *,dp2, double,f1, double,f2)
{
  int back = 1;
  int me = 0;
  double w, h, alpha, d1, d2;
  w = dkma_sub_double_ok((bj->trans).iu.x1, (bj->trans).iu.x0, &me);
  h = dkma_sub_double_ok((bj->trans).iu.y1, (bj->trans).iu.y0, &me);
  alpha = atan2(w, h);
  if(w < h) {
    d1 = f1 * w;
    d2 = f2 * w;
  } else {
    d1 = f1 * h;
    d2 = f2 * h;
  }
  dp1->d = d1; dp2->d = d2;
  dp1->deltax = dkma_div_double_ok(
    d1,
    (2.0 * sin(alpha)),
    &me
  );
  dp1->deltay = dkma_div_double_ok(
    d1,
    (2.0 * cos(alpha)),
    &me
  );
  dp2->deltax = dkma_div_double_ok(
    d2,
    (2.0 * sin(alpha)),
    &me
  );
  dp2->deltay = dkma_div_double_ok(
    d2,
    (2.0 * cos(alpha)),
    &me
  );
  if(me) back = 0;
  return back;
}



/**	Get text for a message number.
	@param	bj	Bmeps job.
	@param	n	Index in message array.
	@return	The message text.
*/
char *
bmeps_get_msg_no DK_P2(BJ *,bj, size_t,n)
{
  char *back = NULL;
  if(bj->msg) {
    back = (bj->msg)[n];
    if(!back) {
      back = bmeps_str_get(n);
    }
  } else {
    back = bmeps_str_get(n);
  }
  return back;
}




/**	Show simple error message.
	@param	bj	Bmeps job.
	@param	ll	Log level (DK_LOG_xxx).
	@param	n	Index in string array.
*/
void
bmeps_tool_msg_1 DK_P3(BJ *,bj, int,ll, size_t,n)
{
  char *buffer[2];
  buffer[0] = bmeps_get_msg_no(bj, n);
  if(bj->a) {
    dkapp_log_msg(bj->a, ll, buffer, 1);
  } else {
    if(ll <= DK_LOG_LEVEL_INFO) {
      buffer[1] = bmeps_get_msg_no(bj, (26 + ll));
      fprintf(stderr, "%s%s\n", buffer[1], buffer[0]);
      fflush(stderr);
    }
  }
}



/**	Show  error message, one custom string:
	bmeps_str_get(n1) s bmeps_str_get(n2).
	@param	bj	Bmeps job.
	@param	ll	Log level (DK_LOG_xxx).
	@param	n1	Index in string array.
	@param	n2	Index in string array.
	@param	s	Customized string.
*/
void
bmeps_tool_msg_3 DK_P5(BJ *,bj, int,ll, size_t,n1, size_t,n2, char *,s)
{
  char *buffer[4];
  buffer[0] = bmeps_get_msg_no(bj, n1);
  buffer[1] = s;
  buffer[2] = bmeps_get_msg_no(bj, n2);
  if(bj->a) {
    dkapp_log_msg(bj->a, ll, buffer, 3);
  } else {
    if(ll <= DK_LOG_LEVEL_INFO) {
      buffer[3] = bmeps_get_msg_no(bj, (26 + ll));
      fprintf(
        stderr, "%s%s%s%s\n",
	buffer[3], buffer[0], buffer[1], buffer[2]
      ); fflush(stderr);
    }
  }
}



/**	Show  error message, two custom strings:
	bmeps_str_get(n1) s1 bmeps_str_get(n2) s2 bmeps_str_get(n3).
	@param	bj	Bmeps job.
	@param	ll	Log level (DK_LOG_xxx).
	@param	n1	Index in string array.
	@param	n2	Index in string array.
	@param	n3	Index in string array.
	@param	s1	1st customized string.
	@param	s2	2nd customized string.
*/
void
bmeps_tool_msg_5 DK_P7(BJ *,bj, int,ll, size_t,n1, size_t,n2, size_t,n3, char *,s1, char *,s2)
{
  char *buffer[6];
  buffer[0] = bmeps_get_msg_no(bj, n1);
  buffer[1] = s1;
  buffer[2] = bmeps_get_msg_no(bj, n2);
  buffer[3] = s2;
  buffer[4] = bmeps_get_msg_no(bj, n3);
  if(bj->a) {
    dkapp_log_msg(bj->a, ll, buffer, 5);
  } else {
    if(ll <= DK_LOG_LEVEL_INFO) {
      buffer[5] = bmeps_get_msg_no(bj, (26 + ll));
      fprintf(
        stderr, "%s%s%s%s%s%s\n",
	buffer[5], buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]
      ); fflush(stderr);
    }
  }
}




/**	Show error message: Not enough memory.
	@param	bj	Bmeps job.
*/
void
bmeps_tool_error_memory DK_P1(BJ *,bj)
{
  if(bj->msg) {
    bmeps_tool_msg_1(bj, DK_LOG_LEVEL_ERROR, 32);
  } else {
    fprintf(stderr, "ERROR: Not enough memory (RAM/swap space)!\n");
    fflush(stderr);
  }
}



