/* */
/*  Little cms - profiler construction set */
/*  Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
/* */
/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
/* */
/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
/* OF THIS SOFTWARE. */
/* */
/* */
/* This library is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU Lesser General Public */
/* License as published by the Free Software Foundation; either */
/* version 2 of the License, or (at your option) any later version. */
/* */
/* This library 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 */
/* Lesser General Public License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public */
/* License along with this library; if not, write to the Free Software */
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA */

#include "lcmsprf.h"


double cdecl _cmsxSaturate65535To255(double d);
double cdecl _cmsxSaturate255To65535(double d);


void   cdecl _cmsxClampXYZ100(LPcmsCIEXYZ xyz);


BOOL cdecl cmsxEmbedCharTarget(LPPROFILERCOMMONDATA hdr);
BOOL cdecl cmsxEmbedMatrixShaper(LPPROFILERCOMMONDATA hdr);
BOOL cdecl cmsxEmbedTextualInfo(LPPROFILERCOMMONDATA hdr);

/* ----------------------------------------------------------------- Implementation */


/* Convert from 0.0..65535.0 to 0.0..255.0 */

double _cmsxSaturate65535To255(double d)
{   
    double v;

    v = d / 257.0;

	if (v < 0)     return 0;
    if (v > 255.0) return 255.0;

    return v;
}


double _cmsxSaturate255To65535(double d)
{   
    double v;

    v = d * 257.0;

	if (v < 0)     return 0;
    if (v > 65535.0) return 65535.0;

    return v;
}



/* Cut off absurd values */

void _cmsxClampXYZ100(LPcmsCIEXYZ xyz)
{

        if (xyz->X > 199.996)           
                xyz->X = 199.996;
                
        if (xyz->Y > 199.996)           
                xyz->Y = 199.996;
        
        if (xyz->Z > 199.996)           
                xyz->Z = 199.996;
        
		 if (xyz->Y < 0)
                xyz->Y = 0;

        if (xyz->X < 0)
                xyz->X = 0;

        if (xyz->Z < 0)
                xyz->Z = 0;

}

static
int xfilelength(int fd)
{
#ifdef _MSC_VER
		return _filelength(fd);
#else
        struct stat sb;
        if (fstat(fd, &sb) < 0)
                return(-1);
        return(sb.st_size);
#endif


}


BOOL cmsxEmbedCharTarget(LPPROFILERCOMMONDATA hdr)
{
		LCMSHANDLE it8 = cmsxIT8Alloc();
		LPBYTE mem;
		size_t size, readed;
		FILE* f;
		BOOL lFreeOnExit = false;


		if (!hdr->m.Patches) {

			if (!hdr ->ReferenceSheet[0] && !hdr->MeasurementSheet[0]) return false;

			if (cmsxPCollBuildMeasurement(&hdr ->m, 
                                    hdr->ReferenceSheet, 
                                    hdr->MeasurementSheet,
                                    PATCH_HAS_RGB|PATCH_HAS_XYZ) == false) return false;
			lFreeOnExit = true;
			
		}

		cmsxIT8SetSheetType(it8,"LCMSEMBED");
		cmsxIT8SetProperty(it8, "ORIGINATOR",   (const char *) "Little cms");
		cmsxIT8SetProperty(it8, "DESCRIPTOR",   (const char *) hdr -> Description);
		cmsxIT8SetProperty(it8, "MANUFACTURER", (const char *) hdr ->Manufacturer);
					
		cmsxPCollSaveToSheet(&hdr->m, it8);
		cmsxIT8SaveToFile(it8, "TMP00.IT8");
		cmsxIT8Free(it8);

		f = fopen("TMP00.IT8", "rb");
		size = xfilelength(fileno(f));
		mem = (unsigned char*) malloc(size + 1);          // C->C++ : fixed cast
		readed = fread(mem, 1, size, f);
		fclose(f);

		mem[readed] = 0;
		unlink("TMP00.IT8");

		cmsAddTag(hdr->hProfile, icSigCharTargetTag, mem);
		free(mem);

		if (lFreeOnExit) {
			
			cmsxPCollFreeMeasurements(&hdr->m);
		}

		return true;
}


