#include <stdio.h>   // sprintf()
#include <string.h>  // strstr()
#include <ctype.h>   // isdigit()
#include <fstream.h> // ofstream
#include <stdlib.h>  // abs()

#include "graphic.h"
#include "line.h"
#include "notice.h"
#include "figure.h"
#include "cpos.h" // class CPosition, Positionklasse mit Zaehler
#include "point.h"

int Figure::number = 0;

//
// class Figure
//
Figure::Figure( Screen *newScreen )
  : GObject( newScreen, 0, 0 ), Graph()
{
  char s[200];
  sprintf( s, "Figure%i", number++ );
  setName( s );
  dx = 1;
  dy = 1;
  startPosition.set( -1, -1 );
  lineOver = NULL;
  setBlowup( "" );
  savedBackground = 0;
}

Figure::Figure( Figure& figure ) // Copyconstructor
  : GObject( figure.screen, figure.getX(), figure.getY() )
{
  char s[200];
  sprintf( s, "Figure%i", number++ );
  setName( s );
  if( !figure.lineList.isEmpty() )
  {
    figure.lineList.first();
    do
    {
      Line* l = figure.lineList.retrieve();
      Line* nl = new MLine(*l) ;
      lineList.append( nl );
    }while( figure.lineList.next() != NULL );
  }
  setScale( figure.dx, figure.dy );
  setStartPos( figure.startPosition );
  setColor( figure.color );
  setBlowup( figure.blowup );
  lineOver = figure.lineOver;
}

Figure& Figure::operator=( Figure& figure )
{
  lineList.emptyWithElementDelete();
  if( !figure.lineList.isEmpty() )
  {
    figure.lineList.first();
    do
    {
      Line* l = figure.lineList.retrieve();
      Line* nl = new MLine(*l) ;
      lineList.append( nl );
    }while( figure.lineList.next() != NULL );
  }
  Position::set( figure.getX(), figure.getY() );
  setScale( figure.dx, figure.dy );
  setStartPos( figure.startPosition );
  screen = figure.screen;
  setColor( figure.color );
  x = figure.x;
  y = figure.y;
  setBlowup( figure.blowup );
  lineOver = figure.lineOver;
  return *this;
}

Figure::~Figure()
{
}

void Figure::draw()
{
  screen->mouseOff();
  saveBackground();
  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    lineList.retrieve()->draw();
  }
  screen->mouseOn();
}

void Figure::draw1()
{
  screen->mouseOff();
  saveBackground();
  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    Line* l = lineList.retrieve();
    l->Line::draw();
  }
  screen->mouseOn();
}
void Figure::move( int dx, int dy )
{
  restoreBackground();
  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    Line* line = lineList.retrieve();
    line->set( line->getX()+dx, line->getY()+dy );
    line->draw();
  }
  Line* line = lineList.first();
  Position::set( line->getX(), line->getY() );
  startPosition.set( line->getX(), line->getY() );
}


void Figure::moveTo( int newX, int newY )
{
  move( newX - getX(), newY - getY() );
}

void Figure::saveBackground()
{
  for( lineList.first(); !lineList.isLast(); lineList.next() )
    lineList.retrieve()->saveBackground();
  savedBackground = 1;
}

void Figure::restoreBackground()
{
  if( !savedBackground )
    return;
  lineList.last();
  while( !lineList.isFirst() )
  {
    lineList.previous()->restoreBackground();
  }
}

void Figure::set(int x0, int y0 )
{
  int dx = x0-getX();
  int dy = y0-getY();
  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    Line* line = lineList.retrieve();
    line->set( line->getX()+dx, line->getY()+dy );
  }

  Position::set( x0, y0 );
}


Bool Figure::isOver( int x, int y )
{
  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    if( lineList.retrieve()->isOver( x, y ) )
    {
      lineOver = lineList.retrieve();
      return 1;
    }
  }
  lineOver = NULL;
  return 0;
}

Line* Figure::isOverLine( int x, int y )
{
  isOver( x, y );
  return lineOver;
}

void Figure::setColor( Color ncolor )
{
  GObject::setColor( ncolor );
  for( lineList.first(); !lineList.isLast(); lineList.next() )
    lineList.retrieve()->setColor( color );
}

