
#include "mview_main.h"

#include "gx11/visual_context.h"
#include "gsys/cfc.h"
#include "mview_display.h"
#include "mview_stuff.h"
#include "mview_cursor.h"
#include "mview_slice.h"
#include "mview_view.h"
#include "mview_misc.h"
#include "mview_widgets.h"
#include "mview_macros.h"
#include "mview_spectrum.h"
#include "gcore/gcomm.h"

#include <locale.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <Xm/Xm.h>

#include <Xm/RowColumn.h>
#include <Xm/DrawingA.h>
#include <Xm/PushBG.h>
#include <Xm/LabelG.h>
#include <Xm/SeparatoG.h>

#define mview CFC_EXPORT_NAME(mview)

Pixmap *subpixmap;
int npix[4];
static char dumstr[80];

static struct itimerval rttimer, old_rttimer;

int standalone = 0;

int isize, zsizex, zsizey, cols, rows, Nsubs, zoom, factor = 0, mode, localzoom;
unsigned int sizex, sizey;
int *row, *col;
int voffset = 0;
int incr = 0;
float *fimage, *sfimage;
float Blank, Tolerance;

float clip[2], displayclip[2];

#define max( a,b) (((a)>(b))? (a):(b))

void (*phys_coord) () = NULL;   /* Address storage of phys_coord function */
char *image_name;
char *helplocation;

static int com_params[3];

Widget zoomshell = NULL, zoom_w;
static Widget popup_pane1, popup_pane2, popup_pane3;

#define MENU_ZOOM_X2      100
#define MENU_ZOOM_X4      101
#define MENU_ZOOM_OFF     102
#define MENU_OTHER_WINDOW 103

XImage *hugeimage;

/* Actions to be added to the Translation Manager */
static XtActionsRec actions_list[] = {
    {"resize_zoom_w", (XtActionProc)resize_zoom_w},
};

static char zoom_translations[] = "<ConfigureNotify>: resize_zoom_w( )\n";

Dimension zoom_width = 128, zoom_height = 128;

void destroy_zoomshell( Widget w, XtPointer w_code, XmAnyCallbackStruct *cbs)
{
    zoomshell = NULL;
}

void resize_zoom_w( Widget w, XEvent * event, String * params, Cardinal num_params)
{
    switch (event->type) {

    case ConfigureNotify:
        XtVaGetValues( w, XmNwidth, &zoom_width, XmNheight, &zoom_height, NULL);
        break;
    }
}

void re_enter( Widget w, XtPointer w_code, XmAnyCallbackStruct *cbs)
{
    /*printf( "OK\n") */ ;
}

void get_com_params( int *keyboard, int *idsemap, int *idmemory)
{
    *keyboard = com_params[0];
    *idsemap = com_params[1];
    *idmemory = com_params[2];
}

static int test, Z;

static int ERROR;

static int gesterreurs( Display * canal_aff, XErrorEvent * errev)
{
    XGetErrorText( canal_aff, errev->error_code, dumstr, 80);
    /*
       fprintf( stderr, "%s\n", dumstr);
     */
    ERROR = 1;
    return 0;
}

static int other_gesterreurs( Display * canal_aff, XErrorEvent * errev)
{
    XGetErrorText( canal_aff, errev->error_code, dumstr, 80);
    /*  fprintf( stderr, "%s\n", dumstr); */
    fprintf( stderr, "error freeing %d\n", test);
    return 0;
}

char *lotofpixels = NULL;

extern int xoffset, yoffset;

float *fimagesav;

extern void trap_SIGALRM();

void long_timer( )
{
    rttimer.it_value.tv_sec = 3600;
    rttimer.it_value.tv_usec = 0;
    rttimer.it_interval.tv_sec = 3600;
    rttimer.it_interval.tv_usec = 0;

    signal( SIGALRM, trap_SIGALRM);

    setitimer( ITIMER_REAL, &rttimer, &old_rttimer);
}