static
BOOL ComputeColorantMatrix(LPcmsCIEXYZTRIPLE Colorants, 
						   LPcmsCIExyY WhitePoint, 
						   LPcmsCIExyYTRIPLE Primaries)
{
	   MAT3 MColorants;
       
	   if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, WhitePoint, Primaries))
       {       
              return false;
       }  

	  
	   cmsAdaptMatrixToD50(&MColorants, WhitePoint);

       Colorants->Red.X = MColorants.v[0].n[0];
       Colorants->Red.Y = MColorants.v[1].n[0];
       Colorants->Red.Z = MColorants.v[2].n[0];

       Colorants->Green.X = MColorants.v[0].n[1];
       Colorants->Green.Y = MColorants.v[1].n[1];
       Colorants->Green.Z = MColorants.v[2].n[1];

       Colorants->Blue.X = MColorants.v[0].n[2];
       Colorants->Blue.Y = MColorants.v[1].n[2];
       Colorants->Blue.Z = MColorants.v[2].n[2];

	   return true;

}


BOOL cmsxEmbedMatrixShaper(LPPROFILERCOMMONDATA hdr)
{
	cmsCIEXYZTRIPLE Colorant;
	cmsCIExyY MediaWhite;

		cmsXYZ2xyY(&MediaWhite, &hdr ->WhitePoint);

		if (ComputeColorantMatrix(&Colorant, &MediaWhite, &hdr ->Primaries)) {

		cmsAddTag(hdr ->hProfile, icSigRedColorantTag, &Colorant.Red);
		cmsAddTag(hdr ->hProfile, icSigGreenColorantTag, &Colorant.Green);
		cmsAddTag(hdr ->hProfile, icSigBlueColorantTag, &Colorant.Blue);
		}

		cmsAddTag(hdr ->hProfile, icSigRedTRCTag, hdr ->Gamma[0]);
		cmsAddTag(hdr ->hProfile, icSigGreenTRCTag, hdr ->Gamma[1]);
		cmsAddTag(hdr ->hProfile, icSigBlueTRCTag, hdr ->Gamma[2]);

		return true;
}


BOOL cmsxEmbedTextualInfo(LPPROFILERCOMMONDATA hdr)
{
	    if (*hdr ->Description)
           cmsAddTag(hdr ->hProfile, icSigProfileDescriptionTag, hdr ->Description);
     
        if (*hdr ->Copyright)
           cmsAddTag(hdr ->hProfile, icSigCopyrightTag,          hdr ->Copyright);
     
        if (*hdr ->Manufacturer)
           cmsAddTag(hdr ->hProfile, icSigDeviceMfgDescTag,      hdr ->Manufacturer);
     
        if (*hdr ->Model)
           cmsAddTag(hdr ->hProfile, icSigDeviceModelDescTag,    hdr ->Model);

		return true;
}



void cmsxChromaticAdaptationAndNormalization(LPPROFILERCOMMONDATA hdr, LPcmsCIEXYZ xyz, BOOL lReverse)
{
    
        if (hdr->lUseCIECAM97s) {

                cmsJCh JCh;

                /* Let's CIECAM97s to do the adaptation to D50 */
			   
               xyz->X *= 100.;
               xyz->Y *= 100.;
               xyz->Z *= 100.;

               _cmsxClampXYZ100(xyz);

			   if (lReverse) {
				   	cmsCIECAM97sForward(hdr->hPCS, xyz, &JCh);
					cmsCIECAM97sReverse(hdr->hDevice, &JCh, xyz);
			   }
			   else {

					cmsCIECAM97sForward(hdr->hDevice, xyz, &JCh);
					cmsCIECAM97sReverse(hdr->hPCS, &JCh, xyz);
			   }

               _cmsxClampXYZ100(xyz);

               xyz -> X /= 100.;
               xyz -> Y /= 100.;
               xyz -> Z /= 100.;
							   
        }
        else {
                    
              /* Else, use Bradford */

			if (lReverse) {
				 cmsAdaptToIlluminant(xyz, cmsD50_XYZ(), &hdr->WhitePoint,  xyz);            
			}
			else {
			  cmsAdaptToIlluminant(xyz, &hdr->WhitePoint, cmsD50_XYZ(), xyz);            
			}
               
       }
        
}


void cmsxInitPCSViewingConditions(LPPROFILERCOMMONDATA hdr)
{
	  
	    hdr->PCS.whitePoint.X = cmsD50_XYZ()->X * 100.;
		hdr->PCS.whitePoint.Y = cmsD50_XYZ()->Y * 100.;
		hdr->PCS.whitePoint.Z = cmsD50_XYZ()->Z * 100.;


        hdr->PCS.Yb = 20;                     /* 20% of surround */
        hdr->PCS.La = 20;                     /* Adapting field luminance */
        hdr->PCS.surround = AVG_SURROUND;
        hdr->PCS.D_value  = 1.0;			 /* Complete adaptation */

}


