// screen.cpp - implementation for Screen and Point classes.
// Written by (c) Wayne Pollock, 2000.  All Rights Reserved.

#include <iostream>
#include <iomanip>
#include <cstdlib>   // For older compilers use: <stdlib.h>
#include "screen.h"

using namespace std;

//=========================================================================
//                      Point Class implementation:
//=========================================================================

Point Point::operator+ ( const Point& p ) const
{   Point t( row, col );
    t.row += p.row;
    t.col += p.col;
    return t;
}

int Point::operator== ( const Point& p ) const
{   return ( p.row == row && p.col == col );
}

//=========================================================================
//                      Screen class implementation:
//=========================================================================

// In this implementation, the grid of characters is represented as a
// two dimentional array.
// Any value can be used to draw (or erase) the screen, even ' ' (blank).
// You can draw with ' ' to selectively erase parts of the image.
// Note the origin is in the upper left-hand corner.

Screen::Screen ()
{
    ink = INK_DEFAULT;
    current_pt.row = 0;
    current_pt.col = 0;
}

Screen::Screen ( const Screen& s )   
{
    ink = s.ink;
    current_pt = s.current_pt;
    for ( int r=0; r<NUM_ROWS; ++r )
	for ( int c=0; c<NUM_COLS; ++c )
	    rep[r][c] = s.rep[r][c];
}

Screen& Screen::operator= ( const Screen& s )
{   ink = s.ink;
    current_pt = s.current_pt;
    for ( int r=0; r<NUM_ROWS; ++r )
	for ( int c=0; c<NUM_COLS; ++c )
	    rep[r][c] = s.rep[r][c];
    return *this;
}

// Note if "ink" is zero (the default), plot uses the Screen::ink character.
void Screen::plot ( const Point& p, char new_ink )	// new_ink has default value.
{
    if ( p.row < NUM_ROWS && p.col < NUM_COLS && p.row >= 0 && p.col >= 0 )
    {	if ( ! new_ink )
	    new_ink = ink;
	rep[p.row][p.col] = new_ink;
	current_pt.row = p.row;  // Update the current point.
	current_pt.col = p.col;
    }
    return;
}

void Screen::move_to ( const Point& p )  // never move the point off-screen!
{
    if ( p.row < NUM_ROWS && p.col < NUM_COLS && p.row >= 0 && p.col >= 0 )
	current_pt = p;
    return;
}

void Screen::clear ( char erase_char )	// erase_char has a default value.
{
    for ( int r=0; r<NUM_ROWS; ++r )
	for ( int c=0; c<NUM_COLS; ++c )
	    rep[r][c] = erase_char;
    return;	// Note that old Current Point is not reset here.
}

void Screen::display ( ostream& out ) const	// out has a default value.
{
    out << "\n\n\n" << endl;
    out << "   ";  // offset the column numbers by the width of row numbers.
    for ( int c=0; c<NUM_COLS; ++c )
	out << ( c % 10 );             // print column numbers.
    out << endl;
    for ( int r=0; r<NUM_ROWS; ++r )
    {	out << setw( 2 ) << r << " ";  // print row numbers.
	out.write( rep[r], NUM_COLS ); // print out one whole row.
	out << endl;
    }
    return;
}

Point Screen::size () const
{
    Point p;
    p.row = NUM_ROWS;
    p.col = NUM_COLS;
    return p;
}

void Screen::draw_line ( const Point& start_pt, const Point& end_pt, char ink )
{
    if (start_pt == end_pt)      //  i.e., a one point line.
    {	plot( start_pt, ink );
	return;
    }
    int len_r = end_pt.row - start_pt.row;
    int len_c = end_pt.col - start_pt.col;
    int len = abs( len_c );
    if ( abs( len_r ) > abs( len_c ) )
	len = abs( len_r );
    double r_inc = len_r / double( len );
    double c_inc = len_c / double( len );
    double r = start_pt.row + 0.5;
    double c = start_pt.col + 0.5;
    for ( int i=0; i<=len; ++i )
    {	plot( Point( int( r ), int( c ) ), ink );
	r += r_inc;
	c += c_inc;
    }
    return;
}


void Screen::draw_circle ( const Point& center, int radius, char ink )
{
    int r = radius;
    int c = 0;
    int decision = 1 - r;
    circle_points( Point( r, c ), center, ink );
    while ( r > c )
    {	if ( decision < 0 )
	{   decision += 2 * c + 3;
	    ++c;
	} else {
	    decision += 2 * ( c - r ) + 5;
	    ++c;   --r;
	}
	circle_points( Point( r, c ), center, ink );
    }
    move_to( center );	// Reset the current point to the center.
    return;
}


void Screen::circle_points ( const Point& p, const Point& c, char ink )
{
    plot( Point(  p.row+c.row,  p.col+c.col ), ink );
    plot( Point(  p.row+c.row, -p.col+c.col ), ink );
    plot( Point( -p.row+c.row,  p.col+c.col ), ink );
    plot( Point( -p.row+c.row, -p.col+c.col ), ink );
    plot( Point(  p.col+c.row,  p.row+c.col ), ink );
    plot( Point(  p.col+c.row, -p.row+c.col ), ink );
    plot( Point( -p.col+c.row,  p.row+c.col ), ink );
    plot( Point( -p.col+c.row, -p.row+c.col ), ink );
    return;
}
