#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/Vect.h>
#include <grass/dbmi.h>
#include <grass/raster.h>
#include <grass/display.h>
#include <grass/colors.h>
#include <grass/form.h>
#include "global.h"
#include "proto.h"

/* Split line */
int split_line (void)
{
    int sxn, syn, line, last_line, last_seg, node1, node2, type, seg, i, np;
    int button, first;
    double x, y, thresh, xo, yo;
    struct line_pnts *Points, *NPoints;
    struct line_cats *Cats;
    
    G_debug (2, "split_line()");

    Points = Vect_new_line_struct ();
    NPoints = Vect_new_line_struct ();
    Cats = Vect_new_cats_struct ();
    
    i_prompt ( "Split line:"); 
    i_prompt_buttons ( "Select", "", "Quit tool"); 
    
    driver_open();
    
    /* TODO: use some better threshold */
    thresh = fabs ( D_d_to_u_col ( 10 ) - D_d_to_u_col ( 0 ) ) ; 
    G_debug (2, "thresh = %f", thresh );
    
    first = 1; 
    last_line = 0;
    last_seg = 0;
    sxn = COOR_NULL; syn = COOR_NULL;
    while ( 1 ) {
	/* Get next coordinate */
        R_set_update_function ( update );
	R_get_location_with_pointer ( &sxn, &syn, &button); 
	if ( last_line == 0 ) {
            i_prompt_buttons ( "Select", "", "Quit tool"); 
        } 
	
	if ( last_line > 0 ) {
	    display_line ( last_line, SYMB_DEFAULT, 1);
	}

	x =  D_d_to_u_col ( sxn );
	y =  D_d_to_u_row ( syn );
	G_debug (3, "button = %d x = %d = %f y = %d = %f", button, sxn, x, syn, y);

	if ( button == 0 || button == 3 ) break;

	if ( button == 1 ) { /* Select / split */
	    if ( last_line > 0 ) { /* Line is already selected -> split */
	        display_line ( last_line, SYMB_BACKGROUND, 1);
		Vect_get_line_nodes ( &Map, last_line, &node1, &node2 ); 
                display_node ( node1, SYMB_BACKGROUND, 1);
                display_node ( node2, SYMB_BACKGROUND, 1);
		symb_set_driver_color ( SYMB_BACKGROUND );
		display_icon ( xo, yo, G_ICON_CROSS, 0, 10, 1);

		/* Read and delete old */
		type = Vect_read_line ( &Map, Points, Cats, last_line );
		Vect_delete_line ( &Map, last_line );
		updated_lines_and_nodes_erase_refresh_display();
		np = Points->n_points;

		/* First part */
		Vect_reset_line ( NPoints );
		for ( i = 0; i < last_seg; i++ ){ 
		    Vect_append_point (NPoints, Points->x[i], Points->y[i], Points->z[i] );
		}
		Vect_append_point (NPoints, xo, yo, 0 );
		Vect_write_line( &Map, type, NPoints, Cats);
		updated_lines_and_nodes_erase_refresh_display();

		/* Second part */
		Vect_reset_line ( NPoints );
		Vect_append_point (NPoints, xo, yo, 0 );
		for ( i = last_seg; i < np; i++ ){ 
		    Vect_append_point (NPoints, Points->x[i], Points->y[i], Points->z[i] );
		}
		Vect_write_line( &Map, type, NPoints, Cats);
		updated_lines_and_nodes_erase_refresh_display();

		last_line = 0;
	    } 

	    /* Select vertex */ 
	    line = Vect_find_line (&Map, x, y, 0, GV_LINE|GV_BOUNDARY, thresh, 0, 0);
	    G_debug (2, "line found = %d", line );
	    
	    /* Display new selected line if any */
	    if ( line > 0 ) {

		/* Find the nearest vertex on the line */
		type = Vect_read_line ( &Map, Points, NULL, line );
		seg = Vect_line_distance ( Points, x, y, 0, 0, &xo, &yo, NULL, NULL, NULL, NULL );

	        display_line ( line, SYMB_HIGHLIGHT, 1);
		symb_set_driver_color ( SYMB_HIGHLIGHT );
		display_icon ( xo, yo, G_ICON_CROSS, 0, 10, 1);

		i_prompt_buttons ( "Confirm and select next", "Unselect", "Quit tool"); 
		last_line = line;
		last_seg = seg;
	    }
	}
	if ( button == 2 ) { /* Unselect */
	    if ( last_line > 0 ) {
		symb_set_driver_color ( SYMB_BACKGROUND );
		display_icon ( xo, yo, G_ICON_CROSS, 0, 10, 1);
		last_line = 0;
	    }
	}
    }
    if ( last_line > 0 ) {
	symb_set_driver_color ( SYMB_BACKGROUND );
	display_icon ( xo, yo, G_ICON_CROSS, 0, 10, 1);
    }

    driver_close();
    
    i_prompt (""); 
    i_prompt_buttons ( "", "", ""); 
    i_coor ( COOR_NULL, COOR_NULL); 
    
    G_debug (3, "split_line(): End");

    return 1;
}

