/*
 * $Id: transferRGBBox.cxx,v 4.0 2003/04/28 14:40:15 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.
 * */

#include <FL/math.h>

#include "imview.hxx"
#include "imageIO.hxx"
#include "imageViewer.hxx"
#include "menubar.hxx"
#include "transferFunction.hxx"
#include "transferRGBFunction.hxx"
#include "transferBox.hxx"

extern imageIO *IOBlackBox;
extern imageViewer *mainViewer;
extern imViewMenuBar *mainMenuBar;
extern transfer  *transferEditorPanel;
extern transferRGB *transferRGBEditorPanel;


myTransferRGBBox::~myTransferRGBBox()
{
    dbgprintf("Transfer Box destroyed");
    return;
}

void myTransferRGBBox::draw()
{
    draw_box();
    draw_label();

    // the superclass actually does no further drawing

    return;
}


void myTransferRGBBox::computeRGBEndPoints(double x0[3], double y0[3], double x1[3], double y1[3])
{
    double dx, dy;
    
    for (int i = 0 ; i < 3 ; ++i) {
	// OK, draw the transfer function now!
	if (currContrast[i] >= -0.5 && currContrast[i] <= 0.5) {
	    dy = tan(currContrast[i]*M_PI/2);
	    if (dy != 0) {
		x0[i] = currBrightness[i] - 0.5/dy;
		x1[i] = 0.5/dy + currBrightness[i];
	    } else {
		x0[i] = 0;
		x1[i] = 1;
	    }
	    y0[i] = 0.5 - dy*currBrightness[i];
	    y1[i] = 0.5 + dy*(1-currBrightness[i]);
	} else {
	    dx = 1/tan(currContrast[i]*M_PI/2);
	    dbgprintf("Contrast[%d]=%g, dx= %g\n", i, currContrast[i], dx);
	    if (fabs(dx) >= 1e-7) {
		y0[i] = 0.5 - currBrightness[i]/dx;
		y1[i] = (1-currBrightness[i])/dx +0.5;
	    } else {
		if (dx >= 0) {
		    y0[i] = -2;
		    y1[i] = 2;
		} else {
		    y0[i] = 200;
		    y1[i] = -200;
		}
	    }
	    x0[i] = currBrightness[i] - 0.5*dx;
	    x1[i] = currBrightness[i] + 0.5*dx;
	}
    }
}

// distinct lack of comments there.
// I'm guessing this computes the cryptic `left,right,top,bottom' values
// found in the dialog
void myTransferRGBBox::findRGBExtremes(double x0[3], double y0[3], double x1[3], double y1[3],
				       double startX[3], double startY[3],
				       double endX[3], double endY[3])
{
    // the values that will end up in the input widgets
    double    swapd;

    for (int i = 0 ; i < 3 ; ++i) {
	// see if the contrast is positive or negative:
	if (x0[i] > x1[i] || y0[i] > y1[i]) { // negative
	    swapd = x0[i];
	    x0[i] = x1[i];
	    x1[i] = swapd;
	    if (y0[i] > 1) {
		startX[i] = x0[i];
		startY[i] = 1.0;
	    } else {
		startX[i] = 0.0;
		startY[i] = y0[i];
	    }
	    if (y1 > 0) {
		endX[i] = 1.0;
		endY[i] = y1[i];
	    } else {
		endX[i] = x1[i];
		endY[i] = 0.0;
	    }
	} else { // positive
	    if (y0[i] < 0)  {
		startX[i] = x0[i];
		startY[i] = 0.0;
	    } else {
		startX[i] = 0.0;
		startY[i] = y0[i];
	    }
	    if (y1[i] <= 1) {
		endX[i] = 1.0;
		endY[i] = y1[i];
	    } else {
		endX[i] = x1[i];
		endY[i] = 1.0;
	    }
	}
    }

    return;	   
}