void mview( char *field, float *user_image, int *size, void (*user_coord) (), char *helpdir, int *params, float *bl)
{


    int ret, i = 0;

    char *works;

    /* force locale "en" to avoir problems reading or writing lut files */
    if (setlocale( LC_NUMERIC, "en_US") == NULL) {
        if (setlocale( LC_NUMERIC, "UTF-8") == NULL) {
            fprintf( stderr, "Warning, Unable to set locale to \"en_US\", may be unable to read color files.\n");
        }
    }

    if (params == NULL)
        standalone = 1;
    if (standalone == 0) {
        int comm_id = params[0];

        /* open communication board */
        if (sic_open_comm_board( comm_id) == -1) {
            return;
        }

        com_params[0] = params[0];
        com_params[1] = params[1];
        com_params[2] = params[2];
    }
    Blank = bl[0];
    Tolerance = bl[1];

    printf( "Blank %e Tolerance %f\n", Blank, Tolerance);
    fflush( stdout);
    phys_coord = user_coord;
    fimage = user_image;

    helplocation = helpdir;
    image_name = field;

/* works on all machines: */
    works = strrchr( image_name, '/');

    for (i = 0; i < 4; i++)
        npix[i] = size[i];
    /*
       fprintf( stderr, "NPIX %d %d %d %d\n", npix[0], npix[1], npix[2], npix[3]);
     */
    Nsubs = npix[2];

    sfimage = (float *)malloc( Nsubs * npix[0] * npix[1] * sizeof( float));
    for (i = 0; i < Nsubs * npix[0] * npix[1]; i++)
        sfimage[i] = fimage[i];

    clip[0] = +10000000.;
    clip[1] = -10000000.;

    for (i = 0; i < npix[0] * npix[1] * Nsubs; i++) {
        if ((float)fabs( (double)(fimage[i] - Blank)) <= Tolerance)
            continue;

        if (fimage[i] < clip[0])
            clip[0] = fimage[i];
        if (fimage[i] > clip[1])
            clip[1] = fimage[i];
    }

    fprintf( stderr, "%s %f %f\n", "clips=", clip[0], clip[1]);

    if ((clip[0] == +10000000.) && (clip[1] == -10000000.)) {
        fprintf( stderr, "%s\n", "Image is blank ...\n");
        exit( 1);
    }

    displayclip[0] = clip[0];
    displayclip[1] = clip[1];
    /*
       fprintf( stderr, "%s %d %d %d\n", "npix=", npix[0], npix[1], npix[2]);
     */

    isize = npix[0] * npix[1];

    inidisplay( );

    xoffset = (maxspectrumwidth - sizex) / 2;
    yoffset = (maxspectrumheight - sizey) / 2;

    specheight = zsizey;

    /*
       fprintf( stderr, "%s %d %s %d\n", "sizex", sizex,
       "sizey", sizey);
     */
    row = (int *)malloc( Nsubs * sizeof( int));
    col = (int *)malloc( Nsubs * sizeof( int));

    subpixmap = (Pixmap *) malloc( Nsubs * sizeof( Pixmap));

    for (Z = 0; Z < Nsubs; Z++) {
        row[Z] = Z / cols;
        col[Z] = Z - row[Z] * cols;

        AffImage( &fimage[isize * Z], pixmap, sizex, sizey,
                 1, npix[0] / incr, 1, npix[1] / incr,
                 zoom, zoom, clip[0], clip[1], incr, 0, 0, col[Z] * sizex, row[Z] * sizey, sizex, sizey);
    }

    /* ret = make_huge( ); */
    ret = -1;

    {
        XmString label;
        Widget button;
        Arg args[2];
        void PostPopup( ), MenuCB();

        popup_pane1 = XmCreatePopupMenu( small_w, "popupMenu", NULL, 0);
        popup_pane2 = XmCreatePopupMenu( small_w, "popupMenu", NULL, 0);
        popup_pane3 = XmCreatePopupMenu( small_w, "popupMenu", NULL, 0);

        if (ret == 0) {
            mode = 3;
            XtAddEventHandler( small_w, ButtonPressMask, False, (XtEventHandler) PostPopup, popup_pane3);
        } else {
            mode = 1;
            XtAddEventHandler( small_w, ButtonPressMask, False, (XtEventHandler) PostPopup, popup_pane1);
        }
        label = XmStringCreateLocalized( "    ");

        XtSetArg( args[0], XmNlabelString, label);
        button = XmCreateLabelGadget( popup_pane1, NULL, args, 1);
        XtManageChild( button);
        button = XmCreateLabelGadget( popup_pane2, NULL, args, 1);
        XtManageChild( button);
        button = XmCreateLabelGadget( popup_pane3, NULL, args, 1);
        XtManageChild( button);
        XmStringFree( label);

        button = XmCreateSeparatorGadget( popup_pane1, NULL, NULL, 0);
        XtManageChild( button);
        button = XmCreateSeparatorGadget( popup_pane2, NULL, NULL, 0);
        XtManageChild( button);
        button = XmCreateSeparatorGadget( popup_pane3, NULL, NULL, 0);
        XtManageChild( button);

        label = XmStringCreateLocalized( "X2");
        XtSetArg( args[0], XmNlabelString, label);

        button = XmCreatePushButtonGadget( popup_pane1, NULL, args, 1);
        XtManageChild( button);
        XtAddCallback( button, XmNactivateCallback, (XtCallbackProc) MenuCB, (caddr_t) MENU_ZOOM_X2);
        button = XmCreateSeparatorGadget( popup_pane1, NULL, NULL, 0);
        XtManageChild( button);

        button = XmCreatePushButtonGadget( popup_pane2, NULL, args, 1);
        XtManageChild( button);
        XtAddCallback( button, XmNactivateCallback, (XtCallbackProc) MenuCB, (caddr_t) MENU_ZOOM_X2);
        button = XmCreateSeparatorGadget( popup_pane2, NULL, NULL, 0);
        XtManageChild( button);

        XmStringFree( label);

        label = XmStringCreateLocalized( "X4");
        XtSetArg( args[0], XmNlabelString, label);

        button = XmCreatePushButtonGadget( popup_pane1, NULL, args, 1);
        XtManageChild( button);
        XtAddCallback( button, XmNactivateCallback, (XtCallbackProc) MenuCB, (caddr_t) MENU_ZOOM_X4);

        button = XmCreatePushButtonGadget( popup_pane3, NULL, args, 1);
        XtManageChild( button);
        XtAddCallback( button, XmNactivateCallback, (XtCallbackProc) MenuCB, (caddr_t) MENU_ZOOM_X4);
        button = XmCreateSeparatorGadget( popup_pane3, NULL, NULL, 0);
        XtManageChild( button);

        XmStringFree( label);

        label = XmStringCreateLocalized( "Zoom off");
        XtSetArg( args[0], XmNlabelString, label);

        button = XmCreatePushButtonGadget( popup_pane2, NULL, args, 1);
        XtManageChild( button);
        XtAddCallback( button, XmNactivateCallback, (XtCallbackProc) MenuCB, (caddr_t) MENU_ZOOM_OFF);
        button = XmCreateSeparatorGadget( popup_pane2, NULL, NULL, 0);
        XtManageChild( button);
        button = XmCreatePushButtonGadget( popup_pane3, NULL, args, 1);
        XtManageChild( button);
        XtAddCallback( button, XmNactivateCallback, (XtCallbackProc) MenuCB, (caddr_t) MENU_ZOOM_OFF);
        button = XmCreateSeparatorGadget( popup_pane2, NULL, NULL, 0);
        XtManageChild( button);

        XmStringFree( label);

        label = XmStringCreateLocalized( "Other window");
        XtSetArg( args[0], XmNlabelString, label);

        button = XmCreatePushButtonGadget( popup_pane2, NULL, args, 1);
        XtManageChild( button);
        XtAddCallback( button, XmNactivateCallback, (XtCallbackProc) MenuCB, (caddr_t) MENU_OTHER_WINDOW);
        button = XmCreatePushButtonGadget( popup_pane3, NULL, args, 1);
        XtManageChild( button);
        XtAddCallback( button, XmNactivateCallback, (XtCallbackProc) MenuCB, (caddr_t) MENU_OTHER_WINDOW);


        XmStringFree( label);

    }

    if ((Nsubs >= 2) && (npix[0] < maxspectrumwidth)
        && (npix[1] < maxspectrumheight)) {

        XSetErrorHandler( gesterreurs);

        ERROR = 0;

        for (Z = 0; Z < Nsubs; Z++) {
            subpixmap[Z] = XCreatePixmap( display, g_vc.root_window, zsizex, zsizey, g_vc.depth);
            XSync( display, False);
            if (ERROR)
                break;
        }

        if (ERROR) {
            /*      fprintf( stderr, "%s", "Failed , Freeing ..."); */
            XSetErrorHandler( other_gesterreurs);

            for (test = 0; test < Z; test++) {
                XFreePixmap( display, subpixmap[test]);
                XSync( display, False);
                subpixmap[test] = 0;
            }
            subpixmap[Z] = 0;

        } else {

            for (Z = 0; Z < Nsubs; Z++) {

                AffImage( &fimage[isize * Z], subpixmap[Z], zsizex, zsizey,
                         1, npix[0], 1, npix[1], localzoom, localzoom, clip[0], clip[1], 1, 0, 0, 0, 0, zsizex, zsizey);
            }
        }
    }


    XSetForeground( display, gc, g_vc.white_pixel);

    for (Z = 0; Z < cols; Z++)
        XDrawLine( display, pixmap, gc, sizex * Z, 0, sizex * Z, rows * sizey - 1);
    for (Z = 0; Z < rows; Z++)
        XDrawLine( display, pixmap, gc, 0, sizey * Z, cols * sizex - 1, sizey * Z);

    draw_wedge( clip);

    long_timer( );

    realize_top( );
    
    sic_close_comm_board( );
}