/* Remove line vertex */
int rm_vertex (void)
{
    int sxn, syn, line, last_line, last_seg, node1, node2, type, seg, i, np;
    int button, first;
    double x, y, thresh, xo, yo, dist;
    struct line_pnts *Points;
    struct line_cats *Cats;
    
    G_debug (2, "remove_vertex()");

    Points = Vect_new_line_struct ();
    Cats = Vect_new_cats_struct ();
    
    i_prompt ( "Remove vertex:"); 
    i_prompt_buttons ( "Select vertex", "", "Quit tool"); 
    
    driver_open();
    
    /* TODO: use some better threshold */
    thresh = fabs ( D_d_to_u_col ( 10 ) - D_d_to_u_col ( 0 ) ) ; 
    G_debug (2, "thresh = %f", thresh );
    
    first = 1; 
    last_line = 0;
    last_seg = 0;
    sxn = COOR_NULL; syn = COOR_NULL;
    while ( 1 ) {
	/* Get next coordinate */
        R_set_update_function ( update );
	R_get_location_with_pointer ( &sxn, &syn, &button); 
	if ( last_line == 0 ) {
            i_prompt_buttons ( "Select vertex", "", "Quit tool"); 
        } 
	
	if ( last_line > 0 ) {
	    display_line ( last_line, SYMB_DEFAULT, 1);
	}

	x =  D_d_to_u_col ( sxn );
	y =  D_d_to_u_row ( syn );
	G_debug (3, "button = %d x = %d = %f y = %d = %f", button, sxn, x, syn, y);

	if ( button == 0 || button == 3 ) break;

	if ( button == 1 ) { /* Select / new location */
	    if ( last_line > 0 ) { /* Line is already selected */
	        display_line ( last_line, SYMB_BACKGROUND, 1);
		Vect_get_line_nodes ( &Map, last_line, &node1, &node2 ); 
                display_node ( node1, SYMB_BACKGROUND, 1);
                display_node ( node2, SYMB_BACKGROUND, 1);
		symb_set_driver_color ( SYMB_BACKGROUND );
		display_icon ( xo, yo, G_ICON_BOX, 0, 10, 1);

		type = Vect_read_line ( &Map, Points, Cats, last_line );
		np = Points->n_points;
		for ( i = last_seg; i < np - 1; i++ ){ 
                    Points->x[i] = Points->x[i+1];
                    Points->y[i] = Points->y[i+1];
                    Points->z[i] = Points->z[i+1];
		}
		Points->n_points--;
		Vect_rewrite_line(&Map, last_line, type, Points, Cats);
		updated_lines_and_nodes_erase_refresh_display();
		last_line = 0;
	    } 

	    /* Select vertex */ 
	    line = Vect_find_line (&Map, x, y, 0, GV_LINE|GV_BOUNDARY, thresh, 0, 0);
	    G_debug (2, "line found = %d", line );
	    
	    /* Display new selected line if any */
	    if ( line > 0 ) {

		/* Find the nearest vertex on the line */
		type = Vect_read_line ( &Map, Points, NULL, line );
		seg = Vect_line_distance ( Points, x, y, 0, 0, &xo, &yo, NULL, NULL, NULL, NULL );

		dist = Vect_points_distance ( xo, yo, 0, Points->x[seg-1], Points->y[seg-1], 0, 0);

		if ( dist < Vect_points_distance ( xo, yo, 0, Points->x[seg], Points->y[seg], 0, 0) ) {
		    seg -= 1;
		}
		
		xo = Points->x[seg];
		yo = Points->y[seg];
		
	        display_line ( line, SYMB_HIGHLIGHT, 1);
		symb_set_driver_color ( SYMB_HIGHLIGHT );
		display_icon ( xo, yo, G_ICON_BOX, 0, 10, 1);

		i_prompt_buttons ( "Confirm and select next", "Unselect", "Quit tool"); 
		last_line = line;
		last_seg = seg;
	    }
	}
	if ( button == 2 ) { /* Unselect */
	    if ( last_line > 0 ) {
		symb_set_driver_color ( SYMB_BACKGROUND );
		display_icon ( xo, yo, G_ICON_BOX, 0, 10, 1);
		last_line = 0;
	    }
	}
    }
    if ( last_line > 0 ) {
	symb_set_driver_color ( SYMB_BACKGROUND );
	display_icon ( xo, yo, G_ICON_BOX, 0, 10, 1);
    }

    driver_close();
    
    i_prompt (""); 
    i_prompt_buttons ( "", "", ""); 
    i_coor ( COOR_NULL, COOR_NULL); 
    
    G_debug (3, "remove_vertex(): End");

    return 1;
}