/* Build gamut hull by geometric means */
void cmsxComputeGamutHull(LPPROFILERCOMMONDATA hdr)
{	
	int i;
	int x0, y0, z0;
	int Inside, Outside, Boundaries;
	char code;


	hdr -> hRGBHull = cmsxHullInit();

	/* For all valid patches, mark RGB knots as 0 */
	for (i=0; i < hdr ->m.nPatches; i++) {

        if (hdr ->m.Allowed[i]) {

            LPPATCH p = hdr ->m.Patches + i;


			x0 = (int) floor(p->Colorant.RGB[0]  + .5);
			y0 = (int) floor(p->Colorant.RGB[1]  + .5);
			z0 = (int) floor(p->Colorant.RGB[2]  + .5);

			cmsxHullAddPoint(hdr->hRGBHull, x0, y0, z0);			
		}
	}
	
	cmsxHullComputeHull(hdr ->hRGBHull);

/* #ifdef DEBUG */
	cmsxHullDumpVRML(hdr -> hRGBHull, "rgbhull.wrl");
/* #endif */



	/* A check */

	Inside = Outside = Boundaries = 0;
	/* For all valid patches, mark RGB knots as 0 */
	for (i=0; i < hdr ->m.nPatches; i++) {

        if (hdr ->m.Allowed[i]) {

            LPPATCH p = hdr ->m.Patches + i;

			x0 = (int) floor(p->Colorant.RGB[0]  + .5);
			y0 = (int) floor(p->Colorant.RGB[1]  + .5);
			z0 = (int) floor(p->Colorant.RGB[2]  + .5);

			code = cmsxHullCheckpoint(hdr -> hRGBHull, x0, y0, z0);

			switch (code) {
			
			case 'i': Inside++; break;
			case 'o': Outside++; break;
			default:  Boundaries++; 				
			}

		}
	}

	if (hdr ->printf)
		hdr ->printf("Gamut hull: %d inside, %d outside, %d on boundaries", Inside, Outside, Boundaries);
	
}

BOOL cmsxChoosePCS(LPPROFILERCOMMONDATA hdr)
{

        double gamma_r, gamma_g, gamma_b;        
        cmsCIExyY SourceWhite;

        /* At first, compute aproximation on matrix-shaper */
        if (!cmsxComputeMatrixShaper(hdr ->ReferenceSheet,                                 
                                     hdr ->MeasurementSheet,
									 hdr -> Medium,
                                     hdr ->Gamma,
                                     &hdr ->WhitePoint,
                                     &hdr ->BlackPoint,
                                     &hdr ->Primaries)) return false;

        		

		cmsXYZ2xyY(&SourceWhite,   &hdr ->WhitePoint);	
		    
        gamma_r = cmsEstimateGamma(hdr ->Gamma[0]);
        gamma_g = cmsEstimateGamma(hdr ->Gamma[1]);
        gamma_b = cmsEstimateGamma(hdr ->Gamma[2]);

		

        if (gamma_r > 1.8 || gamma_g > 1.8 || gamma_b > 1.8 ||
             gamma_r == -1 || gamma_g == -1 || gamma_b == -1) {

                hdr ->PCSType = PT_Lab;

                if (hdr ->printf)
                       hdr ->printf("I have chosen Lab as PCS");                        

        }
        else {
  
                hdr ->PCSType = PT_XYZ;

                if (hdr ->printf)
                       hdr ->printf("I have chosen XYZ as PCS");                        
     }

                

     if (hdr ->printf) {

                   char Buffer[256] = "Infered ";
                        
                   _cmsIdentifyWhitePoint(Buffer, &hdr ->WhitePoint);
                   hdr ->printf("%s", Buffer);                        
                   hdr ->printf("Primaries (x-y): [Red: %2.2f, %2.2f] [Green: %2.2f, %2.2f] [Blue: %2.2f, %2.2f]", 
                                      hdr ->Primaries.Red.x, hdr ->Primaries.Red.y, 
                                      hdr ->Primaries.Green.x, hdr ->Primaries.Green.y,
                                      hdr ->Primaries.Blue.x, hdr ->Primaries.Blue.y);

                   if ((gamma_r != -1) && (gamma_g != -1) && (gamma_b != -1)) {
                        
                   hdr ->printf("Estimated gamma: [Red: %2.2f] [Green: %2.2f] [Blue: %2.2f]", 
                                                            gamma_r, gamma_g, gamma_b);
                   }
                                                

         }


		
        return true;
}
