/**********************************************************************
 * File:        ocrblock.cpp  (Formerly block.c)
 * Description: BLOCK member functions and iterator functions.
 * Author:		Ray Smith
 * Created:		Fri Mar 15 09:41:28 GMT 1991
 *
 * (C) Copyright 1991, Hewlett-Packard 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.
 *
 **********************************************************************/

#include "mfcpch.h"
#include          <stdlib.h>
#include          "blckerr.h"
#include          "ocrblock.h"
#include          "tprintf.h"

#define BLOCK_LABEL_HEIGHT  150  //char height of block id

ELISTIZE_S (BLOCK)
/**********************************************************************
 * BLOCK::BLOCK
 *
 * Constructor for a simple rectangular block.
 **********************************************************************/
BLOCK::BLOCK (                   //rectangular block
const char *name,                //filename
BOOL8 prop,                      //proportional
INT16 kern,                      //kerning
INT16 space,                     //spacing
INT16 xmin,                      //bottom left
INT16 ymin, INT16 xmax,          //top right
INT16 ymax):
PDBLK (xmin, ymin, xmax, ymax),
filename(name) {  //box(ICOORD(xmin,ymin),ICOORD(xmax,ymax))
                                 //boundaries
  ICOORDELT_IT left_it = &leftside;
  ICOORDELT_IT right_it = &rightside;

  proportional = prop;
  kerning = kern;
  spacing = space;
  font_class = -1;               //not assigned
  hand_block = NULL;
  hand_poly = NULL;
  left_it.set_to_list (&leftside);
  right_it.set_to_list (&rightside);
                                 //make default box
  left_it.add_to_end (new ICOORDELT (xmin, ymin));
  left_it.add_to_end (new ICOORDELT (xmin, ymax));
  right_it.add_to_end (new ICOORDELT (xmax, ymin));
  right_it.add_to_end (new ICOORDELT (xmax, ymax));
}


/**********************************************************************
 * BLOCK::set_sides
 *
 * Sets left and right vertex lists
 **********************************************************************/

//void                                                          BLOCK::set_sides(                       //set vertex lists
//ICOORDELT_LIST             *left,                        //left vertices
//ICOORDELT_LIST             *right                        //right vertices
//)
//{
//      ICOORDELT_IT                            left_it= &leftside;                             //boundaries
//      ICOORDELT_IT                            right_it= &rightside;

//      leftside.clear();
//      left_it.move_to_first();
//      left_it.add_list_before(left);
//      rightside.clear();
//      right_it.move_to_first();
//      right_it.add_list_before(right);
//}

/**********************************************************************
 * BLOCK::contains
 *
 * Return TRUE if the given point is within the block.
 **********************************************************************/

//BOOL8                                                         BLOCK::contains(                                        //test containment
//ICOORD                                                        pt                                                                              //point to test
//)
//{
//      BLOCK_RECT_IT                           it=this;                                                                //rectangle iterator
//      ICOORD                                          bleft,tright;                                           //corners of rectangle

//      for (it.start_block();!it.cycled_rects();it.forward())
//      {
//              it.bounding_box(bleft,tright);                                                  //get rectangle
//              if (pt.x()>=bleft.x() && pt.x()<=tright.x()                     //inside rect
//              && pt.y()>=bleft.y() && pt.y()<=tright.y())
//                      return TRUE;                                                                                            //is inside
//      }
//      return FALSE;                                                                                                           //not inside
//}

/**********************************************************************
 * BLOCK::move
 *
 * Reposition block
 **********************************************************************/

//void                                                     BLOCK::move(                                         // reposition block
//const ICOORD                                  vec                                                                     // by vector
//)
//{
//   ROW_IT                                             row_it( &rows );
//      ICOORDELT_IT                            it( &leftside );

//      for( row_it.mark_cycle_pt(); !row_it.cycled_list(); row_it.forward() )
//         row_it.data()->move( vec );