/* Add new vertex to line */
int add_vertex (void)
{
    int i, sxn, syn, sxo = 0, syo = 0, line, last_line, last_seg, node1, node2, type, seg, np;
    int button, first, do_snap = 0;
    double x, y, px, py, thresh, xo, yo, dist, len;
    struct line_pnts *Points;
    struct line_cats *Cats;
    
    G_debug (2, "add_vertex()");

    Points = Vect_new_line_struct ();
    Cats = Vect_new_cats_struct ();
    
    i_prompt ( "Add vertex:"); 
    
    driver_open();
    
    /* TODO: use some better threshold */
    thresh = fabs ( D_d_to_u_col ( 10 ) - D_d_to_u_col ( 0 ) ) ; 
    G_debug (2, "thresh = %f", thresh );
    
    first = 1; 
    last_line = 0;
    last_seg = 0;
    sxn = COOR_NULL; syn = COOR_NULL;
    while ( 1 ) {
	/* Get next coordinate */
        R_set_update_function ( update );
	if ( last_line == 0 ) {
            i_prompt_buttons ( "Select", "", "Quit tool"); 
	    R_get_location_with_pointer ( &sxn, &syn, &button); 
        } else R_get_location_with_line (sxo, syo, &sxn, &syn, &button); 
	
	if ( last_line > 0 ) {
	    display_line ( last_line, SYMB_DEFAULT, 1);
	}

	x =  D_d_to_u_col ( sxn );
	y =  D_d_to_u_row ( syn );
	G_debug (3, "button = %d x = %d = %f y = %d = %f", button, sxn, x, syn, y);

	if ( button == 0 || button == 3 ) break;

	if ( button == 1 ) { /* Select line segment */
	    if ( last_line == 0 ) { /* Select line */ 
                line = Vect_find_line (&Map, x, y, 0, GV_LINE|GV_BOUNDARY, thresh, 0, 0);
                G_debug (2, "line found = %d", line );
                
                /* Display new selected line if any */
                if ( line > 0 ) {
		    display_line ( line, SYMB_HIGHLIGHT, 1);

		    /* Find the nearest vertex on the line */
		    type = Vect_read_line ( &Map, Points, NULL, line );
		    seg = Vect_line_distance ( Points, x, y, 0, 0, &px, &py, NULL, NULL, NULL, NULL );

                    G_debug (3, "seg = %d", seg );

		    xo = ( Points->x[seg-1] + Points->x[seg] ) / 2;
		    yo = ( Points->y[seg-1] + Points->y[seg] ) / 2;

		    /* If close to first or last point insert before / after the line. 
		     * 'close' is here < 1/4 of segment length */
	            do_snap = 0;
		    if ( seg == 1 ) {
		        dist = Vect_points_distance ( px, py, 0, Points->x[0], Points->y[0], 0, 0);
		        len = Vect_points_distance ( Points->x[0], Points->y[0], 0, 
				                     Points->x[1], Points->y[1], 0, 0);
                    
			if ( dist < len/4 ) {
			    seg = 0;
			    xo = Points->x[0]; 
			    yo = Points->y[0]; 
			    do_snap = 1;
			}
		    }

		    if ( seg == Points->n_points - 1 ) {
		        np = Points->n_points;
		        dist = Vect_points_distance ( px, py, 0, Points->x[np-1], Points->y[np-1], 0, 0);
		        len = Vect_points_distance  ( Points->x[np-2], Points->y[np-2], 0, 
				                      Points->x[np-1], Points->y[np-1], 0, 0);
			if ( dist < len/4 ) {
			    seg ++;
			    xo = Points->x[np-1]; 
			    yo = Points->y[np-1]; 
			    do_snap = 1;
			}
		    }
                    G_debug (3, "seg 2 = %d", seg );

                    sxo = D_u_to_d_col ( xo  ) ; 
		    syo = D_u_to_d_row ( yo );

                    i_prompt_buttons ( "New vertex", "Unselect", "Quit tool"); 
		    last_line = line;
		    last_seg = seg;
	        }
	    } else { /* Line is already selected -> new vertex */
		if ( do_snap ) {
		    snap ( &x, &y );
		}
	        display_line ( last_line, SYMB_BACKGROUND, 1);
		Vect_get_line_nodes ( &Map, last_line, &node1, &node2 ); 
                display_node ( node1, SYMB_BACKGROUND, 1);
                display_node ( node2, SYMB_BACKGROUND, 1);

		type = Vect_read_line ( &Map, Points, Cats, last_line );
		np = Points->n_points;
		/* insert vertex */
		Vect_append_point (Points, 0, 0, 0);
		for ( i = np; i > last_seg; i-- ) {
		    Points->x[i] = Points->x[i-1];
		    Points->y[i] = Points->y[i-1];
		    Points->z[i] = Points->z[i-1];
		}
		
                Points->x[last_seg] = x;
                Points->y[last_seg] = y;
                Points->z[last_seg] = 0;
		
		Vect_rewrite_line(&Map, last_line, type, Points, Cats);
		updated_lines_and_nodes_erase_refresh_display();
		last_line = 0;
	    }

	}
	if ( button == 2 ) { /* Unselect */
	    if ( last_line > 0 ) {
		last_line = 0;
	    }
	}
    }

    driver_close();
    
    i_prompt (""); 
    i_prompt_buttons ( "", "", ""); 
    i_coor ( COOR_NULL, COOR_NULL); 
    
    G_debug (3, "add_vertex(): End");

    return 1;
}