int Figure::save( ofstream& out )
{
  char* saveString = "                DB %2i%s%s, %2i%s%s\n";
  char s[ 200 ];
  Line* line = NULL;
  int x = 0, y = 0;
  int oldX, oldY;
  int sdx, sdy;
  int sx, sy;  // scaled x, y

  if( !out ) // Dateifehler
    return 1;

  if( isEuler() == NICHTZUSAMMENHAENGEND )
  {
    sprintf(s, "%s Figur nicht speicherbar!", getName() );
    Notice n( screen, s );
    n.start();
    return 1;
  }

  if( sort( startPosition ) != 0 ) // kann nicht zusammenhaengend abgespeichert werden
  {
    sprintf(s, "%s vom Startpunkt aus nicht speicherbar!", getName() );
    Notice n( screen, s );
    n.start();
    return 1;
  }
  line = lineList.first();
  Position::set( line->getX(), line->getY() );
  startPosition.set( line->getX(), line->getY() );

  out << "; sprite definition\n";
  out << getName() << ":\n";
  // erstmal Anzahl der Linien speichern
  out << "                DB " << lineList.getSize()-1 << "\n";

  ///line = lineList.first();
  oldX = line->getX();
  oldY = line->getY();

  while( line!= NULL )
  {
    x = line->getX1();
    y = line->getY1();
    sdx = x-oldX;
    sdy = y-oldY;
    oldX = x;
    oldY = y;
    sx = sdx/dx;
    sy = sdy/dy;

    sprintf( s, saveString, -sy, *blowup ? "*" : "" , *blowup ? blowup : "" , sx, *blowup ? "*" : "", *blowup ? blowup : "" );

    out << s;

    line = lineList.next();
  }
  return 0;
}

int Figure::save( char* newName )
{
  ofstream* out = new ofstream(newName);
  int ret = save( *out );
  delete out;
  return ret;
}

int  Figure::North( void )
{
  Position cp = _findStartPosition( REALUPPER );
  north = cp.getY();
  return north;
}
int  Figure::South( void )
{
  Position cp = _findStartPosition( REALLOWER );
  south = cp.getY();
  return south;
}
int  Figure::West( void )
{
  Position cp = _findStartPosition( REALLEFT );
  west = cp.getX();
  return west;
}
int  Figure::East( void )
{
  Position cp = _findStartPosition( REALRIGHT );
  east = cp.getX();
  return east;
}

Position Figure::_findStartPosition( int p )
{
  CPosition cp;
  switch( p )
  {
    case UPPER:  // oben und unten vertauscht ??
    case REALUPPER:  // oben und unten vertauscht ??
    {
      cp.set( 0, 1000 );
      break;
    }
    case LOWER:
    case REALLOWER:
    {
      cp.set( 0, 0 );
      break;
    }
    case RIGHT:
    case REALRIGHT:
    {
      cp.set( 0, 0 );
      break;
    }
    case LEFT:
    case REALLEFT:
    {
      cp.set( 1000, 0 );
      break;
    }
    default:
    {
      cp.set( -1, -1 );
      return cp;
    }
  }

  if( isEuler() == EULERWEG && p!=REALUPPER && p!=REALLEFT && p!=REALRIGHT && p!=REALLOWER )
  {
    CPosition cp1;
    CPosition cp2;
    _makePositionList();
    for( cpList.first(); !cpList.isLast(); cpList.next() )
    {
      cp1 = *cpList.retrieve();
      if( ( cp1.getCount() % 2 ) != 0 )
        break;
    }
    cpList.next();
    for( ; !cpList.isLast(); cpList.next() )
    {
      cp2 = *cpList.retrieve();
      if( ( cp2.getCount() % 2 ) != 0 )
        break;
    }

    switch( p )
    {
      case UPPER:
        if( cp1.getY() > cp2.getY() )
          cp = cp2;
        else
          cp = cp1;
        break;
      case LOWER:
        if( cp1.getY() < cp2.getY() )
          cp = cp2;
        else
          cp = cp1;
        break;
      case RIGHT:
        if( cp1.getX() < cp2.getX() )
          cp = cp2;
        else
          cp = cp1;
        break;
      case LEFT:
        if( cp1.getX() > cp2.getX() )
          cp = cp2;
        else
          cp = cp1;
        break;
    }
    Position::set( cp.getX(), cp.getY() );
    return cp;
  }

  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    Line *l = lineList.retrieve();
    switch( p )
    {
      case UPPER:
      case REALUPPER:
      {
        if( cp.getY() > l->getY() )
          cp.set( l->getX(), l->getY() );
        if( cp.getY() > l->getY1() )
          cp.set( l->getX1(), l->getY1() );
        break;
      }
      case LOWER:
      case REALLOWER:
      {
        if( cp.getY() < l->getY() )
          cp.set( l->getX(), l->getY() );
        if( cp.getY() < l->getY1() )
          cp.set( l->getX1(), l->getY1() );
        break;
      }
      case RIGHT:
      case REALRIGHT:
      {
        if( cp.getX() < l->getX() )
        {
          cp.set( l->getX(), l->getY() );
        }
        if( cp.getX() < l->getX1() )
        {
          cp.set( l->getX1(), l->getY1() );
        }
        break;
      }
      case LEFT:
      case REALLEFT:
      {
        if( cp.getX() > l->getX() )
          cp.set( l->getX(), l->getY() );
        if( cp.getX() > l->getX1() )
          cp.set( l->getX1(), l->getY1() );
        break;
      }
    }
  }
  //Position::set( cp.getX(), cp.getY() );
  return cp;
}


