/*
 * $Id: profileBox.cxx,v 4.0 2003/04/28 14:39:57 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/*------------------------------------------------------------------------
 *
 * The code for the profile display panel
 *
 * Hugues Talbot	 1 Aug 1998
 *      
 *-----------------------------------------------------------------------*/

#include <string.h>
#include "imview.hxx"
#include "imageIO.hxx"
#include "imageViewer.hxx"
#include "profileBox.hxx"
#include "saveSpect.hxx"
#include "printSpect.hxx"

extern imageViewer      *mainViewer;
extern savespect        *saveSpectPanel;
extern printspect       *printSpectPanel;

void myProfileBox::setProfiles(int startX,
			       int startY,
			       int endX,
			       int endY,
                               double *theProfileR,
			       double *theProfileG,
			       double *theProfileB,
			       int nbval)
{
    if (theProfileR != 0) {
	if (profileR) delete[] profileR;
	if (profileG) delete[] profileG;
	if (profileB) delete[] profileB;
	// R will be either Red or Grey
	profileR = new double[nbval];
	memcpy(profileR, theProfileR, nbval*sizeof(double));

	if (theProfileG) {
	    profileG = new double[nbval];
	    memcpy(profileG, theProfileG, nbval*sizeof(double));
	} else {
	    profileG = 0;
	}
	if (theProfileB) {
	    profileB = new double[nbval];
	    memcpy(profileB, theProfileB, nbval*sizeof(double));
	} else {
	    profileB = 0;
	}
	nbProfileValues = nbval;
	sx = startX ; sy = startY;
	ex = endX; ey = endY;
	recomputeLimits();
    } else {
	if (profileR) delete[] profileR;
	if (profileG) delete[] profileG;
	if (profileB) delete[] profileB;
	
	profileR = profileG = profileB = 0;
	nbProfileValues = 0;
    }

    return;
}

void myProfileBox::recomputeLimits(void)
{
    if (profileR) {
	if (!absolute)
	    mins = maxs = profileR[0];
	// else they have already been set to 0
	for (int i = 1 ; i < nbProfileValues ; i++) {
	    if (profileR[i] > maxs) maxs = profileR[i];
	    if (profileR[i] < mins) mins = profileR[i];
	}
	// same for G and B if they exist
	if (profileG) {
	    for (int i = 1 ; i < nbProfileValues ; i++) {
		if (profileG[i] > maxs) maxs = profileG[i];
		if (profileG[i] < mins) mins = profileG[i];
	    }
	}
	if (profileB) {
	    for (int i = 1 ; i < nbProfileValues ; i++) {
		if (profileB[i] > maxs) maxs = profileB[i];
		if (profileB[i] < mins) mins = profileB[i];
	    }
	}
	// special case of the char values.
	if (absolute && (mins >= 0.0) && (maxs <= 255.0)) {
	    mins = 0.0;
	    maxs = 255.0;
	}
	// what if they are both equal?
	if (maxs == mins)
	    maxs = 1.0;
	if (theProfile) {
	    theProfile->setAxisBoxesLimits(0, nbProfileValues,
					   mins, maxs);
	}
    }

    return;
}

int myProfileBox::handle(int event)
{
    int retval = 0;
    int button;

    if (profileR) {
	button = Fl::event_button();
	switch (event) {
	  case FL_PUSH:
	    handleButtonPushed();
	    retval = 1;
	    break;

	  case FL_DRAG:
	    handleButtonDragged();
	    retval = 1;
	    break;

	  case FL_RELEASE:
	    handleButtonReleased();
	    retval = 1;
	    break;

	  case FL_LEAVE:
	    // Return to normal cursor
	    fl_cursor(FL_CURSOR_DEFAULT);
	    retval = 1;
	    break;
	    
	  default:
	    retval = 0;
	    break;
	}
    }
    
    return retval;
}

void myProfileBox::handleButtonPushed()
{

    fl_cursor((Fl_Cursor)MY_CURSOR_VLINE);
    handleButtonDragged();
    
    return;
}