//      for( it.mark_cycle_pt(); !it.cycled_list(); it.forward() )
//         *(it.data()) += vec;

//   it.set_to_list( &rightside );

//   for( it.mark_cycle_pt(); !it.cycled_list(); it.forward() )
//         *(it.data()) += vec;

//   box.move( vec );
//}

/**********************************************************************
 * decreasing_top_order
 *
 * Sort Comparator: Return <0 if row1 top < row2 top
 **********************************************************************/

int decreasing_top_order(  //
                         const void *row1,
                         const void *row2) {
  return (*(ROW **) row2)->bounding_box ().top () -
    (*(ROW **) row1)->bounding_box ().top ();
}


/**********************************************************************
 * BLOCK::sort_rows
 *
 * Order rows so that they are in order of decreasing Y coordinate
 **********************************************************************/

void BLOCK::sort_rows() {  // order on "top"
  ROW_IT row_it(&rows); 

  row_it.sort (decreasing_top_order);
}


/**********************************************************************
 * BLOCK::compress
 *
 * Delete space between the rows. (And maybe one day, compress the rows)
 * Fill space of block from top down, left aligning rows.
 **********************************************************************/

void BLOCK::compress() {  // squash it up
  #define           ROW_SPACING 5

  ROW_IT row_it(&rows); 
  ROW *row;
  ICOORD row_spacing (0, ROW_SPACING);

  ICOORDELT_IT icoordelt_it;

  sort_rows(); 

  box = BOX (box.topleft (), box.topleft ());
  box.move_bottom_edge (ROW_SPACING);
  for (row_it.mark_cycle_pt (); !row_it.cycled_list (); row_it.forward ()) {
    row = row_it.data ();
    row->move (box.botleft () - row_spacing -
      row->bounding_box ().topleft ());
    box += row->bounding_box ();
  }

  leftside.clear ();
  icoordelt_it.set_to_list (&leftside);
  icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.bottom ()));
  icoordelt_it.add_to_end (new ICOORDELT (box.left (), box.top ()));
  rightside.clear ();
  icoordelt_it.set_to_list (&rightside);
  icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.bottom ()));
  icoordelt_it.add_to_end (new ICOORDELT (box.right (), box.top ()));
}


/**********************************************************************
 * BLOCK::check_pitch
 *
 * Check whether the block is fixed or prop, set the flag, and set
 * the pitch if it is fixed.
 **********************************************************************/

void BLOCK::check_pitch() {  // check prop
  //      tprintf("Missing FFT fixed pitch stuff!\n");
  pitch = -1;
}


/**********************************************************************
 * BLOCK::compress
 *
 * Compress and move in a single operation.
 **********************************************************************/

void BLOCK::compress(                  // squash it up
                     const ICOORD vec  // and move
                    ) {
  box.move (vec);
  compress(); 
}


/**********************************************************************
 * BLOCK::print
 *
 * Print the info on a block
 **********************************************************************/

void BLOCK::print(            //print list of sides
                  FILE *,     //file to print on
                  BOOL8 dump  //print full detail
                 ) {
  ICOORDELT_IT it = &leftside;   //iterator

  box.print ();
  tprintf ("Proportional= %s\n", proportional ? "TRUE" : "FALSE");
  tprintf ("Kerning= %d\n", kerning);
  tprintf ("Spacing= %d\n", spacing);
  tprintf ("Fixed_pitch=%d\n", pitch);
  tprintf ("Filename= %s\n", filename.string ());

  if (dump) {
    tprintf ("Left side coords are:\n");
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
      tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ());
    tprintf ("\n");
    tprintf ("Right side coords are:\n");
    it.set_to_list (&rightside);
    for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
      tprintf ("(%d,%d) ", it.data ()->x (), it.data ()->y ());
    tprintf ("\n");
  }
}


/**********************************************************************
 * BLOCK::plot
 *
 * Plot the outline of a block in the given colour.
 **********************************************************************/