inline char* getDB( char* line );

int Figure::load( ifstream& in )
{
  const MAXLINE=200;
  char line[MAXLINE];
  int  pos = 0;
  int  lineCounter = 0;
  int  sdx=0, sdy=0;
  int  x0=0, y0=0;
  int  bDy = 1;  // soll jetzt dx oder dy gelesen werden
  MLine* newLine;

  if( !in ) // Dateifehler
    return DATEIFEHLER;

  restoreBackground();
  clearLines();
  // erst Spritekennung suchen
  while( in.getline( line, MAXLINE ) )
  {
    if( strstr( line, "sprite definition" ) )
      break;
  }
  if( in.eof() ) // am Ende der Datei angelangt und keine sprite def. gefunden
  {
    return KEINESPRITEKENNUNG;
  }
  // Spritename suchen
  while( in.getline( line, MAXLINE ) )
  {
    if( strchr( line, ':' ) )
      break;
  }
  if( in.eof() ) // am Ende der Datei angelangt und keinen Spritenamen gefunden
  {
    return KEINENSPRITENAMEN;
  }
  *strchr( line, ':' ) = 0;
  setName( line );

  // erste DB-Zeile suchen
  while( in.getline( line, MAXLINE ) )
  {
    if( getDB( line ) )
      break;
  }
  if( in.eof() ) // am Ende der Datei angelangt und keine DB-Zeile gefunden
    return KEINEDBKENNUNG;

  // Linienanzahl lesen
  do
  {
    pos = getNextIntPos( line, 0 );
    if( pos != -1 )
      break;
  }while( in.getline( line, MAXLINE ) );
  if( in.eof() ) // am Ende der Datei angelangt und keine Linienanzahl gefunden
    return KEINELINIENANZAHL;

  sscanf( line+pos, "%i", &lineCounter );
  lineCounter++; // lineCounter immer Linien - 1!

  int blow = 0;    // 0 blowup noch nicht gelesen , 1 sonst
  // lineCounter * (-dy, dx) - Paare aus Datei lesen
  do
  {
    // nicht DB Zeilen einfach ueberlesen
    if( getDB( line ) == NULL )
      continue;
    // (-dy, dx) - Paare aus Zeile lesen
    while(1)
    {
      // -d? - Werte aus Zeile lesen
      pos = getNextIntPos( line, pos );
      if( pos == -1 )
        break;
      if( pos != -1 )
      {
        if( bDy )
        {
          sscanf( line+pos, "%i", &sdy );
        }
        else
        {
          sscanf( line+pos, "%i", &sdx );
        }
        if( !blow )
        {
          getBlowup( line );
          blow = 1;
        }
        if( !bDy )
        {
          newLine = new MLine( screen, x0, y0, x0+sdx*dx, y0-sdy*dy );
          append(newLine);
          x0 += sdx*dx;
          y0 -= sdy*dy;
        }
        bDy = !bDy;
      }
      else
        break;
    }
    pos = 0;
    if( lineCounter-- == 0 )
      break;
  }while( in.getline( line, MAXLINE ) );
  // Figur ist eingelesen

  Position lcp = _findStartPosition( REALLEFT );
  Position ucp = _findStartPosition( REALUPPER );
  int dx = lcp.getX();
  int dy = ucp.getY();

  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    Line* l = lineList.retrieve();
#ifdef __TOOLLIB__
    l->set( l->getX()-dx+200, l->getY()-dy );
#endif
#ifdef __SVGALIB__
    l->set( l->getX()-dx+100, l->getY()-dy );
#endif
  }

  Line* l = lineList.first();
  Position::set(  l->getX(), l->getY() );
  startPosition.set( l->getX(), l->getY() );
  return LOAD_OK;
}

