#include <stdlib.h> // abs()

#include "screen.h"
#include "line.h"
#include "figure.h"
#include "notice.h"

// prototyping

static void fswap( int&, int& );

//
// Class Line
//
Line::Line( Screen* newScreen, int x1, int y1, int x2, int y2 )
  : GObject( newScreen, x1, y1 )
{
  set( x1, y1, x2, y2 );
  marked = 0;
  savedBackground = 0;
}

Line::Line( Line& line ) // Copyconstructor
  : GObject( line.screen, 0, 0 )
{
  set( line.x, line.y, line.x+line.cDx, line.y+line.cDy );
  color = line.color;
  savedBackground = line.savedBackground;
  if( savedBackground)
  {
    for( int i=0; i<MAXLINE; i++ )
      buffer[i] = line.buffer[i];
  }
  marked = line.marked;
}

Line& Line::operator= ( Line& line )
{
  set( line.x, line.y, line.x+line.cDx, line.y+line.cDy );
  setColor( line.color );
  screen = line.screen;
  savedBackground = line.savedBackground;
  if( savedBackground)
  {
    for( int i=0; i<MAXLINE; i++ )
      buffer[i] = line.buffer[i];
  }
  marked = line.marked;
  return *this;
}

Line::~Line()
{
}

void Line::draw()
{
  screen->mouseOff();
  set( x, y, x+cDx, y+cDy );
  saveBackground();
  DrawFunction func( this );
  bresenham( func );
  screen->mouseOn();
}

void Line::draw( int x1, int y1, int x2, int y2 )
{
  set( x1, y1, x2, y2 );
  draw();
}

void Line::restoreBackground()
{
  if( !savedBackground )
    return;

  int bx = x;
  int by = y;
  x = cOldX;
  y = cOldY;
  RestoreFunction func( this );
  bresenham( func );
  x = bx;
  y = by;
}

void Line::saveBackground()
{
  cOldX = x;
  cOldY = y;
  SaveFunction func( this );
  bresenham( func );
  savedBackground = 1;
}

ZeigerListe<Line>* Line::split( void )
{
  SplitLineFunction func( this );
  bresenham( func );
  return func.list;
}

//
// setzt Anfang und Endpunkte der Linie
//
void Line::set(int x1, int y1, int x2, int y2)
{
  x = x1;
  y = y1;
  if( x2 != -1 )
  {
    cDx = x2-x1;
  }
  if( y2 != -1 )
  {
    cDy = y2-y1;
  }
  iLX1 = x;
  iLY1 = y;
  iLX2 = x+cDx;
  iLY2 = y+cDy;
}

Bool Line::isOver( int x, int y )
{
  IsOverFunction func( x, y );
  bresenham( func );
  return func.over;
}

//void Line::doFunction( Function& func )
//{
  //bresenham( func );
//}

