/**********************************************************************
 * File:        pdblock.c  (Formerly pdblk.c)
 * Description: PDBLK 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          "pdblock.h"
#include          "showim.h"

#include          "hpddef.h"     //must be last (handpd.dll)

#define BLOCK_LABEL_HEIGHT  150  //char height of block id

CLISTIZE (PDBLK)
/**********************************************************************
 * PDBLK::PDBLK
 *
 * Constructor for a simple rectangular block.
 **********************************************************************/
PDBLK::PDBLK (                   //rectangular block
INT16 xmin,                      //bottom left
INT16 ymin, INT16 xmax,          //top right
INT16 ymax):    box (ICOORD (xmin, ymin), ICOORD (xmax, ymax)) {
                                 //boundaries
  ICOORDELT_IT left_it = &leftside;
  ICOORDELT_IT right_it = &rightside;

  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));
}


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

void PDBLK::set_sides(                       //set vertex lists
                      ICOORDELT_LIST *left,  //left vertices
                      ICOORDELT_LIST *right  //right vertices
                     ) {
                                 //boundaries
  ICOORDELT_IT left_it = &leftside;
  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);
}


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

BOOL8 PDBLK::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 ()) {
                                 //get rectangle
    it.bounding_box (bleft, tright);
                                 //inside rect
    if (pt.x () >= bleft.x () && pt.x () <= tright.x ()
      && pt.y () >= bleft.y () && pt.y () <= tright.y ())
      return TRUE;               //is inside
  }
  return FALSE;                  //not inside
}


/**********************************************************************
 * PDBLK::move
 *
 * Reposition block
 **********************************************************************/

void PDBLK::move(                  // reposition block
                 const ICOORD vec  // by vector
                ) {
  ICOORDELT_IT it(&leftside); 

  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);
}


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

#ifndef GRAPHICS_DISABLED
void PDBLK::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

                                 //set the colour
  line_color_index(window, 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
    //              tprintf("Block %d bottom left is (%d,%d)\n",
    //                      serial,startpt.x(),startpt.y());
    char temp_buff[34];
    #ifdef __UNIX__
    sprintf(temp_buff, INT32FORMAT, serial); 
    #else
    ultoa (serial, temp_buff, 10);
    #endif
    text2d (window, startpt.x (), startpt.y (), temp_buff, 0, FALSE);

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

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


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

#ifndef GRAPHICS_DISABLED
void PDBLK::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 ()) {
                                 //get rectangle
    it.bounding_box (bleft, tright);
    //              tprintf("Drawing a block with a bottom left of (%d,%d)\n",
    //                      bleft.x(),bleft.y());
                                 //show it
    show_sub_image (image, bleft.x (), bleft.y (), tright.x () - bleft.x (), tright.y () - bleft.y (), window, bleft.x (), bleft.y ());
  }
}
#endif


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

PDBLK & PDBLK::operator= (       //assignment
const PDBLK & source             //from this
) {
  //      this->ELIST_LINK::operator=(source);
  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;
}


/**********************************************************************
 * BLOCK_RECT_IT::BLOCK_RECT_IT
 *
 * Construct a block rectangle iterator.
 **********************************************************************/

BLOCK_RECT_IT::BLOCK_RECT_IT (
//iterate rectangles
PDBLK * blkptr                   //from block
):left_it (&blkptr->leftside), right_it (&blkptr->rightside) {
  block = blkptr;                //remember block
                                 //non empty list
  if (!blkptr->leftside.empty ()) {
    start_block();  //ready for iteration
  }
}


/**********************************************************************
 * BLOCK_RECT_IT::set_to_block
 *
 * Start a new block.
 **********************************************************************/

void BLOCK_RECT_IT::set_to_block(                  //start (new) block
                                 PDBLK *blkptr) {  //block to start
  block = blkptr;                //remember block
                                 //set iterators
  left_it.set_to_list (&blkptr->leftside);
  right_it.set_to_list (&blkptr->rightside);
  if (!blkptr->leftside.empty ())
    start_block();  //ready for iteration
}


/**********************************************************************
 * BLOCK_RECT_IT::start_block
 *
 * Restart a block.
 **********************************************************************/

void BLOCK_RECT_IT::start_block() {  //start (new) block
  left_it.move_to_first ();
  right_it.move_to_first ();
  left_it.mark_cycle_pt ();
  right_it.mark_cycle_pt ();
  ymin = left_it.data ()->y ();  //bottom of first box
  ymax = left_it.data_relative (1)->y ();
  if (right_it.data_relative (1)->y () < ymax)
                                 //smallest step
    ymax = right_it.data_relative (1)->y ();
}


/**********************************************************************
 * BLOCK_RECT_IT::forward
 *
 * Move to the next rectangle in the block.
 **********************************************************************/

void BLOCK_RECT_IT::forward() {  //next rectangle
  if (!left_it.empty ()) {       //non-empty list
    if (left_it.data_relative (1)->y () == ymax)
      left_it.forward ();        //move to meet top
    if (right_it.data_relative (1)->y () == ymax)
      right_it.forward ();
                                 //last is special
    if (left_it.at_last () || right_it.at_last ()) {
      left_it.move_to_first ();  //restart
      right_it.move_to_first ();
                                 //now at bottom
      ymin = left_it.data ()->y ();
    }
    else {
      ymin = ymax;               //new bottom
    }
                                 //next point
    ymax = left_it.data_relative (1)->y ();
    if (right_it.data_relative (1)->y () < ymax)
                                 //least step forward
      ymax = right_it.data_relative (1)->y ();
  }
}


/**********************************************************************
 * BLOCK_LINE_IT::get_line
 *
 * Get the the start and width of a line in the block.
 **********************************************************************/

INT16 BLOCK_LINE_IT::get_line(             //get a line
                              INT16 y,     //line to get
                              INT16 &xext  //output extent
                             ) {
  ICOORD bleft;                  //bounding box
  ICOORD tright;                 //of block & rect

                                 //get block box
  block->bounding_box (bleft, tright);
  if (y < bleft.y () || y >= tright.y ()) {
    //              block->print(stderr,FALSE);
    BADBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y);
  }

                                 //get rectangle box
  rect_it.bounding_box (bleft, tright);
                                 //inside rectangle
  if (y >= bleft.y () && y < tright.y ()) {
                                 //width of line
    xext = tright.x () - bleft.x ();
    return bleft.x ();           //start of line
  }
  for (rect_it.start_block (); !rect_it.cycled_rects (); rect_it.forward ()) {
                                 //get rectangle box
    rect_it.bounding_box (bleft, tright);
                                 //inside rectangle
    if (y >= bleft.y () && y < tright.y ()) {
                                 //width of line
      xext = tright.x () - bleft.x ();
      return bleft.x ();         //start of line
    }
  }
  LOSTBLOCKLINE.error ("BLOCK_LINE_IT::get_line", ABORT, "Y=%d", y);
  return 0;                      //dummy to stop warning
}