void Figure::remove( Line* nl )
{
  Graph::remove( nl );
  if( !lineList.isEmpty() )
  {
    Line* l = lineList.first();
    Position::set( l->getX(), l->getY() ); // eventuell neue Startposition
  }
  else
    Position::set(0,0);
}

void Figure::append( Line* newLine )
{
  if( lineList.isEmpty() )
    Position::set( newLine->getX(), newLine->getY() );

  Graph::append( newLine );
}

int  Figure::sort( Position startPos )
{
  if( startPos.getX() == -1 )
  {
    startPos = _findStartPosition( LEFT );
  }

  int ret = Graph::sort( startPos );
  if( ret == 0 )
  {
    Line* l = lineList.first();
    Position::set( l->getX(), l->getY() );
    startPosition.set( l->getX(), l->getY() );
  }
  return ret;
}

void Figure::clearLines()
{
  lineList.emptyWithElementDelete();
}

void Figure::setScale( int newDx, int newDy )
{
  dx = newDx;
  dy = newDy;
}

void Figure::scale( int newDx, int newDy )
{
  Line* l;
  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    l = lineList.retrieve();
    int oldX = l->getX();
    int oldY = l->getY();
    int oldX1 = l->getX1();
    int oldY1 = l->getY1();
    // verkleinern
    l->set( oldX*newDx/dx, oldY*newDy/dy, oldX1*newDx/dx, oldY1*newDy/dy );
  }
  if( !lineList.isEmpty() )
  {
    l = lineList.first();
    Position::set( l->getX(), l->getY() );
  }
  setScale( newDx, newDy );
}

int Figure::getSize()
{
  lineList.last();
  return lineList.getPos();
}

char* Figure::getName()
{
  return name;
}

void  Figure::setName( char* newName )
{
  strcpy( name, newName );
}

int Figure::setStartPos( int p )
{
  startPosition = _findStartPosition( p );
  return sort( startPosition );
}

int Figure::setStartPos( Position& p )
{
  startPosition = p;
  return sort( startPosition );
}

Position Figure::getStartPos( void )
{
  return startPosition;
}

void Figure::setBlowup( char* nblowup )
{
  strcpy( blowup, nblowup );
}

inline char* getDB( char* line )
{
  return strstr( line, "DB" );
}

inline int Figure::getNextIntPos( char* line, int pos )
{
  char c = line[pos] ;

  // falls pos != 0, erstmal aktuelle Zahl ueberlesen
  if( pos != 0 )
  {
    if( line[pos] == '-' )
      pos++;
      while( ( c != 0 ) && isdigit( c ) &&  ( c != ';' ) )
        c = line[++pos];

      if( c == ';' )
        return -1;

      if( c == 0 )
        return -1;
  }

  // erste Ziffer suchen
  while( ( c != 0 ) && !isdigit( c )  && ( c != ';' ) )
    c = line[++pos];

  if( c == ';' )
    return -1;

  if( line[pos-1] == '-' )
    pos--;

  if( c == 0 )
    return -1;
  return pos;
}


void Figure::getBlowup( char* line )
{
  char* l=strchr( line, '*' );
  if( l )
  {
    int i = 1;
    while( l[i] != ',' )
    { // ende blowup-Faktor
      blowup[i-1] = l[i];
      i++;
    }
    blowup[i-1] = '\0';
  }
}