void get_image_name( char name[256])
{
    strcpy( name, image_name);
}


void PostPopup( Widget w, Widget popup_pane, XButtonEvent * event)
{
    if (event->button != Button3)
        return;

    XmMenuPosition( popup_pane, event);

    XtManageChild( popup_pane);
}



void MenuCB( Widget w, caddr_t client_data, caddr_t call_data)
{
    int ret, make_huge( );

    if ((long)client_data != MENU_OTHER_WINDOW) {

        if (mode == 1)
            XtRemoveEventHandler( small_w, XtAllEvents, True, (XtEventHandler) PostPopup, popup_pane1);
        if (mode == 2)
            XtRemoveEventHandler( small_w, XtAllEvents, True, (XtEventHandler) PostPopup, popup_pane2);
        if (mode == 3)
            XtRemoveEventHandler( small_w, XtAllEvents, True, (XtEventHandler) PostPopup, popup_pane3);


        XSetForeground( display, gc, g_vc.white_pixel);

        switch ((long)client_data) {
        case MENU_ZOOM_X4:
            factor = 4;
            XDrawString( display, XtWindow( small_w), gc, 0, 20, "Wait...", 7);
            ret = make_huge( );
            XDrawString( display, XtWindow( small_w), gc, 0, 40, "OK", 2);
            if (ret == 0) {
                mode = 2;
                XtAddEventHandler( small_w, ButtonPressMask, False, (XtEventHandler) PostPopup, popup_pane2);
            } else {
                mode = 1;
                XtAddEventHandler( small_w, ButtonPressMask, False, (XtEventHandler) PostPopup, popup_pane1);
            }
            break;
        case MENU_ZOOM_X2:
            factor = 2;
            XDrawString( display, XtWindow( small_w), gc, 0, 20, "Wait...", 7);
            ret = make_huge( );
            XDrawString( display, XtWindow( small_w), gc, 0, 40, "OK", 2);
            if (ret == 0) {
                mode = 3;
                XtAddEventHandler( small_w, ButtonPressMask, False, (XtEventHandler) PostPopup, popup_pane3);
            } else {
                mode = 1;
                XtAddEventHandler( small_w, ButtonPressMask, False, (XtEventHandler) PostPopup, popup_pane1);
            }
            break;
        case MENU_ZOOM_OFF:
            factor = 0;
            fprintf( stderr, "%s\n", "Freeing memory for zoom...");
            XFree( hugeimage);
            free( lotofpixels);
            lotofpixels = NULL;
            mode = 1;
            XSetForeground( display, gc, blackpix);
            XFillRectangle( display, XtWindow( small_w), gc, 0, 0, 64, 64);
            XtAddEventHandler( small_w, ButtonPressMask, False, (XtEventHandler) PostPopup, popup_pane1);
            if (zoomshell) {
                XtDestroyWidget( zoomshell);
                zoomshell = NULL;
            }
            break;
        }
    } else {

        if (zoomshell == NULL) {

            zoomshell = XtVaAppCreateShell( "Zoom", "zoomshell", topLevelShellWidgetClass, XtDisplay( toplevel), NULL);
            XtAddCallback( zoomshell, XtNdestroyCallback, (XtCallbackProc) destroy_zoomshell, NULL);

            zoom_w =
             XtVaCreateManagedWidget( "zoom_w",
                                     xmDrawingAreaWidgetClass,
                                     zoomshell,
                                     XmNbackground, blackpix,
                                     XmNforeground, whitepix,
                                     XmNwidth, zoom_width,
                                     XmNheight, zoom_height, XmNtranslations, XtParseTranslationTable( zoom_translations), NULL);

            /*
             ** Add actions to the Translation manager 
             */
            XtAppAddActions( app, actions_list, XtNumber( actions_list));
            XFillRectangle( display, XtWindow( small_w), gc, 0, 0, 64, 64);
            XtRealizeWidget( zoomshell);
            set_colormap( zoomshell);
        } else
            XMapRaised( display, XtWindow( zoomshell));
    }
}