/* Move vertex */
int move_vertex (void)
{
    int sxn, syn, sxo = 0, syo = 0, line, last_line, last_seg, node1, node2, type, seg;
    int button, first;
    double x, y, thresh, xo, yo, dist;
    struct line_pnts *Points;
    struct line_cats *Cats;
    
    G_debug (2, "move_vertex()");

    Points = Vect_new_line_struct ();
    Cats = Vect_new_cats_struct ();
    
    i_prompt ( "Move vertex:"); 
    
    driver_open();
    
    /* TODO: use some better threshold */
    thresh = fabs ( D_d_to_u_col ( 10 ) - D_d_to_u_col ( 0 ) ) ; 
    G_debug (2, "thresh = %f", thresh );
    
    first = 1; 
    last_line = 0;
    last_seg = 0;
    sxn = COOR_NULL; syn = COOR_NULL;
    while ( 1 ) {
	/* Get next coordinate */
        R_set_update_function ( update );
	if ( last_line == 0 ) {
            i_prompt_buttons ( "Select", "", "Quit tool"); 
	    R_get_location_with_pointer ( &sxn, &syn, &button); 
        } else R_get_location_with_line (sxo, syo, &sxn, &syn, &button); 
	
	if ( last_line > 0 ) {
	    display_line ( last_line, SYMB_DEFAULT, 1);
	}

	x =  D_d_to_u_col ( sxn );
	y =  D_d_to_u_row ( syn );
	G_debug (3, "button = %d x = %d = %f y = %d = %f", button, sxn, x, syn, y);

	if ( button == 0 || button == 3 ) break;

	if ( button == 1 ) { /* Select / new location */
	    if ( last_line == 0 ) { /* Select line */ 
                line = Vect_find_line (&Map, x, y, 0, GV_LINE|GV_BOUNDARY, thresh, 0, 0);
                G_debug (2, "line found = %d", line );
                
                /* Display new selected line if any */
                if ( line > 0 ) {
		    display_line ( line, SYMB_HIGHLIGHT, 1);

		    /* Find the nearest vertex on the line */
		    type = Vect_read_line ( &Map, Points, NULL, line );
		    seg = Vect_line_distance ( Points, x, y, 0, 0, &xo, &yo, NULL, NULL, NULL, NULL );

		    dist = Vect_points_distance ( xo, yo, 0, Points->x[seg-1], Points->y[seg-1], 0, 0);

		    if ( dist < Vect_points_distance ( xo, yo, 0, Points->x[seg], Points->y[seg], 0, 0) ) {
			seg -= 1;
		    }
		    
		    xo = Points->x[seg];
		    yo = Points->y[seg];
                    sxo = D_u_to_d_col ( xo  ) ; 
		    syo = D_u_to_d_row ( yo );


                    i_prompt_buttons ( "New location", "Unselect", "Quit tool"); 
		    last_line = line;
		    last_seg = seg;
	        }
	    } else { /* Line is already selected */
		if ( last_seg == 0 || last_seg == Points->n_points - 1 ) {
		    snap ( &x, &y );
		}
	        display_line ( last_line, SYMB_BACKGROUND, 1);
		Vect_get_line_nodes ( &Map, last_line, &node1, &node2 ); 
                display_node ( node1, SYMB_BACKGROUND, 1);
                display_node ( node2, SYMB_BACKGROUND, 1);

		type = Vect_read_line ( &Map, Points, Cats, last_line );
                Points->x[last_seg] = Points->x[last_seg] + x - xo;
                Points->y[last_seg] = Points->y[last_seg] + y - yo;
		Vect_rewrite_line(&Map, last_line, type, Points, Cats);
		updated_lines_and_nodes_erase_refresh_display();
		last_line = 0;
	    }

	}
	if ( button == 2 ) { /* Unselect */
	    if ( last_line > 0 ) {
		last_line = 0;
	    }
	}
    }

    driver_close();
    
    i_prompt (""); 
    i_prompt_buttons ( "", "", ""); 
    i_coor ( COOR_NULL, COOR_NULL); 
    
    G_debug (3, "move_vertex(): End");

    return 1;
}