void myProfileBox::handleButtonDragged()
{
    int index;
    int xx = Fl::event_x();
    int realwidth = w() - WIDTHMARGIN;
    
    index = (int)(((double)xx-x()-WIDTHMARGIN/2)*(nbProfileValues-1)/realwidth + 0.5);
    if ((index >= 0) && (index < nbProfileValues) && theProfile) {
	theProfile->setXValue((int)(sx + (double)index*(ex-sx)/nbProfileValues));
	theProfile->setYValue((int)(sy + (double)index*(ey-sy)/nbProfileValues));
	theProfile->setRValue(profileR[index]);
	if (profileG)
	    theProfile->setGValue(profileG[index]);
	else
	    theProfile->setGValue(profileR[index]);
	if (profileB)
	    theProfile->setBValue(profileB[index]);
	else
	    theProfile->setBValue(profileR[index]);
    } 
    return;
}

void myProfileBox::handleButtonReleased()
{
    fl_cursor(FL_CURSOR_DEFAULT);

    return;
}

void myProfileBox::draw()
{
    int xa, ya, xb, yb;
    int realwidth = w() - WIDTHMARGIN;
    int realheight = h() - HEIGHTMARGIN;

    // superclass draw:
    draw_box();
    draw_label();

    fl_clip(x(),y(),w(),h());

    if (profileR != 0) {
	if (profileG != 0)
	    fl_color(FL_RED);
	else
	    fl_color(FL_BLACK);
	for (int i = 1 ; i < nbProfileValues ; i++) {
	    xa = (int)(((i-1)*realwidth)/(nbProfileValues-1) + WIDTHMARGIN/2 + 0.5);
	    ya = (int)(((1.0 - (profileR[i-1]-mins)/(maxs-mins))*realheight) + HEIGHTMARGIN/2 + 0.5);
	    xb = (int)((i*realwidth)/(nbProfileValues - 1) + WIDTHMARGIN/2 + 0.5);
	    yb = (int)(((1.0 - (profileR[i]-mins)/(maxs-mins))*realheight) + HEIGHTMARGIN/2 +0.5);
	    fl_line(xa+x(), ya+y(), xb+x(), yb+y());
	}
    }
    
    if (profileG != 0) {
	fl_color(FL_GREEN);
	for (int i = 1 ; i < nbProfileValues ; i++) {
	    xa = (int)(((i-1)*realwidth)/(nbProfileValues-1) + WIDTHMARGIN/2 + 0.5);
	    ya = (int)(((1.0 - (profileG[i-1]-mins)/(maxs-mins))*realheight) + HEIGHTMARGIN/2 +0.5);
	    xb = (int)((i*realwidth)/(nbProfileValues - 1) + WIDTHMARGIN/2 + 0.5);
	    yb = (int)(((1.0 - (profileG[i]-mins)/(maxs-mins))*realheight) + HEIGHTMARGIN/2 + 0.5);
	    fl_line(xa+x(), ya+y(), xb+x(), yb+y());
	}
    }

    if (profileB != 0) {
	fl_color(FL_BLUE);
	for (int i = 1 ; i < nbProfileValues ; i++) {
	    xa = (int)(((i-1)*realwidth)/(nbProfileValues-1) + WIDTHMARGIN/2 + 0.5);
	    ya = (int)(((1.0 - (profileB[i-1]-mins)/(maxs-mins))*realheight) + HEIGHTMARGIN/2 + 0.5);
	    xb = (int)((i*realwidth)/(nbProfileValues - 1) + WIDTHMARGIN/2 + 0.5);
	    yb = (int)(((1.0 - (profileB[i]-mins)/(maxs-mins))*realheight) + HEIGHTMARGIN/2 + 0.5);
	    fl_line(xa+x(), ya+y(), xb+x(), yb+y());
	}
    }
    
    fl_pop_clip();
    return; 
}


/// The profile stuff

profile::profile()
{
    dbgprintf("Profile display dialog created\n");
    profileWindow = 0; // fluid depends on that
    profileBox = 0;
    
    return;
}