//void                                                          BLOCK::plot(                                            //draw outline
//WINDOW                                                        window,                                                         //window to draw in
//INT32                                                         serial,                                                         //serial number
//COLOUR                                                        colour                                                          //colour to draw in
//)
//{
//      ICOORD                                          startpt;                                                                //start of outline
//      ICOORD                                          endpt;                                                          //end of outline
//      ICOORD                                          prevpt;                                                         //previous point
//      ICOORDELT_IT                            it= &leftside;                                          //iterator
//      char                                                    number[32];                                                     //block id

//      line_color_index(window,colour);                                                                //set the colour
//      text_color_index(window,colour);
//      character_height(window,(float)BLOCK_LABEL_HEIGHT);
//      text_font_index(window,6);

//      if (!leftside.empty())
//      {
//              startpt= *(it.data());                                                                          //bottom left corner
////            fprintf(stderr,"Block %d bottom left is (%d,%d)\n",
////                    serial,startpt.x(),startpt.y());
//              sprintf(number,"%d",serial);
//              text2d(window,startpt.x(),startpt.y(),number,0,FALSE);

//              move2d(window,startpt.x(),startpt.y());
//              do
//              {
//                      prevpt= *(it.data());                                                                   //previous point
//                      it.forward();                                                                                           //move to next point
//                      draw2d(window,prevpt.x(),it.data()->y());                       //draw round corner
//                      draw2d(window,it.data()->x(),it.data()->y());
//              }
//              while (!it.at_last());                                                                          //until end of list
//              endpt= *(it.data());                                                                                    //end point

//              move2d(window,startpt.x(),startpt.y());                         //other side of boundary
//              it.set_to_list(&rightside);
//              prevpt=startpt;
//              for (it.mark_cycle_pt();!it.cycled_list();it.forward())
//              {
//                      draw2d(window,prevpt.x(),it.data()->y());                       //draw round corner
//                      draw2d(window,it.data()->x(),it.data()->y());
//                      prevpt= *(it.data());                                                                   //previous point
//              }
//              draw2d(window,endpt.x(),endpt.y());                                             //close boundary
//              if (hand_block!=NULL)
//                      hand_block->plot(window,colour,serial);
//      }
//}

/**********************************************************************
 * BLOCK::show
 *
 * Show the image corresponding to a block as its set of rectangles.
 **********************************************************************/

//void                                                          BLOCK::show(                                            //show image block
//IMAGE                                                         *image,                                                         //image to show
//WINDOW                                                        window                                                          //window to show in
//)
//{
//      BLOCK_RECT_IT                           it=this;                                                                //rectangle iterator
//      ICOORD                                          bleft,tright;                                           //corners of rectangle

//      for (it.start_block();!it.cycled_rects();it.forward())
//      {
//              it.bounding_box(bleft,tright);                                                  //get rectangle
////            fprintf(stderr,"Drawing a block with a bottom left of (%d,%d)\n",
////                    bleft.x(),bleft.y());
//              show_sub_image(image,bleft.x(),bleft.y(),
//                      tright.x()-bleft.x(),tright.y()-bleft.y(),
//                      window,bleft.x(),bleft.y());                                                    //show it
//      }
//}

/**********************************************************************
 * BLOCK::operator=
 *
 * Assignment - duplicate the block structure, but with an EMPTY row list.
 **********************************************************************/

BLOCK & BLOCK::operator= (       //assignment
const BLOCK & source             //from this
) {
  this->ELIST_LINK::operator= (source);
  this->PDBLK::operator= (source);
  proportional = source.proportional;
  kerning = source.kerning;
  spacing = source.spacing;
  filename = source.filename;    //STRINGs assign ok
  if (!rows.empty ())
    rows.clear ();
  //   if ( !leftside.empty() )
  //              leftside.clear();
  // if ( !rightside.empty() )
  //              rightside.clear();
  //   leftside.deep_copy( &source.leftside );
  //   rightside.deep_copy( &source.rightside );
  //      box=source.box;
  return *this;
}
