/* $Id: hpgl.c,v 1.1 2007/05/24 18:20:43 Kevin Bacon Exp $ */
/* A program to drive a couple of stepper motors */
/* HPGL reader */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "libesketch.h"

#define NPOINTS 200000
#define POINT_DONE   0
#define POINT_START  1
#define POINT_MIDDLE 2
#define POINT_END    3

struct point
{
    int x;
    int y;
    int flags;
};

void small_remove( struct point *points, int n_points, int min_size )
{
    int i;
    int j;
    int start_i;
    int min_x, min_y, max_x, max_y;

printf( "small_remove\n" );
    for( i = 0; i < n_points; i++ )
    {
        if( points[i].flags == POINT_START )
        {
            start_i = i;
            min_x = max_x = points[i].x;
            min_y = max_y = points[i].y;
        }

        min_x = MIN( min_x, points[i].x );
        max_x = MAX( max_x, points[i].x );
        min_y = MIN( min_y, points[i].y );
        max_y = MAX( max_y, points[i].y );

        if( points[i].flags == POINT_END )
        {
            if((( max_x - min_x ) < min_size ) && (( max_y - min_y ) < min_size ))
            {
printf( "size_x=%d, size_y=%d removing %d-%d\n", max_x - min_x, max_y - min_y, start_i, i );
                for( j = start_i; j <= i; j++ )
                {
                    points[j].flags = POINT_DONE;
                }
            }
        }
    }
}

int main( int argc, char **argv )
{
    int a, b;
    int x, y;
    int i;
    struct point *points;
    int current_point = 0;
    int dist, min;
    int min_i;
    float scale = 0.1;
    int down = 0;
    int sort = 0;
    int min_size = 100;

    points = malloc( NPOINTS * sizeof( struct point ));
    if( points == NULL )
    {
        printf( "Error: Can't allocate %d points\n", NPOINTS );
        exit( 1 );
    }

    printf( "Usage: hpgl [scale [stretch]][sort] (default is %g %d)\n", scale, stretch );
    if( argc >= 2 ) scale   = atof( argv[1] );
    if( argc >= 3 ) stretch = atoi( argv[2] );
    for( i = 1; i < argc; i++ )
    {
        if( strcmp( argv[i], "sort" ) == 0 ) sort = 1;
    }

    printf( "scale=%g, stretch=%d, sort=%d\n", scale, stretch, sort );

    /* Init libesketch */
    init();

    while( 1 )
    {
        if( current_point >= NPOINTS )
        {
            printf( "ERROR: too many points\n" );
            break;
        }

        // Read command characters
        a = getchar();
        b = getchar();

        if( a == EOF || b == EOF ) break;

        // Check for plot absolute command
        if(( tolower( a ) == 'p' ) && ( tolower( b ) == 'a' ))
        {
            while( 1 )
            {
                // Read coordinates
                i = scanf( "%d,%d", &x, &y );
                if( i != 2 ) break;

                if( down )
                {
                    points[ current_point ].x = x;
                    points[ current_point ].y = y;
                    points[ current_point ].flags = POINT_MIDDLE;

//printf( "%c (%d,%d) %d\n", c, points[current_point].x, points[current_point].y, points[current_point].flags );
                    current_point++;
                }

                a = getchar();
                if( a == ';' || a == EOF ) break;
            }
        }
        else if(( tolower( a ) == 'p' ) && ( tolower( b ) == 'd' ))
        {
            // Mark point as start
            points[ current_point ].x = x;
            points[ current_point ].y = y;
            points[ current_point ].flags = POINT_START;
//printf( "%c (%d,%d) %d\n", c, points[current_point].x, points[current_point].y, points[current_point].flags );
            current_point++;
            down = 1;
            a = getchar();
            continue;
        }
        else if(( tolower( a ) == 'p' ) && ( tolower( b ) == 'u' ))
        {
            // Mark point as end
            points[ current_point ].x = x;
            points[ current_point ].y = y;
            points[ current_point ].flags = POINT_END;
//printf( "%c (%d,%d) %d\n", c, points[current_point].x, points[current_point].y, points[current_point].flags );
            current_point++;
            down = 0;
            a = getchar();
            continue;
        }

        // else discard the rest of the line
        else
        {
            while( 1 )
            {
                a = getchar();
                if( a == ';' || a == EOF ) break;
            }
        }
    }

    printf( "total points = %d\n", current_point );

    // Remove small regions
    small_remove( points, current_point, min_size );

    if( !sort )
    {
        for( i = 0; i < current_point; i++ )
        {
            // printf( "(%d,%d) %d\n", points[i].x, points[i].y, points[i].flags );
            if( points[i].flags != POINT_DONE )
            {
                drawto( scale * points[i].x, scale * points[i].y );
            }
        }
    }
    else
    {
        // Loop until all points drawn
        x = y = 0;
        while( 1 )
        {
            // Find nearest start/end point to current point
            min = 10000 * 10000;
            min_i = -1;
            for( i = 0; i < current_point; i++ )
            {
                if( points[i].flags == POINT_START || points[i].flags == POINT_END )
                {
                     dist = ( points[i].x - x ) * ( points[i].x - x )
                          + ( points[i].y - y ) * ( points[i].y - y );
                     if( dist < min )
                     {
                         min = dist;
                         min_i = i;
                     }
                }
            }

            // Stop when no start/end found
            if( min_i == -1 ) break;

            // Draw this path forwards or backwards
            if( points[min_i].flags == POINT_START )
            {
                while( 1 )
                {
                    x = points[min_i].x;
                    y = points[min_i].y;
                    drawto( scale * x, scale * y );

                    if( points[min_i].flags == POINT_END ) break;
                    points[min_i].flags = POINT_DONE;
                    min_i++;
                }

                // Set the last flag
                points[min_i].flags = POINT_DONE;
            }
            else
            {
                while( 1 )
                {
                    x = points[min_i].x;
                    y = points[min_i].y;
                    drawto( scale * x, scale * y );

                    if( points[min_i].flags == POINT_START ) break;
                    points[min_i].flags = POINT_DONE;
                    min_i--;
                }

                // Set the last flag
                points[min_i].flags = POINT_DONE;
            }
        }
    }

    /* Turn off motor drivers */
    stop();

    return 0;
}