void profile::setDefaults(void)
{
    // we display relative values by default
    relative->set(); // button appearance
    profileBox->setRelative(); // set the variable correctly
    profileTitle->value("Position: undefined");
    xvalue->value("0");
    yvalue->value("0");
    // the profileBox needs to know us
    profileBox->setProfile(this);
    
    
    return;
}

// pass on the data...
void profile::setData(double *R,
		      double *G,
		      double *B,
		      int nb,
		      int startx, int starty,
		      int endx, int endy)
{
    static char buff[100];
    
    profileBox->setProfiles(startx, starty,
			    endx, endy,
			    R, G, B,
			    nb);

    sprintf(buff, "Position: xs=%d, ys=%d, xe=%d, ye=%d",
	    startx, starty,
	    endx, endy);
    xS = startx;
    yS = starty;
    xE = endx;
    yE = endy;
    profileTitle->value(buff);

    return;
}

int profile::visible()
{
    return profileWindow->visible();
}

void profile::redraw()
{
    profileBox->redraw();
    return;
}

void profile::show()
{
    profileWindow->show();
}

void profile::hide()
{
    profileWindow->hide();
}

void profile::setAxisBoxesLimits(double xminlimit, double xmaxlimit,
				 double yminlimit, double ymaxlimit)
{
    abcissaBox->setRange(xminlimit, xmaxlimit);
    ordinateBox->setRange(yminlimit, ymaxlimit);
    abcissaBox->redraw();
    ordinateBox->redraw();
}

void profile::setXValue(int xv) {
    static char a[10];
    sprintf(a, "%d", xv);
    xvalue->value(a);
}

void profile::setYValue(int yv) {
    static char a[30];
    sprintf(a, "%d", yv);
    yvalue->value(a);
}

void profile::setZValue(int zv) {
    static char a[30];
    sprintf(a, "%d", zv);
    zvalue->value(a);
}

void profile::setRValue(double Rv) {
    static char a[10];
    sprintf(a, "%g", Rv);
    Rvalue->value(a);
}

void profile::setGValue(double Gv) {
    static char a[30];
    sprintf(a, "%g", Gv);
    Gvalue->value(a);
}

void profile::setBValue(double Bv) {
    static char a[30];
    sprintf(a, "%g", Bv);
    Bvalue->value(a);
}

/// The Profile callbacks.

void relativecheck_cb(Fl_Check_Button *, profile *panel)
{
    panel->displayRelative();
    return;
}

void absolutecheck_cb(Fl_Check_Button *, profile *panel)
{
    panel->displayAbsolute();
    return;
}		      
    

void keepline_cb(Fl_Check_Button *b, profile *)
{
    if (b->value()) 
	mainViewer->keepLine();
    else
	mainViewer->removeLine();
    return;
}

void printbutton_cb(Fl_Button *, profile *)
{
    dbgprintf("Print button pressed\n");
    if (printSpectPanel == 0) {
	printSpectPanel = new printspect;
	printspect &printref = *printSpectPanel;
	// initializes the fluid-generated panel
	printspect_panel(printref);
	// do our own initialization
	printSpectPanel->setDefaults();
    }

    // show the panel
    printSpectPanel->setCallerType(PROFILEPANEL);
    printSpectPanel->show();
    
    return;
}

void savebutton_cb(Fl_Button *, profile *)
{
    dbgprintf("Save button pressed\n");
    if (saveSpectPanel == 0) {
	saveSpectPanel = new savespect;
	savespect &saveref = *saveSpectPanel;
	// initializes the fluid-generated panel
	savespect_panel(saveref);
	// do our own initialization
	saveSpectPanel->setDefaults();
    }
    // show the panel
    saveSpectPanel->setCallerType(PROFILEPANEL);
    saveSpectPanel->show();

    return;
}

void okbutton_cb(Fl_Return_Button*, profile *panel)
{
    // this is very simple...
    panel->hide();
}