//
// Gibt die Linie zurueck, die diesen Punkt als Start oder Endpunkt hat.
// Die Linie wird gefunden wenn der Punkt x+-DX, y+-DX vom reellen Punkt
// abweicht.
//

#define DX 0
#define DY 0

LineList* Figure::getLineFromPosition( int x, int y )
{
  LineList* ll = new LineList;
  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    Line* line = lineList.retrieve();
    if( (abs(line->getX1() - x ) <= DX ) && ( abs( line->getY1() - y ) <= DY ) )
    {
      line->mirror();
      ll->append( line );
      continue;
    }
    if( (abs(line->getX() - x ) <= DX ) && ( abs( line->getY() - y ) <= DY ) )
    {
      ll->append( line );
      continue;
    }
  }
  return ll;
}

int Figure::isOverPoint( int x0, int y0 )
{
  for( lineList.first(); !lineList.isLast(); lineList.next() )
  {
    Line* line = lineList.retrieve();
    if( (abs(line->getX1() - x0 ) <= DX ) && ( abs( line->getY1() - y0 ) <= DY ) )
    {
      return 1;
    }
    if( (abs(line->getX() - x0 ) <= DX ) && ( abs( line->getY() - y0 ) <= DY ) )
    {
      return 1;
    }
  }
  return 0;
}

void  Figure::changedLine( Line* line )
{
 int pos = lineList.find( line );
 if( pos == 0 )
 {
   Line* l = lineList.first();
   startPosition.set( l->getX(), l->getY() );
   Position::set( l->getX(), l->getY() );
 }
}


void  Figure::showStartPoint( int r )
{
  Point p( screen, startPosition.getX(), startPosition.getY() );
  Line* line = lineList.first();
  //Point p( screen, line->getX(), line->getY() );
  p.highlight( r );
  line->highlight();
  Notice n(screen, "Ok!" );
  n.start();
  line->restoreBackground();
  line->setColor( screen->getFgc() );
  line->draw();
  p.restoreBackground();
}

void Figure::mirror_x( void )
{
 int x_offset=0;
 int max_offset=0;
 int old_x1;
 if( lineList.isEmpty() )
   return;
 restoreBackground();
 Position cp = _findStartPosition( LEFT );
 sort( cp );
 lineList.first();
 old_x1=lineList.retrieve()->getX1();
 for( lineList.first(); !lineList.isLast(); lineList.next() )
 {
   Line* line = lineList.retrieve();
   int X1, X0;
   X1=line->getX1();
   X0=line->getX();
   line->set( X0-x_offset,
              line->getY(),
              X0-(X1-X0)-x_offset,
              line->getY1());
   x_offset+=2*(X1-X0);
   if (abs(x_offset)>max_offset)
    max_offset=abs(x_offset);
   line->draw();
 }
 lineList.first();
 if (old_x1<lineList.retrieve()->getX1())
  move( -max_offset/2, 0 );
 else
  move( max_offset/2, 0 );
 Line* line = lineList.first();
 Position::set( line->getX(), line->getY() );
 startPosition.set( line->getX(), line->getY() );
}

void Figure::mirror_y( void )
{
 int y_offset=0;
 int max_offset=0;
 int old_y1;
 if( lineList.isEmpty() )
   return;
 restoreBackground();
 Position cp = _findStartPosition( UPPER );
 sort( cp );
 lineList.first();
 old_y1=lineList.retrieve()->getY1();
 for( lineList.first(); !lineList.isLast(); lineList.next() )
 {
   Line* line = lineList.retrieve();
   int Y1, Y0;
   Y1=line->getY1();
   Y0=line->getY();
   line->set( line->getX(),
              Y0-y_offset,
              line->getX1(),
              Y0-(Y1-Y0)-y_offset);
   y_offset+=2*(Y1-Y0);
   if (abs(y_offset)>max_offset)
    max_offset=abs(y_offset);
   line->draw();
 }
 lineList.first();
 if (old_y1<lineList.retrieve()->getY1())
  move( 0,-max_offset/2 );
 else
  move( 0, max_offset/2 );
 Line* line = lineList.first();
 Position::set( line->getX(), line->getY() );
 startPosition.set( line->getX(), line->getY() );
}