////////////////
// drawline() //
////////////////
//
// benutzt den Bresenham algorithmus
//
//void Line::bresenham( void (*function) ( int x, int y ) )
void Line::bresenham( Function& func, int iX1, int iY1, int iX2, int iY2 )
{
  if( iX1 == -1 ) iX1 = iLX1;
  if( iY1 == -1 ) iY1 = iLY1;
  if( iX2 == -1 ) iX2 = iLX2;
  if( iY2 == -1 ) iY2 = iLY2;

  signed int dx=0,dy=0,err=0,inc=0;
/**************************/
/* Do straight line cases */
/**************************/
 if(iX1==iX2)
 {
  if(iY1>iY2)
  {
   fswap(iY1, iY2); // vertausch die beiden werte
  }
  do
  {
   /******************************************************************************************/
   /* not to be put out of loop, offset can get to high... (high iY1 are pointing to maxline)*/
   /******************************************************************************************/
   func.function( iX1, iY1 );
  }
  while(iY1++!=iY2);
 }
 else if(iY1==iY2)
 {
  if(iX1>iX2)
  {
   fswap(iX1,iX2);
  }
  do
  {
   func.function( iX1, iY1 );
  }
  while(iX1++!=iX2);
 }
 else
 {
  /*************/
  /* X Variant */
  /*************/
  dx=iX2-iX1;
  dy=iY2-iY1;
  if(abs(dx)>=abs(dy))
  {
   if(iX1>iX2)
   {
    fswap(iX1,iX2);
    fswap(iY1,iY2);
   }
   dx=iX2-iX1;
   dy=iY2-iY1;
   err=-(dx>>1);
   if(dy>0)
   {
    inc=1;
   }
   else
   {
    inc=-1;
    dy=-dy;
   }
   do
   {
    /**************************/
    /* Calulate byte position */
    /**************************/
    func.function( iX1, iY1 );
    /**********************/
    /* Check error status */
    /**********************/
    err+=dy;
    if(err>0)
    {
     err-=dx;
     iY1+=inc;
    }
   }
   while(iX1++!=iX2);
  }
  else
  {
   if(iY1>iY2)
   {
    fswap(iX1,iX2);
    fswap(iY1,iY2);
   }
   dx=iX2-iX1;
   dy=iY2-iY1;
   err=-(dy>>1);
   if(dx>0)
   {
    inc=1;
   }
   else
   {
    inc=-1;
    dx=-dx;
   }
   do
   {
    /**************************/
    /* Calulate byte position */
    /**************************/
    func.function( iX1, iY1 );
    /**********************/
    /* Check error status */
    /**********************/
    err+=dx;
    if(err>=0)
    {
     err-=dy;
     iX1+=inc;
    }
   }
   while(iY1++!=iY2);
  }
 }
}

//////////////////////////////////
//
// class MLine
//
void MLine::draw()
{
  Line::draw();
  drawPoint( x,y, screen->Red() );
  drawPoint( x+cDx, y+cDy , screen->Red() );
}

////////////////////////////////

void fswap( int& x, int& y)
{
  int h;
  h   = x;
  x = y;
  y = h;
}


void DrawFunction::function( int x, int y )
{
  line->drawPoint( x, y );
}

void SaveFunction::function( int x, int y )
{
  line->buffer[i++] = line->screen->getPoint( x, y );
}

void RestoreFunction::function( int x, int y )
{
  line->drawPoint( x, y, line->buffer[i++] );
}

void IsOverFunction::function( int x, int y )
{
  const DX = 3;
  const DY = 3;
  //if(( x == fx ) && ( y == fy ))
  if( (abs(x - fx) < DX) && (abs( y - fy) < DY) )
    over = 1;
}

int makeX( int i )
{
  int dx = 10;
  return ((i-100+dx/2)/dx);
}
int makeY( int i )
{
  int dy = 10;
  return ((i+dy/2)/dy);
}

SplitLineFunction::SplitLineFunction( Line* nline )
{
  oldX = nline->getX();
  oldY = nline->getY();
  startX = oldX;
  startY = oldY;
  list = new ZeigerListe<Line>;

  list->append( nline ); // erstes Element der Liste ist die original Zeile
}
#define DX 10
#define DY 10
void SplitLineFunction::function( int x, int y )
{
  Line* myLine = list->first();
  if( ( makeY(y) != makeY(oldY) ) || ( abs(makeX(x) - makeX(startX)) >= DX ) ||
      ( ( myLine->getX1() == x ) && ( myLine->getY1() == y ) ) )
  {
    MLine* line = new MLine( myLine->screen, makeX(startX)*DX+100, makeY(startY)*DY, makeX(oldX)*DX+100, makeY(oldY)*DY );
    list->append( line );
    startX = oldX;
    startY = oldY;
  }
  oldX = x;
  oldY = y;
}

void Line::mirror()
{
  x = x + cDx;
  y = y + cDy;
  cDx = -cDx;
  cDy = -cDy;
}

int Line::North( void )
{
  if( getY() < getY1() )
    return getY();
  return getY1();
}

int Line::South( void )
{
  if( getY() < getY1() )
    return getY1();
  return getY();
}

int Line::West( void )
{
  if( getX() < getX1() )
    return getX();
  return getX1();
}

int Line::East( void )
{
  if( getX() < getX1() )
    return getX1();
  return getX();
}
