(********************************************************************)
(*                                                                  *)
(*  graph_file.s7i  Implementation type to display graphic text     *)
(*  Copyright (C) 2001, 2005, 2007, 2011  Thomas Mertes             *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime 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 Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


include "draw.s7i";
include "text.s7i";


(**
 *  Implementation type to display graphic [[text]].
 *  This type allows writing text to graphic windows.
 *  This is done with the standard font of the graphic library.
 *)
const type: graph_file is sub null_file struct
    var PRIMITIVE_WINDOW: win is PRIMITIVE_WINDOW.value;
    var integer: line_delta is 0;
    var integer: column_delta is 0;
    var integer: height is 0;
    var integer: width is 0;
    var integer: line is 0;
    var integer: column is 0;
    var integer: min_x is 0;
    var integer: min_y is 0;
    var integer: lineStartX is 0;
    var integer: curr_x is 0;
    var integer: curr_y is 0;
    var color: foreground is white;
    var color: background is black;
  end struct;


type_implements_interface(graph_file, text);


(**
 *  Creates a ''graph_file'' at the upper left corner of ''graphWin''.
 *  The ''graph_file'' extends over the whole ''graphWin''.
 *  @return the file opened.
 *)
const func graph_file: open (in PRIMITIVE_WINDOW: graphWin) is func
  result
    var graph_file: aGraphFile is graph_file.value;
  begin
    aGraphFile.win := graphWin;
    aGraphFile.line_delta := 13;
    aGraphFile.column_delta := 6;
    aGraphFile.height := height(graphWin) div aGraphFile.line_delta;
    aGraphFile.width := width(graphWin) div aGraphFile.column_delta;
    aGraphFile.line := 1;
    aGraphFile.column := 1;
    aGraphFile.min_x := 0;
    aGraphFile.min_y := 0;
    aGraphFile.lineStartX := 0;
    aGraphFile.curr_x := 0;
    aGraphFile.curr_y := 11;
  end func;


(**
 *  Creates a ''graph_file'' at (minX, minY) in ''graphWin''.
 *  The ''graph_file'' extends to the lower right edge of ''graphWin''.
 *  @return the file opened.
 *)
const func graph_file: open (in PRIMITIVE_WINDOW: graphWin,
    in integer: minX, in integer: minY) is func
  result
    var graph_file: aGraphFile is graph_file.value;
  begin
    aGraphFile.win := graphWin;
    aGraphFile.line_delta := 13;
    aGraphFile.column_delta := 6;
    aGraphFile.height := (height(graphWin) - minY) div aGraphFile.line_delta;
    aGraphFile.width := (width(graphWin) - minX) div aGraphFile.column_delta;
    aGraphFile.line := 1;
    aGraphFile.column := 1;
    aGraphFile.min_x := minX;
    aGraphFile.min_y := minY;
    aGraphFile.lineStartX := minX;
    aGraphFile.curr_x := minX;
    aGraphFile.curr_y := minY + 11;
  end func;


(**
 *  Creates a ''graph_file'' at (minX, minY) in ''graphWin''.
 *  The ''graph_file'' is created with the given ''width'' and ''height'.
 *  @return the file opened.
 *)
const func graph_file: open (in PRIMITIVE_WINDOW: graphWin,
    in integer: minX, in integer: minY, in integer: width, in integer: height) is func
  result
    var graph_file: aGraphFile is graph_file.value;
  begin
    aGraphFile.win := graphWin;
    aGraphFile.line_delta := 13;
    aGraphFile.column_delta := 6;
    aGraphFile.height := height div aGraphFile.line_delta;
    aGraphFile.width := width div aGraphFile.column_delta;
    aGraphFile.line := 1;
    aGraphFile.column := 1;
    aGraphFile.min_x := minX;
    aGraphFile.min_y := minY;
    aGraphFile.lineStartX := minX;
    aGraphFile.curr_x := minX;
    aGraphFile.curr_y := minY + 11;
  end func;


const func graph_file: open (in PRIMITIVE_WINDOW: graphWin, in integer: lin_delta) is func
  result
    var graph_file: aGraphFile is graph_file.value;
  begin
    aGraphFile.win := graphWin;
    aGraphFile.line_delta := lin_delta;
    aGraphFile.column_delta := 6;
    aGraphFile.height := height(graphWin) div aGraphFile.line_delta;
    aGraphFile.width := width(graphWin) div aGraphFile.column_delta;
    aGraphFile.line := 1;
    aGraphFile.column := 1;
    aGraphFile.min_x := 0;
    aGraphFile.min_y := 0;
    aGraphFile.lineStartX := 0;
    aGraphFile.curr_x := 0;
    aGraphFile.curr_y := 11;
  end func;


(**
 *  Close an graph_file.
 *)
const proc: close (inout graph_file: aGraphFile) is func
  begin
    aGraphFile.win := PRIMITIVE_WINDOW.value;
  end func;


(**
 *  Forces that all buffered data of ''aFile'' is sent to its destination.
 *  This causes data to be sent to the graphic system of the OS.
 *)
const proc: flush (in graph_file: aGraphFile) is func
  begin
    flushGraphic;
  end func;


(**
 *  Set the foreground color of ''aGraphFile''.
 *)
const proc: color (inout graph_file: aGraphFile, in color: col) is func
  begin
    aGraphFile.foreground := col;
  end func;


(**
 *  Set the foreground and background color of ''aGraphFile''.
 *)
const proc: color (inout graph_file: aGraphFile, in color: col1, in color: col2) is func
  begin
    aGraphFile.foreground := col1;
    aGraphFile.background := col2;
  end func;


(**
 *  Get the height of ''aGraphFile''.
 *  @return the height of ''aGraphFile''.
 *)
const func integer: height (in graph_file: aGraphFile) is
  return aGraphFile.height;


(**
 *  Get the width of ''aGraphFile''.
 *  @return the width of ''aGraphFile''.
 *)
const func integer: width (in graph_file: aGraphFile) is
  return aGraphFile.width;


(**
 *  Get the current line of ''aGraphFile''.
 *  @return the current line of ''aGraphFile''.
 *)
const func integer: line (in graph_file: aGraphFile) is
  return aGraphFile.line;


(**
 *  Get the current column of ''aGraphFile''.
 *  @return the current column of ''aGraphFile''.
 *)
const func integer: column (in graph_file: aGraphFile) is
  return aGraphFile.column;


(**
 *  Clear an area of ''aGraphFile'' with the background color.
 *  The area is specified in (line, column) coordinates and is
 *  between the (''upper'', ''left'') and (''lower'', ''right'').
 *)
const proc: clear (inout graph_file: aGraphFile,
    in integer: upper, in integer: left, in integer: lower, in integer: right) is func
  begin
    rectTo(aGraphFile.win,
        aGraphFile.min_x + aGraphFile.column_delta * pred(left),
        aGraphFile.min_y + aGraphFile.line_delta * pred(upper),
        aGraphFile.min_x + pred(aGraphFile.column_delta * right),
        aGraphFile.min_y + pred(aGraphFile.line_delta * lower),
        aGraphFile.background);
    aGraphFile.line := upper;
    aGraphFile.column := left;
    aGraphFile.lineStartX := aGraphFile.min_x;
    aGraphFile.curr_x := aGraphFile.min_x + aGraphFile.column_delta * left - 6;
    aGraphFile.curr_y := aGraphFile.min_y + aGraphFile.line_delta * upper - 2;
  end func;


(**
 *  Clear the area of ''aGraphFile'' with the background color.
 *)
const proc: clear (inout graph_file: aGraphFile) is func
  begin
    clear(aGraphFile, 1, 1, height(aGraphFile), width(aGraphFile));
  end func;


const proc: cursor (ref graph_file: aGraphFile, ref boolean: active) is noop;


const proc: v_scroll (inout graph_file: aGraphFile,
    in integer: upper, in integer: left, in integer: lower, in integer: right,
    in integer: count) is func
  begin
    if count > 0 then
      copyArea(aGraphFile.win, aGraphFile.win,
          aGraphFile.min_x + aGraphFile.column_delta * pred(left),
          aGraphFile.min_y + aGraphFile.line_delta * pred(upper + count),
          aGraphFile.column_delta * succ(right - left),
          aGraphFile.line_delta * succ(lower - upper - count),
          aGraphFile.min_x + aGraphFile.column_delta * pred(left),
          aGraphFile.min_y + aGraphFile.line_delta * pred(upper));
      rect(aGraphFile.win,
          aGraphFile.min_x + aGraphFile.column_delta * pred(left),
          aGraphFile.min_y + aGraphFile.line_delta * (lower - count),
          aGraphFile.column_delta * succ(right - left),
          aGraphFile.line_delta * count,
          black);
    elsif count < 0 then
      copyArea(aGraphFile.win, aGraphFile.win,
          aGraphFile.min_x + aGraphFile.column_delta * pred(left),
          aGraphFile.min_y + aGraphFile.line_delta * pred(upper),
          aGraphFile.column_delta * succ(right - left),
          aGraphFile.line_delta * succ(lower - upper + count),
          aGraphFile.min_x + aGraphFile.column_delta * pred(left),
          aGraphFile.min_y + aGraphFile.line_delta * pred(upper - count));
    end if;
  end func;


const proc: h_scroll (ref graph_file: aGraphFile,
    in integer: upper, in integer: left, in integer: lower, in integer: right,
    in integer: count) is func
  begin
    noop;
  end func;


(**
 *  Set the current position of ''aGraphFile'' to ''line'' and ''column''.
 *)
const proc: setPos (inout graph_file: aGraphFile, in integer: line, in integer: column) is func
  begin
    aGraphFile.line := line;
    aGraphFile.column := column;
    aGraphFile.lineStartX := aGraphFile.min_x;
    aGraphFile.curr_x := aGraphFile.min_x + aGraphFile.column_delta * column - 6;
    aGraphFile.curr_y := aGraphFile.min_y + aGraphFile.line_delta * line - 2;
  end func;


(**
 *  Set the current position of ''aGraphFile'' to the coordinates (xPos, yPos).
 *  The coordinates are from the graphic window which belongs to ''aGraphFile''.
 *)
const proc: setPosXY (inout graph_file: aGraphFile, in integer: xPos, in integer: yPos) is func
  begin
    aGraphFile.lineStartX := aGraphFile.min_x + xPos;
    aGraphFile.curr_x := aGraphFile.min_x + xPos;
    aGraphFile.curr_y := aGraphFile.min_y + yPos;
    aGraphFile.line := (aGraphFile.curr_y + 2) div aGraphFile.line_delta;
    aGraphFile.column := (aGraphFile.curr_x + 6) div aGraphFile.column_delta;
  end func;


(**
 *  Set the ''line'' of the current position of ''aGraphFile''.
 *)
const proc: setLine (inout graph_file: aGraphFile, in integer: line) is func
  begin
    aGraphFile.line := line;
    aGraphFile.curr_y := aGraphFile.min_y + aGraphFile.line_delta * line - 2;
  end func;


(**
 *  Set the ''column'' of the current position of ''aGraphFile''.
 *)
const proc: setColumn (inout graph_file: aGraphFile, in integer: column) is func
  begin
    aGraphFile.column := column;
    aGraphFile.curr_x := aGraphFile.min_x + aGraphFile.column_delta * column - 6;
  end func;


(**
 *  Write a string to the current position of ''aGraphFile''.
 *)
const proc: write (inout graph_file: aGraphFile, in string: stri) is func
  begin
    DRAW_TEXT(aGraphFile.win, aGraphFile.curr_x, aGraphFile.curr_y, stri,
        colorPixel(aGraphFile.foreground), colorPixel(aGraphFile.background));
    aGraphFile.column +:= length(stri);
    aGraphFile.curr_x +:= aGraphFile.column_delta * length(stri);
  end func;


(**
 *  Write end-of-line to ''aGraphFile''.
 *  Set the current position to the beginning of the next line.
 *)
const proc: writeln (inout graph_file: aGraphFile) is func
  begin
    incr(aGraphFile.line);
    aGraphFile.column := 1;
    aGraphFile.curr_x := aGraphFile.lineStartX;
    # aGraphFile.curr_x := aGraphFile.min_x + aGraphFile.column_delta - 6;
    aGraphFile.curr_y +:= aGraphFile.line_delta;
  end func;


const proc: moveLeft (inout graph_file: aGraphFile, in string: stri) is func
  begin
    aGraphFile.column -:= length(stri);
    aGraphFile.curr_x -:= aGraphFile.column_delta * length(stri);
  end func;


const proc: erase (inout graph_file: aGraphFile, in string: stri) is func
  begin
    DRAW_TEXT(aGraphFile.win, aGraphFile.curr_x, aGraphFile.curr_y, "" lpad length(stri),
        colorPixel(aGraphFile.foreground), colorPixel(aGraphFile.background));
    aGraphFile.column +:= length(stri);
    aGraphFile.curr_x +:= aGraphFile.column_delta * length(stri);
  end func;


const proc: cursorOn (inout graph_file: aGraphFile, in char: cursorChar) is func
  begin
    DRAW_TEXT(aGraphFile.win, aGraphFile.curr_x, aGraphFile.curr_y, str(cursorChar),
        colorPixel(aGraphFile.background), colorPixel(aGraphFile.foreground));
  end func;


const proc: cursorOff (inout graph_file: aGraphFile, in char: cursorChar) is func
  begin
    DRAW_TEXT(aGraphFile.win, aGraphFile.curr_x, aGraphFile.curr_y, str(cursorChar),
        colorPixel(aGraphFile.foreground), colorPixel(aGraphFile.background));
  end func;


const proc: box (ref graph_file: aGraphFile) is noop;
const proc: clear_box (ref graph_file: aGraphFile) is noop;