int make_huge( )
{
    /* Making huge image for zoom */
    int i, j, k, l;
    int ret;
    XImage *image;
    unsigned long pixel;

    if (factor == 0)
        return (0);

    image = XGetImage( display, pixmap, 0, 0, cols * sizex, rows * sizey, -1, ZPixmap);

    if (lotofpixels != NULL) {
        /*
           fprintf( stderr, "%s\n", "Freeing memory for zoom...");
         */
        XFree( hugeimage);
        free( lotofpixels);
    }

    lotofpixels = (char *)malloc( factor * factor * cols * sizex * rows * sizey * sizeof( unsigned int));

    if (lotofpixels != NULL) {
        /*
           fprintf( stderr,  "%s %d %s\n", "Allocate",
           (factor*factor*cols*sizex*rows*sizey)/(1024*1024), 
           "Megabytes for zoom");
         */
        hugeimage = XCreateImage( display, g_vc.visual, g_vc.depth,
                                 ZPixmap, 0, lotofpixels, factor * cols * sizex, factor * rows * sizey, 8, 0);

        for (i = 0; i < rows * sizey; i++) {
            for (j = 0; j < cols * sizex; j++) {
                pixel = XGetPixel( image, j, i);
                for (k = 0; k < factor; k++) {
                    for (l = 0; l < factor; l++) {
                        ret = XPutPixel( hugeimage, factor * j + l, factor * i + k, pixel);
                        if (ret == 0)
                            fprintf( stderr, "%s\n", "ERRPIX");
                    }
                }
            }
        }

        return (0);
    } else {
        fprintf( stderr, "%s %d %s\n", "Can't Allocate",
                (factor * factor * cols * sizex * rows * sizey) / (1024 * 1024), "Megabytes for zoom !");

        return (-1);
    }
}

void average_n( float *startf, float *sstartf, int n, int xmin, int xmax, int ymin, int ymax)
{
    int x, y;
    float tested = 0.0;

    for (y = ymin; y <= ymax; y++) {
        for (x = xmin; x <= xmax; x++) {
            switch (n) {
            case 1:
                tested = *startf;
                break;
            case 2:
                tested = *startf + *(startf + isize);
                break;
            case 3:
                tested = *(startf - isize) + *startf + *(startf + isize);
                break;
            }

            *sstartf = tested / n;

            startf++;
            sstartf++;
        }
    }
}