// fills up the RGBmap with the transfer function based on the given
// parameters
void myTransferRGBBox::buildRGBFunction(double x0[3], double y0[3], double x1[3], double y1[3])
{
    // the values that will end up in the input widgets
    int       startX[3], startY[3], endX[3], endY[3];
    double    swapd;
    const int mininval = 0, maxinval = 255;
    const int minoutval = 0, maxoutval = 255;
    int       j;

    for (int i = 0 ; i < 3 ; ++i) { 
	// see if the contrast is positive or negative:
	if (x0[i] > x1[i] || y0[i] > y1[i]) { // negative
	    swapd = x0[i];
	    x0[i] = x1[i];
	    x1[i] = swapd;
	    if (y0[i] > 1) {
		startX[i] = (int)(x0[i] * maxinval);
		startY[i] = maxoutval;
	    } else {
		startX[i] = mininval;
		startY[i] = (int)(y0[i] * maxoutval);
	    }
	    if (y1 > 0) {
		endX[i] = maxinval;
		endY[i] = (int)(y1[i] * maxoutval);
	    } else {
		endX[i] = (int)(x1[i] * maxinval);
		endY[i] = 0;
	    }
	    for (j = mininval ; j < startX[i]; ++j)
		RGBmap[i][j] = maxoutval;

	    int h0 = endY[i] - startY[i];
	    int w0 = endX[i] - startX[i];
	    for (j = startX[i] ; j < endX[i] ; ++j) {
		RGBmap[i][j] = (unsigned char)(startY[i] + h0*pow(((double)j-startX[i])/w0,1/currGamma[i]));
	    }

	    for (j = endX[i] ; j <= maxinval ; ++j) {
		RGBmap[i][j] = minoutval;
	    }
	} else { // contrast is positive
	    if (y0[i] < 0)  {
		startX[i] = (int)(x0[i] * maxinval);
		startY[i] = 0;
	    } else {
		startX[i] = mininval;
		startY[i] = (int)(y0[i] * maxoutval);
	    }
	    if (y1[i] <= 1) {
		endX[i] = maxinval;
		endY[i] = (int)(y1[i] * maxoutval);
	    } else {
		endX[i] = (int)(x1[i] * maxinval);
		endY[i] = maxoutval;
	    }
	    for (j = mininval ; j < startX[i]; ++j)
		RGBmap[i][j] = minoutval;

	    int h0 = endY[i] - startY[i];
	    int w0 = endX[i] - startX[i];
	    for (j = startX[i] ; j < endX[i] ; ++j) {
		RGBmap[i][j] = (unsigned char)(startY[i] + h0*pow(((double)j-startX[i])/w0,1/currGamma[i]));
	    }

	    for (j = endX[i] ; j <= maxinval ; ++j) {
		RGBmap[i][j] = maxoutval;
	    }
	}
    }
    
    if (hasReleaseOccured) {
	causeMainWindowRedraw();
	hasReleaseOccured = false;
    }

    
    if (transferRGBEditorPanel) {
	// NOTE : these need to accept 3-vectors
	transferRGBEditorPanel->setTopValue(endX);
	transferRGBEditorPanel->setBottomValue(startX);
	transferRGBEditorPanel->setLeftValue(startY);
	transferRGBEditorPanel->setRightValue(endY);
    }

    
    return;	     
}

// this is a very slow call, so we don't want to do it
// all the time...
void myTransferRGBBox::causeMainWindowRedraw()
{
    // this might be very slow...
    dbgprintf("Really redrawing the image\n");
    
    // a million parameters there...
    IOBlackBox->saveGammaParameters(leftX, leftY, rightX, rightY, gammas,
				    RGBmap[0], RGBmap[1], RGBmap[2]);
    
     // get the current image
    IMAGEPARM *p  = (IMAGEPARM *)mainMenuBar->getSelectedItemArgument(IMAGE_LIST_ID);

    if ((p != 0) && (transferRGBEditorPanel != 0)) {
	// save the gamma, etc
	dbgprintf("Attempting to save the transfer functions parameters\n");
	transferRGBEditorPanel->getParameters(p->Rgamma, p->Ggamma, p->Bgamma,
					      p->Rcontrast, p->Gcontrast, p->Bcontrast,
					      p->Rbrightness, p->Gbrightness, p->Bbrightness);
	dbgprintf("%s got: Rgamma=%g, Rcontrast=%g, Rbrightness=%g\n",
		  p->itempath,
		  p->Rgamma, p->Rcontrast, p->Rbrightness);
    }
    // this re-reads all the parameters, etc.
    IOBlackBox->applyImageParameters(p, 0, 0, true);
    mainViewer->displayCurrentImage(); // this can be quite slow as well...
    trueRedrawHook();

    // and don't forget myself...
    redraw();
}



void myTransferRGBBox::setRGBParms(double c[3], double b[3], double g[3])
{
    double x0[3], y0[3], x1[3], y1[3];
    double startX[3], startY[3], endX[3], endY[3];
	int i;
    
    for (i = 0 ; i < 3 ; ++i) {
	currContrast[i] = (c[i] < -1.0) ? -1.0:c[i];
	currBrightness[i] = b[i];
	currGamma[i] = g[i];
    }
    
    computeRGBEndPoints(x0, y0, x1, y1);

    findRGBExtremes(x0, y0, x1, y1,
		    startX, startY,
		    endX, endY);
    
    for (i = 0 ; i < 3 ; ++i) {
	gammas[i] = g[i];
	leftX[i] = startX[i];
	leftY[i] = startY[i];
	rightX[i] = endX[i];
	rightY[i] = endY[i];
    }

    // this will update the image if need be,
    // and will put the right numbers in the input widget fields
    buildRGBFunction(x0, y0, x1, y1);
    
    redraw();

    return;
}