void average( Widget w, XtPointer w_code, XmAnyCallbackStruct *cbs)
{
    int i, i1 = 0, i2 = 0, n = 0, curz;
    int pos[2];

    switch ((long)w_code) {
    case k_average_2:
        i1 = 0;
        i2 = Nsubs - 1;
        n = 2;
        for (i = isize * i2; i < isize * Nsubs; i++)
            sfimage[i] = Blank;

        XSetForeground( display, gc, g_vc.black_pixel);
        XFillRectangle( display, pixmap, gc, col[Nsubs - 1] * sizex, row[Nsubs - 1] * sizey, sizex, sizey);

        break;


    case k_average_3:
        i1 = 1;
        i2 = Nsubs - 1;
        n = 3;
        for (i = 0; i < isize; i++)
            sfimage[i] = Blank;
        for (i = isize * i2; i < isize * Nsubs; i++)
            sfimage[i] = Blank;

        XSetForeground( display, gc, g_vc.black_pixel);

        XFillRectangle( display, pixmap, gc, col[0] * sizex, row[0] * sizey, sizex, sizey);
        XFillRectangle( display, pixmap, gc, col[Nsubs - 1] * sizex, row[Nsubs - 1] * sizey, sizex, sizey);

        break;
    case k_none:
        i1 = 0;
        i2 = Nsubs;
        n = 1;
        break;
    }

    fimagesav = (float *)malloc( Nsubs * npix[0] * npix[1] * sizeof( float));
    for (i = 0; i < Nsubs * npix[0] * npix[1]; i++)
        fimagesav[i] = fimage[i];

    for (i = i1; i < i2; i++) {
        average_n( &fimagesav[isize * i], &sfimage[isize * i], n, 1, npix[0], 1, npix[1]);

        AffImage( &sfimage[isize * i], pixmap, sizex, sizey,
                 1, npix[0] / incr, 1, npix[1] / incr,
                 zoom, zoom, clip[0], clip[1], incr, 0, 0, col[i] * sizex, row[i] * sizey, sizex, sizey);

    }

    XSetForeground( display, gc, g_vc.white_pixel);

    for (i = 0; i < cols; i++)
        XDrawLine( display, pixmap, gc, sizex * i, 0, sizex * i, rows * sizey - 1);
    for (i = 0; i < rows; i++)
        XDrawLine( display, pixmap, gc, 0, sizey * i, cols * sizex - 1, sizey * i);

    redraw( XtWindow( radec));

    if (subpixmap[0] != 0) {
        for (i = 0; i < Nsubs; i++) {
            AffImage( &sfimage[isize * i], subpixmap[i], zsizex, zsizey,
                     1, npix[0], 1, npix[1], localzoom, localzoom, clip[0], clip[1], 1, 0, 0, 0, 0, zsizex, zsizey);
        }
    }

    if (Nsubs >= 2) {
        curz = GET_CURZ();

        if (subpixmap[0] != 0)
            XCopyArea( display, subpixmap[curz], subsetpixmap, gc, 0, 0, zsizex, zsizey, 0, 0);
        else
            XCopyArea( display,
                      pixmap,
                      subsetpixmap,
                      gc,
                      POSX(0, curz), POSY(voffset, curz),
                      sizex, sizey, (maxspectrumwidth - sizex) / 2, (maxspectrumheight - sizey) / 2);

        XCopyArea( display, subsetpixmap, XtWindow( subset), gc, 0, 0, zsizex, zsizey, 0, 0);

        redraw_zcross( subset);

        redraws_slice_in_subset( );

    }

    free( fimagesav);

    if (Nsubs >= 2) {
        get_crosspos( pos);
        spec_modify( pos[0], pos[1]);
    }

    slice_after_smooth( );
    force_tics( );
}

void smooth_w( float *startf, float *sstartf, float *weight, int xmin, int xmax, int ymin, int ymax)
{
    int x, y;
    float tested;
    float s00, s10, s11, s20, s21, s22, a00, a10, a11, a20, a21, a22;
    float sum;

    s00 = weight[0];
    a00 = (float)fabs( (double)s00);
    s10 = weight[1];
    a10 = (float)fabs( (double)s10);
    s11 = weight[2];
    a11 = (float)fabs( (double)s11);
    s20 = weight[3];
    a20 = (float)fabs( (double)s20);
    s21 = weight[4];
    a21 = (float)fabs( (double)s21);
    s22 = weight[5];
    a22 = (float)fabs( (double)s22);

    sum = a00 + 4.0 * (a10 + a11 + a20 + 2.0 * a21 + a22);

    for (y = ymin; y <= ymax; y++) {
        for (x = xmin; x <= xmax; x++) {
            if ((x <= 2) || (x > xmax - 2) || (y <= 2) || (y > ymax - 2)) {
                tested = Blank;
            } else {
                tested = s00 * (*startf)
                 + s11 * (*(startf + xmax - 1) + *(startf + xmax + 1) + *(startf - xmax - 1) + *(startf - xmax + 1))
                 + s10 * (*(startf + xmax) + *(startf + 1) + *(startf - 1) + *(startf - xmax))
                 + s22 * (*(startf + 2 * xmax - 2) + *(startf + 2 * xmax + 2) + *(startf - 2 * xmax - 2) + *(startf - 2 * xmax + 2))
                 + s21 * (*(startf + 2 * xmax - 1) +
                          *(startf + 2 * xmax + 1) +
                          *(startf + xmax + 2) +
                          *(startf - xmax + 2) +
                          *(startf - 2 * xmax + 1) + *(startf - 2 * xmax - 1) + *(startf - xmax - 2) + *(startf + xmax - 2))
                 + s20 * (*(startf + 2 * xmax) + *(startf + 2) + *(startf - 2 * xmax) + *(startf - 2));

                tested = tested / sum;
            }

            *sstartf = tested;

            startf++;
            sstartf++;
        }
    }
}

void smooth_with_blanks( float *startf, float *weight, int n)
{
    int i, x, y;
    float tested;
    float s00, s10, s11, s20, s21, s22, a00, a10, a11, a20, a21, a22;
    float *weightfimage;

    weightfimage = (float *)malloc( npix[0] * npix[1] * sizeof( float));

    s00 = weight[0];
    a00 = (float)fabs( (double)s00);
    s10 = weight[1];
    a10 = (float)fabs( (double)s10);
    s11 = weight[2];
    a11 = (float)fabs( (double)s11);
    s20 = weight[3];
    a20 = (float)fabs( (double)s20);
    s21 = weight[4];
    a21 = (float)fabs( (double)s21);
    s22 = weight[5];
    a22 = (float)fabs( (double)s22);

    for (i = 0; i < npix[0] * npix[1]; i++)
        weightfimage[i] = 0.;

    for (y = 0; y < npix[1]; y++) {
        for (x = 0; x < npix[0]; x++) {
            if ((x < 2) || (x >= npix[0] - 2) || (y < 2) || (y >= npix[1] - 2)) {
                sfimage[isize * n + x + npix[0] * y] = Blank;
            } else {
                tested = *startf;
                if ((float)fabs( (double)(tested - Blank)) > Tolerance) {
                    /* Central Pixel */
                    sfimage[isize * n + x + npix[0] * y] = sfimage[isize * n + x + npix[0] * y] + s00 * tested;
                    weightfimage[x + npix[0] * y] = weightfimage[x + npix[0] * y] + a00;

                    /* Second pixels */
                    sfimage[isize * n + x - 1 + npix[0] * (y - 1)] = sfimage[isize * n + x - 1 + npix[0] * (y - 1)] + s11 * tested;
                    sfimage[isize * n + x - 1 + npix[0] * y] = sfimage[isize * n + x - 1 + npix[0] * y] + s10 * tested;
                    sfimage[isize * n + x - 1 + npix[0] * (y + 1)] = sfimage[isize * n + x - 1 + npix[0] * (y + 1)] + s11 * tested;
                    sfimage[isize * n + x + npix[0] * (y - 1)] = sfimage[isize * n + x + npix[0] * (y - 1)] + s10 * tested;
                    sfimage[isize * n + x + npix[0] * (y + 1)] = sfimage[isize * n + x + npix[0] * (y + 1)] + s10 * tested;
                    sfimage[isize * n + x + 1 + npix[0] * (y - 1)] = sfimage[isize * n + x + 1 + npix[0] * (y - 1)] + s11 * tested;
                    sfimage[isize * n + x + 1 + npix[0] * y] = sfimage[isize * n + x + 1 + npix[0] * y] + s10 * tested;
                    sfimage[isize * n + x + 1 + npix[0] * (y + 1)] = sfimage[isize * n + x + 1 + npix[0] * (y + 1)] + s11 * tested;

                    /* and weights */
                    weightfimage[x - 1 + npix[0] * (y - 1)] = weightfimage[x - 1 + npix[0] * (y - 1)] + a11;
                    weightfimage[x - 1 + npix[0] * y] = weightfimage[x - 1 + npix[0] * y] + a10;
                    weightfimage[x - 1 + npix[0] * (y + 1)] = weightfimage[x - 1 + npix[0] * (y + 1)] + a11;
                    weightfimage[x + npix[0] * (y - 1)] = weightfimage[x + npix[0] * (y - 1)] + a10;
                    weightfimage[x + npix[0] * (y + 1)] = weightfimage[x + npix[0] * (y + 1)] + a10;
                    weightfimage[x + 1 + npix[0] * (y - 1)] = weightfimage[x + 1 + npix[0] * (y - 1)] + a11;
                    weightfimage[x + 1 + npix[0] * y] = weightfimage[x + 1 + npix[0] * y] + a10;
                    weightfimage[x + 1 + npix[0] * (y + 1)] = weightfimage[x + 1 + npix[0] * (y + 1)] + a11;


                    /* Third pixels */
                    sfimage[isize * n + x - 2 + npix[0] * (y - 2)] = sfimage[isize * n + x - 2 + npix[0] * (y - 2)] + s22 * tested;
                    sfimage[isize * n + x - 2 + npix[0] * (y - 1)] = sfimage[isize * n + x - 2 + npix[0] * (y - 1)] + s21 * tested;
                    sfimage[isize * n + x - 2 + npix[0] * y] = sfimage[isize * n + x - 2 + npix[0] * y] + s20 * tested;
                    sfimage[isize * n + x - 2 + npix[0] * (y + 1)] = sfimage[isize * n + x - 2 + npix[0] * (y + 1)] + s21 * tested;
                    sfimage[isize * n + x - 2 + npix[0] * (y + 2)] = sfimage[isize * n + x - 2 + npix[0] * (y + 2)] + s22 * tested;
                    sfimage[isize * n + x - 1 + npix[0] * (y - 2)] = sfimage[isize * n + x - 1 + npix[0] * (y - 2)] + s21 * tested;
                    sfimage[isize * n + x - 1 + npix[0] * (y + 2)] = sfimage[isize * n + x - 1 + npix[0] * (y + 2)] + s21 * tested;
                    sfimage[isize * n + x + npix[0] * (y - 2)] = sfimage[isize * n + x + npix[0] * (y - 2)] + s20 * tested;
                    sfimage[isize * n + x + npix[0] * (y + 2)] = sfimage[isize * n + x + npix[0] * (y + 2)] + s20 * tested;
                    sfimage[isize * n + x + 1 + npix[0] * (y - 2)] = sfimage[isize * n + x + 1 + npix[0] * (y - 2)] + s21 * tested;
                    sfimage[isize * n + x + 1 + npix[0] * (y + 2)] = sfimage[isize * n + x + 1 + npix[0] * (y + 2)] + s21 * tested;
                    sfimage[isize * n + x + 2 + npix[0] * (y - 2)] = sfimage[isize * n + x + 2 + npix[0] * (y - 2)] + s22 * tested;
                    sfimage[isize * n + x + 2 + npix[0] * (y - 1)] = sfimage[isize * n + x + 2 + npix[0] * (y - 1)] + s21 * tested;
                    sfimage[isize * n + x + 2 + npix[0] * y] = sfimage[isize * n + x + 2 + npix[0] * y] + s20 * tested;
                    sfimage[isize * n + x + 2 + npix[0] * (y + 1)] = sfimage[isize * n + x + 2 + npix[0] * (y + 1)] + s21 * tested;
                    sfimage[isize * n + x + 2 + npix[0] * (y + 2)] = sfimage[isize * n + x + 2 + npix[0] * (y + 2)] + s22 * tested;

                    /* and weights */
                    weightfimage[x - 2 + npix[0] * (y - 2)] = weightfimage[x - 2 + npix[0] * (y - 2)] + a22;
                    weightfimage[x - 2 + npix[0] * (y - 1)] = weightfimage[x - 2 + npix[0] * (y - 1)] + a21;
                    weightfimage[x - 2 + npix[0] * y] = weightfimage[x - 2 + npix[0] * y] + a20;
                    weightfimage[x - 2 + npix[0] * (y + 1)] = weightfimage[x - 2 + npix[0] * (y + 1)] + a21;
                    weightfimage[x - 2 + npix[0] * (y + 2)] = weightfimage[x - 2 + npix[0] * (y + 2)] + a22;
                    weightfimage[x - 1 + npix[0] * (y - 2)] = weightfimage[x - 1 + npix[0] * (y - 2)] + a21;
                    weightfimage[x - 1 + npix[0] * (y + 2)] = weightfimage[x - 1 + npix[0] * (y + 2)] + a21;
                    weightfimage[x + npix[0] * (y - 2)] = weightfimage[x + npix[0] * (y - 2)] + a20;
                    weightfimage[x + npix[0] * (y + 2)] = weightfimage[x + npix[0] * (y + 2)] + a20;
                    weightfimage[x + 1 + npix[0] * (y - 2)] = weightfimage[x + 1 + npix[0] * (y - 2)] + a21;
                    weightfimage[x + 1 + npix[0] * (y + 2)] = weightfimage[x + 1 + npix[0] * (y + 2)] + a21;
                    weightfimage[x + 2 + npix[0] * (y - 2)] = weightfimage[x + 2 + npix[0] * (y - 2)] + a22;
                    weightfimage[x + 2 + npix[0] * (y - 1)] = weightfimage[x + 2 + npix[0] * (y - 1)] + a21;
                    weightfimage[x + 2 + npix[0] * y] = weightfimage[x + 2 + npix[0] * y] + a20;
                    weightfimage[x + 2 + npix[0] * (y + 1)] = weightfimage[x + 2 + npix[0] * (y + 1)] + a21;
                    weightfimage[x + 2 + npix[0] * (y + 2)] = weightfimage[x + 2 + npix[0] * (y + 2)] + a22;
                }
            }
            startf++;
        }
    }
    for (y = 2; y < npix[1] - 2; y++) {
        for (x = 2; x < npix[0] - 2; x++) {
            if (weightfimage[x + npix[0] * y] == 0)
                sfimage[isize * n + x + npix[0] * y] = Blank;
            else
                sfimage[isize * n + x + npix[0] * y] = sfimage[isize * n + x + npix[0] * y] / weightfimage[x + npix[0] * y];
        }
    }
    free( weightfimage);
}



void smooth( Widget w, XtPointer w_code, XmAnyCallbackStruct *cbs)
{
    int i, curz;
    int pos[2];
    float weight[6];
    float fact;

    fimagesav = (float *)malloc( Nsubs * npix[0] * npix[1] * sizeof( float));
    for (i = 0; i < Nsubs * npix[0] * npix[1]; i++)
        fimagesav[i] = fimage[i];

    switch ((long)w_code) {
    case k_hanning:
        weight[0] = 3.;
        weight[1] = 2.;
        weight[2] = 2.;
        weight[3] = 1.;
        weight[4] = 1.;
        weight[5] = 1.;
        break;
    case k_gauss:
        fact = -(1.665109 / 3.) * (1.665109 / 3.);
        weight[0] = 1.;
        weight[1] = (float)exp( (double)fact);
        weight[2] = (float)exp( (double)2. * fact);
        weight[3] = (float)exp( (double)4. * fact);
        weight[4] = (float)exp( (double)5. * fact);
        weight[5] = (float)exp( (double)16. * fact);
        break;
    case k_box:
        weight[0] = 1.;
        weight[1] = 1.;
        weight[2] = 1.;
        weight[3] = 1.;
        weight[4] = 1.;
        weight[5] = 1.;
        break;

    }

/*   if (Tolerance >= 0) { */

/*     for (i = 0; i<Nsubs*npix[0]*npix[1]; i++) { */

/*       sfimage[i] = 0.; */

/*     } */

/*   } */
    for (i = 0; i < Nsubs; i++) {
        if (Tolerance < 0)
            smooth_w( &fimagesav[isize * i], &sfimage[isize * i], &weight[0], 1, npix[0], 1, npix[1]);
        else {
            smooth_with_blanks( &fimagesav[isize * i], &weight[0], i);
        }
        AffImage( &sfimage[isize * i], pixmap, sizex, sizey,
                 1, npix[0] / incr, 1, npix[1] / incr,
                 zoom, zoom, clip[0], clip[1], incr, 0, 0, col[i] * sizex, row[i] * sizey, sizex, sizey);
    }

    for (i = 0; i < cols; i++)
        XDrawLine( display, pixmap, gc, sizex * i, 0, sizex * i, rows * sizey - 1);
    for (i = 0; i < rows; i++)
        XDrawLine( display, pixmap, gc, 0, sizey * i, cols * sizex - 1, sizey * i);

    redraw( XtWindow( radec));

    if (subpixmap[0] != 0) {
        for (i = 0; i < Nsubs; i++) {
            AffImage( &sfimage[isize * i], subpixmap[i], zsizex, zsizey,
                     1, npix[0], 1, npix[1], localzoom, localzoom, clip[0], clip[1], 1, 0, 0, 0, 0, zsizex, zsizey);
        }
    }

    if (Nsubs >= 2) {
        curz = GET_CURZ();

        if (subpixmap[0] != 0)
            XCopyArea( display, subpixmap[curz], subsetpixmap, gc, 0, 0, zsizex, zsizey, 0, 0);
        else
            XCopyArea( display,
                      pixmap,
                      subsetpixmap,
                      gc,
                      POSX(0, curz), POSY(voffset, curz),
                      sizex, sizey, (maxspectrumwidth - sizex) / 2, (maxspectrumheight - sizey) / 2);

        XCopyArea( display, subsetpixmap, XtWindow( subset), gc, 0, 0, zsizex, zsizey, 0, 0);

        redraw_zcross( subset);
        redraws_slice_in_subset( );

    }

    free( fimagesav);

    if (Nsubs >= 2) {
        get_crosspos( pos);
        spec_modify( pos[0], pos[1]);
    }

    slice_after_smooth( );
    force_tics( );
}

void UpdateColorChanged( )
{
    int i, curz;

    for (i = 0; i < Nsubs; i++) {
        AffImage( &fimage[isize * i], XtWindow( radec), sizex, sizey,
                 1, npix[0] / incr, 1, npix[1] / incr,
                 zoom, zoom, clip[0], clip[1], incr, 0, 0, col[i] * sizex, row[i] * sizey, sizex, sizey);
    }
    if (Nsubs >= 2) {
        curz = GET_CURZ();
        if (subpixmap[0] != 0)
            AffImage( &sfimage[isize * curz], XtWindow( subset), zsizex, zsizey,
                     1, npix[0], 1, npix[1], localzoom, localzoom, clip[0], clip[1], 1, 0, 0, 0, 0, zsizex, zsizey);
        else
            AffImage( &sfimage[isize * curz], XtWindow( subset), zsizex, zsizey,
                     1, npix[0], 1, npix[1],
                     localzoom, localzoom, clip[0], clip[1],
                     1, POSX(0, curz), POSY(voffset, curz), (maxspectrumwidth - sizex) / 2, (maxspectrumheight - sizey) / 2, sizex,
                     sizey);

    }
}

