/******************************************************************************
 * $Id: ceosrecipe.c 36380 2016-11-21 10:21:20Z rouault $
 *
 * Project:  ASI CEOS Translator
 * Purpose:  CEOS field layout recipes.
 * Author:   Paul Lahaie, pjlahaie@atlsci.com
 *
 ******************************************************************************
 * Copyright (c) 2000, Atlantis Scientific Inc
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

#include "ceos.h"

CPL_CVSID("$Id: ceosrecipe.c 36380 2016-11-21 10:21:20Z rouault $");

/* Array of Datatypes and their names/values */

typedef struct {
    const char *String;
    int Type;
} CeosStringType_t;

typedef struct {
    int (*function)(CeosSARVolume_t *volume, const void *token);
    const void *token;
    const char *name;
} RecipeFunctionData_t;


static const CeosStringType_t CeosDataType[] = { { "IU1", CEOS_TYP_UCHAR },
				    { "IU2", CEOS_TYP_USHORT },
				    { "UI1", CEOS_TYP_UCHAR },
				    { "UI2", CEOS_TYP_USHORT },
				    { "CI*2", CEOS_TYP_COMPLEX_CHAR },
				    { "CI*4", CEOS_TYP_COMPLEX_SHORT },
				    { "CIS4", CEOS_TYP_COMPLEX_SHORT },
				    { "CI*8", CEOS_TYP_COMPLEX_LONG },
				    { "C*8", CEOS_TYP_COMPLEX_FLOAT },
				    { "R*4", CEOS_TYP_FLOAT },
				    { NULL, 0 } };

static const CeosStringType_t CeosInterleaveType[] = { { "BSQ", CEOS_IL_BAND },
					  { " BSQ", CEOS_IL_BAND },
					  { "BIL", CEOS_IL_LINE },
					  { " BIL", CEOS_IL_LINE },
					  { NULL, 0 } };

#define IMAGE_OPT { 63, 192, 18, 18 }
#define IMAGE_JERS_OPT { 50, 192, 18, 18 }    /* Some JERS data uses this instead of IMAGE_OPT */
#define PROC_DATA_REC { 50, 11, 18, 20 }
#define PROC_DATA_REC_ALT { 50, 11, 31, 20 }
#define PROC_DATA_REC_ALT2 { 50, 11, 31, 50 }  /* Some cases of ERS 1, 2 */
#define DATA_SET_SUMMARY { 18, 10, 18, 20 }

/* NOTE: This seems to be the generic recipe used for most things */
static const CeosRecipeType_t RadarSatRecipe[] =
{
    { CEOS_REC_NUMCHANS, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      233, 4, CEOS_REC_TYP_I }, /* Number of channels */
    { CEOS_REC_INTERLEAVE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      269, 4, CEOS_REC_TYP_A }, /* Interleaving type */
    { CEOS_REC_DATATYPE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      429, 4, CEOS_REC_TYP_A }, /* Data type */
    { CEOS_REC_BPR, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      0, 0, CEOS_REC_TYP_A }, /* For Defeault CEOS, this is done using other vals */
    { CEOS_REC_LINES, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      237, 8, CEOS_REC_TYP_I }, /* How many lines */
    { CEOS_REC_TBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      261, 4, CEOS_REC_TYP_I },
    { CEOS_REC_BBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      265, 4, CEOS_REC_TYP_I }, /* Bottom border pixels */
    { CEOS_REC_PPL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      249, 8, CEOS_REC_TYP_I }, /* Pixels per line */
    { CEOS_REC_LBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      245, 4, CEOS_REC_TYP_I }, /* Left Border Pixels */
    { CEOS_REC_RBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      257, 4, CEOS_REC_TYP_I }, /* Isn't available for RadarSAT */
    { CEOS_REC_BPP, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      225, 4, CEOS_REC_TYP_I }, /* Bytes Per Pixel */
    { CEOS_REC_RPL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      273, 2, CEOS_REC_TYP_I }, /* Records per line */
    { CEOS_REC_PPR, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      0, 0, CEOS_REC_TYP_I }, /* Pixels Per Record -- need to fill record type */
    { CEOS_REC_PDBPR, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      281, 8, CEOS_REC_TYP_I }, /* pixel data bytes per record */
    { CEOS_REC_IDS, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      277, 4, CEOS_REC_TYP_I }, /* Prefix data per record */
    { CEOS_REC_FDL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      9, 4, CEOS_REC_TYP_B }, /* Length of Imagry Options Header */
    { CEOS_REC_PIXORD, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      0, 0, CEOS_REC_TYP_I }, /* Must be calculated */
    { CEOS_REC_LINORD, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      0, 0, CEOS_REC_TYP_I }, /* Must be calculated */
    { CEOS_REC_PRODTYPE, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      0, 0, CEOS_REC_TYP_I },

    { CEOS_REC_RECORDSIZE, 1, CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC,
      9, 4, CEOS_REC_TYP_B }, /* The processed image record size */

    /* Some ERS-1 products use an alternate data record subtype2. */
    { CEOS_REC_RECORDSIZE, 1, CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC_ALT,
      9, 4, CEOS_REC_TYP_B }, /* The processed image record size */

    /* Yet another ERS-1 and ERS-2 alternate data record subtype2. */
    { CEOS_REC_RECORDSIZE, 1, CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC_ALT2,
      9, 4, CEOS_REC_TYP_B }, /* The processed image record size */

    { CEOS_REC_SUFFIX_SIZE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      289, 4, CEOS_REC_TYP_I }, /* Suffix data per record */
    { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */
} ;


static const CeosRecipeType_t JersRecipe[] =
{
    { CEOS_REC_NUMCHANS, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      233, 4, CEOS_REC_TYP_I }, /* Number of channels */
    { CEOS_REC_INTERLEAVE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      269, 4, CEOS_REC_TYP_A }, /* Interleaving type */
    { CEOS_REC_DATATYPE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      429, 4, CEOS_REC_TYP_A }, /* Data type */
    { CEOS_REC_BPR, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      0, 0, CEOS_REC_TYP_A }, /* For Defeault CEOS, this is done using other vals */
    { CEOS_REC_LINES, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      237, 8, CEOS_REC_TYP_I }, /* How many lines */
    { CEOS_REC_TBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      261, 4, CEOS_REC_TYP_I },
    { CEOS_REC_BBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      265, 4, CEOS_REC_TYP_I }, /* Bottom border pixels */
    { CEOS_REC_PPL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      249, 8, CEOS_REC_TYP_I }, /* Pixels per line */
    { CEOS_REC_LBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      245, 4, CEOS_REC_TYP_I }, /* Left Border Pixels */
    { CEOS_REC_RBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      257, 4, CEOS_REC_TYP_I }, /* Isn't available for RadarSAT */
    { CEOS_REC_BPP, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      225, 4, CEOS_REC_TYP_I }, /* Bytes Per Pixel */
    { CEOS_REC_RPL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      273, 2, CEOS_REC_TYP_I }, /* Records per line */
    { CEOS_REC_PPR, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      0, 0, CEOS_REC_TYP_I }, /* Pixels Per Record -- need to fill record type */
    { CEOS_REC_PDBPR, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      281, 8, CEOS_REC_TYP_I }, /* pixel data bytes per record */
    { CEOS_REC_IDS, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      277, 4, CEOS_REC_TYP_I }, /* Prefix data per record */
    { CEOS_REC_FDL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      9, 4, CEOS_REC_TYP_B }, /* Length of Imagry Options Header */
    { CEOS_REC_PIXORD, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      0, 0, CEOS_REC_TYP_I }, /* Must be calculated */
    { CEOS_REC_LINORD, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      0, 0, CEOS_REC_TYP_I }, /* Must be calculated */
    { CEOS_REC_PRODTYPE, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      0, 0, CEOS_REC_TYP_I },

    { CEOS_REC_RECORDSIZE, 1, CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC,
      9, 4, CEOS_REC_TYP_B }, /* The processed image record size */

    { CEOS_REC_SUFFIX_SIZE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_JERS_OPT,
      289, 4, CEOS_REC_TYP_I }, /* Suffix data per record */
    { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */
} ;

static const CeosRecipeType_t ScanSARRecipe[] =
{
    { CEOS_REC_NUMCHANS, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      233, 4, CEOS_REC_TYP_I }, /* Number of channels */
    { CEOS_REC_INTERLEAVE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      269, 4, CEOS_REC_TYP_A }, /* Interleaving type */
    { CEOS_REC_DATATYPE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      429, 4, CEOS_REC_TYP_A }, /* Data type */
    { CEOS_REC_LINES, 1, CEOS_ANY_FILE, DATA_SET_SUMMARY,
      325, 8, CEOS_REC_TYP_I }, /* How many lines */
    { CEOS_REC_PPL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      249, 8, CEOS_REC_TYP_I }, /* Pixels per line */
    { CEOS_REC_BPP, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      225, 4, CEOS_REC_TYP_I }, /* Bytes Per Pixel */
    { CEOS_REC_RPL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      273, 2, CEOS_REC_TYP_I }, /* Records per line */
    { CEOS_REC_IDS, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      277, 4, CEOS_REC_TYP_I }, /* Prefix data per record */
    { CEOS_REC_FDL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      9, 4, CEOS_REC_TYP_B }, /* Length of Imagry Options Header */
    { CEOS_REC_RECORDSIZE, 1, CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC,
      9, 4, CEOS_REC_TYP_B }, /* The processed image record size */
    { CEOS_REC_SUFFIX_SIZE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      289, 4, CEOS_REC_TYP_I }, /* Suffix data per record */
    { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */
} ;

static const CeosRecipeType_t SIRCRecipe[] =
{
    { CEOS_REC_NUMCHANS, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      233, 4, CEOS_REC_TYP_I }, /* Number of channels */
    { CEOS_REC_INTERLEAVE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      269, 4, CEOS_REC_TYP_A }, /* Interleaving type */
    { CEOS_REC_DATATYPE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      429, 4, CEOS_REC_TYP_A }, /* Data type */
    { CEOS_REC_LINES, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      237, 8, CEOS_REC_TYP_I }, /* How many lines */
    { CEOS_REC_TBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      261, 4, CEOS_REC_TYP_I },
    { CEOS_REC_BBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      265, 4, CEOS_REC_TYP_I }, /* Bottom border pixels */
    { CEOS_REC_PPL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      249, 8, CEOS_REC_TYP_I }, /* Pixels per line */
    { CEOS_REC_LBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      245, 4, CEOS_REC_TYP_I }, /* Left Border Pixels */
    { CEOS_REC_RBP, 0, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      257, 4, CEOS_REC_TYP_I }, /* Right Border Pixels */
    { CEOS_REC_BPP, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      225, 4, CEOS_REC_TYP_I }, /* Bytes Per Pixel */
    { CEOS_REC_RPL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      273, 2, CEOS_REC_TYP_I }, /* Records per line */
    { CEOS_REC_IDS, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      277, 4, CEOS_REC_TYP_I }, /* Prefix data per record */
    { CEOS_REC_FDL, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      9, 4, CEOS_REC_TYP_B }, /* Length of Imagry Options Header */
    { CEOS_REC_RECORDSIZE, 1, CEOS_IMAGRY_OPT_FILE, PROC_DATA_REC,
      9, 4, CEOS_REC_TYP_B }, /* The processed image record size */
    { CEOS_REC_SUFFIX_SIZE, 1, CEOS_IMAGRY_OPT_FILE, IMAGE_OPT,
      289, 4, CEOS_REC_TYP_I }, /* Suffix data per record */

    { 0, 0, 0, { 0, 0, 0, 0 }, 0, 0, 0 } /* Last record is Zero */
} ;

#undef PROC_DATA_REC

static void ExtractInt( CeosRecord_t *record, int type, unsigned int offset, unsigned int length, int *value );

static char *ExtractString( CeosRecord_t *record, unsigned int offset, unsigned int length, char *string );

static int GetCeosStringType(const CeosStringType_t *CeosType, const char *string);

static int SIRCRecipeFCN( CeosSARVolume_t *volume, const void *token );
static int PALSARRecipeFCN( CeosSARVolume_t *volume, const void *token );

Link_t *RecipeFunctions = NULL;

void RegisterRecipes( void )
{

    AddRecipe( SIRCRecipeFCN, SIRCRecipe, "SIR-C" );
    AddRecipe( ScanSARRecipeFCN, ScanSARRecipe, "ScanSAR" );
    AddRecipe( CeosDefaultRecipe, RadarSatRecipe, "RadarSat" );
    AddRecipe( CeosDefaultRecipe, JersRecipe, "Jers" );
    AddRecipe( PALSARRecipeFCN, RadarSatRecipe, "PALSAR-ALOS" );
    /*  AddRecipe( CeosDefaultRecipe, AtlantisRecipe ); */
}

void FreeRecipes( void )

{
    Link_t *l_link;

    for( l_link = RecipeFunctions; l_link != NULL; l_link = l_link->next )
        HFree( l_link->object );

    DestroyList( RecipeFunctions );
    RecipeFunctions = NULL;
}

void AddRecipe( int (*function)(CeosSARVolume_t *volume,
				const void *token),
		const void *token,
                const char *name )
{

    RecipeFunctionData_t *TempData;

    Link_t *Link;

    TempData = HMalloc( sizeof( RecipeFunctionData_t ) );

    TempData->function = function;
    TempData->token = token;
    TempData->name = name;

    Link = ceos2CreateLink( TempData );

    if( RecipeFunctions == NULL)
    {
	RecipeFunctions = Link;
    } else {
	RecipeFunctions = InsertLink( RecipeFunctions, Link );
    }
}

int CeosDefaultRecipe( CeosSARVolume_t *volume, const void *token )
{
    const CeosRecipeType_t *recipe;
    CeosRecord_t *record;
    CeosTypeCode_t TypeCode = { 0 };
    struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc);
    char temp_str[1024];
    int i /*, temp_int */;

#define DoExtractInt(a) ExtractInt( record, recipe[i].Type, recipe[i].Offset, recipe[i].Length, &a)

    if(token == NULL)
    {
	return 0;
    }

    memset(ImageDesc, 0, sizeof( struct CeosSARImageDesc ) );

/*    temp_imagerecipe = (CeosSARImageDescRecipe_t *) token;
    recipe = temp_imagerecipe->Recipe; */

    recipe = token;

    for(i = 0; recipe[i].ImageDescValue != 0; i++ )
    {
	if(recipe[i].Override)
	{
	    TypeCode.UCharCode.Subtype1 = recipe[i].TypeCode.Subtype1;
	    TypeCode.UCharCode.Type = recipe[i].TypeCode.Type;
	    TypeCode.UCharCode.Subtype2 = recipe[i].TypeCode.Subtype2;
	    TypeCode.UCharCode.Subtype3 = recipe[i].TypeCode.Subtype3;

	    record = FindCeosRecord( volume->RecordList, TypeCode, recipe[i].FileId, -1, -1 );

	    if(record == NULL)
	    {
              /* temp_int = 0; */
	    } else {

		switch( recipe[i].ImageDescValue )
		{
		case CEOS_REC_NUMCHANS:
		   DoExtractInt( ImageDesc->NumChannels );
		   break;
		case CEOS_REC_LINES:
		    DoExtractInt( ImageDesc->Lines );
		    break;
		case CEOS_REC_BPP:
		    DoExtractInt( ImageDesc->BytesPerPixel );
		    break;
		case CEOS_REC_RPL:
		    DoExtractInt( ImageDesc->RecordsPerLine );
		    break;
		case CEOS_REC_PDBPR:
		    DoExtractInt( ImageDesc->PixelDataBytesPerRecord );
		    break;
		case CEOS_REC_FDL:
		    DoExtractInt( ImageDesc->FileDescriptorLength );
		    break;
		case CEOS_REC_IDS:
		    DoExtractInt( ImageDesc->ImageDataStart );
                    /*
                    ** This is really reading the quantity of prefix data
                    ** per data record.  We want the offset from the very
                    ** beginning of the record to the data, so we add another
                    ** 12 to that.  I think some products incorrectly indicate
                    ** 192 (prefix+12) instead of 180 so if we see 192 assume
                    ** the 12 bytes of record start data has already been
                    ** added.  Frank Warmerdam.
                    */
		    if( ImageDesc->ImageDataStart != 192 )
                        ImageDesc->ImageDataStart += 12;
		    break;
		case CEOS_REC_SUFFIX_SIZE:
		    DoExtractInt( ImageDesc->ImageSuffixData );
		    break;
		case CEOS_REC_RECORDSIZE:
		    DoExtractInt( ImageDesc->BytesPerRecord );
		    break;
		case CEOS_REC_PPL:
		    DoExtractInt( ImageDesc->PixelsPerLine );
		    break;
		case CEOS_REC_TBP:
		    DoExtractInt( ImageDesc->TopBorderPixels );
		    break;
		case CEOS_REC_BBP:
		    DoExtractInt( ImageDesc->BottomBorderPixels );
		    break;
		case CEOS_REC_LBP:
		    DoExtractInt( ImageDesc->LeftBorderPixels );
		    break;
		case CEOS_REC_RBP:
		    DoExtractInt( ImageDesc->RightBorderPixels );
		    break;
		case CEOS_REC_INTERLEAVE:
		    ExtractString( record, recipe[i].Offset, recipe[i].Length, temp_str );

		    ImageDesc->ChannelInterleaving = GetCeosStringType( CeosInterleaveType, temp_str );
		    break;
		case CEOS_REC_DATATYPE:
		    ExtractString( record, recipe[i].Offset, recipe[i].Length, temp_str );

		    ImageDesc->DataType = GetCeosStringType( CeosDataType, temp_str );
		    break;
		}
	    }
	}
    }

    /* Some files (Telaviv) don't record the number of pixel groups per line.
     * Try to derive it from the size of a data group, and the number of
     * bytes of pixel data if necessary.
     */

    if( ImageDesc->PixelsPerLine == 0
        && ImageDesc->PixelDataBytesPerRecord != 0
        && ImageDesc->BytesPerPixel != 0 )
    {
        ImageDesc->PixelsPerLine =
            ImageDesc->PixelDataBytesPerRecord / ImageDesc->BytesPerPixel;
        CPLDebug( "SAR_CEOS", "Guessing PixelPerLine to be %d\n",
                  ImageDesc->PixelsPerLine );
    }

    /* Some files don't have the BytesPerRecord stuff, so we calculate it if possible */

    if( ImageDesc->BytesPerRecord == 0 && ImageDesc->RecordsPerLine == 1 &&
	ImageDesc->PixelsPerLine > 0 && ImageDesc->BytesPerPixel > 0 )
    {
        CeosRecord_t *img_rec;

        ImageDesc->BytesPerRecord = ImageDesc->PixelsPerLine *
	  ImageDesc->BytesPerPixel + ImageDesc->ImageDataStart +
	  ImageDesc->ImageSuffixData ;

        TypeCode.UCharCode.Subtype1 = 0xed;
        TypeCode.UCharCode.Type = 0xed;
        TypeCode.UCharCode.Subtype2 = 0x12;
        TypeCode.UCharCode.Subtype3 = 0x12;

        img_rec = FindCeosRecord( volume->RecordList, TypeCode,
                                  CEOS_IMAGRY_OPT_FILE, -1, -1 );
        if( img_rec == NULL )
        {
            CPLDebug( "SAR_CEOS",
                      "Unable to find imagery rec to check record length." );
            return 0;
        }

        if( img_rec->Length != ImageDesc->BytesPerRecord )
        {
            CPLDebug( "SAR_CEOS",
                      "Guessed record length (%d) did not match\n"
                      "actual imagery record length (%d), recipe fails.",
                      ImageDesc->BytesPerRecord, img_rec->Length );
            return 0;
        }
    }

    if( ImageDesc->PixelsPerRecord == 0 &&
	ImageDesc->BytesPerRecord != 0 && ImageDesc->BytesPerPixel != 0 )
    {
	ImageDesc->PixelsPerRecord = ( ( ImageDesc->BytesPerRecord -
					 (ImageDesc->ImageSuffixData +
					  ImageDesc->ImageDataStart )) /
				       ImageDesc->BytesPerPixel );

	if(ImageDesc->PixelsPerRecord > ImageDesc->PixelsPerLine)
	    ImageDesc->PixelsPerRecord = ImageDesc->PixelsPerLine;
    }

    /* If we didn't get a data type, try guessing. */
    if( ImageDesc->DataType == 0
        && ImageDesc->BytesPerPixel != 0
        && ImageDesc->NumChannels != 0 )
    {
        int  nDataTypeSize = ImageDesc->BytesPerPixel / ImageDesc->NumChannels;

        if( nDataTypeSize == 1 )
            ImageDesc->DataType = CEOS_TYP_UCHAR;
        else if( nDataTypeSize == 2 )
            ImageDesc->DataType = CEOS_TYP_USHORT;
    }

    /* Sanity checking */

    if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 ||
	ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 ||
	ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 ||
	ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 ||
	ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0)
    {
	return 0;
    } else {

	ImageDesc->ImageDescValid = TRUE;
	return 1;
    }
}

int ScanSARRecipeFCN( CeosSARVolume_t *volume, const void *token )
{
    struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc);

    memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) );

    if( CeosDefaultRecipe( volume, token ) )
    {
	ImageDesc->Lines *= 2;
	return 1;
    }

    return 0;
}

static int SIRCRecipeFCN( CeosSARVolume_t *volume, const void *token )
{
    struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc);
    CeosTypeCode_t TypeCode = { 0 };
    CeosRecord_t *record;
    char szSARDataFormat[29];

    memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) );

/* -------------------------------------------------------------------- */
/*      First, we need to check if the "SAR Data Format Type            */
/*      identifier" is set to "COMPRESSED CROSS-PRODUCTS" which is      */
/*      pretty idiosyncratic to SIRC products.  It might also appear    */
/*      for some other similarly encoded Polarimetric data I suppose.    */
/* -------------------------------------------------------------------- */
    /* IMAGE_OPT */
    TypeCode.UCharCode.Subtype1 = 63;
    TypeCode.UCharCode.Type     = 192;
    TypeCode.UCharCode.Subtype2 = 18;
    TypeCode.UCharCode.Subtype3 = 18;

    record = FindCeosRecord( volume->RecordList, TypeCode,
                             CEOS_IMAGRY_OPT_FILE, -1, -1 );
    if( record == NULL )
        return 0;

    ExtractString( record, 401, 28, szSARDataFormat );
    if( !STARTS_WITH_CI(szSARDataFormat, "COMPRESSED CROSS-PRODUCTS") )
        return 0;

/* -------------------------------------------------------------------- */
/*      Apply normal handling...                                        */
/* -------------------------------------------------------------------- */
    CeosDefaultRecipe( volume, token );

/* -------------------------------------------------------------------- */
/*      Make sure this looks like the SIRC product we are expecting.    */
/* -------------------------------------------------------------------- */
    if( ImageDesc->BytesPerPixel != 10 )
        return 0;

/* -------------------------------------------------------------------- */
/*      Then fix up a few values.                                       */
/* -------------------------------------------------------------------- */
    /* It seems the bytes of pixel data per record is just wrong.  Fix. */
    ImageDesc->PixelDataBytesPerRecord =
        ImageDesc->BytesPerPixel * ImageDesc->PixelsPerLine;

    ImageDesc->DataType = CEOS_TYP_CCP_COMPLEX_FLOAT;

/* -------------------------------------------------------------------- */
/*      Sanity checking                                                 */
/* -------------------------------------------------------------------- */
    if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 ||
	ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 ||
	ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 ||
	ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 ||
	ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0)
    {
	return 0;
    } else {

	ImageDesc->ImageDescValid = TRUE;
	return 1;
    }
}

static int PALSARRecipeFCN( CeosSARVolume_t *volume, const void *token )
{
    struct CeosSARImageDesc *ImageDesc = &(volume->ImageDesc);
    CeosTypeCode_t TypeCode = { 0 };
    CeosRecord_t *record;
    char szSARDataFormat[29], szProduct[32];

    memset( ImageDesc, 0, sizeof( struct CeosSARImageDesc ) );

/* -------------------------------------------------------------------- */
/*      First, we need to check if the "SAR Data Format Type            */
/*      identifier" is set to "COMPRESSED CROSS-PRODUCTS" which is      */
/*      pretty idiosyncratic to SIRC products.  It might also appear    */
/*      for some other similarly encoded Polarimetric data I suppose.    */
/* -------------------------------------------------------------------- */
    /* IMAGE_OPT */
    TypeCode.UCharCode.Subtype1 = 63;
    TypeCode.UCharCode.Type     = 192;
    TypeCode.UCharCode.Subtype2 = 18;
    TypeCode.UCharCode.Subtype3 = 18;

    record = FindCeosRecord( volume->RecordList, TypeCode,
                             CEOS_IMAGRY_OPT_FILE, -1, -1 );
    if( record == NULL )
        return 0;

    ExtractString( record, 401, 28, szSARDataFormat );
    if( !STARTS_WITH_CI( szSARDataFormat, "INTEGER*18                 ") )
        return 0;

    ExtractString( record, 49, 16, szProduct );
    if( !STARTS_WITH_CI(szProduct, "ALOS-") )
        return 0;

/* -------------------------------------------------------------------- */
/*      Apply normal handling...                                        */
/* -------------------------------------------------------------------- */
    CeosDefaultRecipe( volume, token );

/* -------------------------------------------------------------------- */
/*      Make sure this looks like the SIRC product we are expecting.    */
/* -------------------------------------------------------------------- */
    if( ImageDesc->BytesPerPixel != 18 )
        return 0;

/* -------------------------------------------------------------------- */
/*      Then fix up a few values.                                       */
/* -------------------------------------------------------------------- */
    ImageDesc->DataType = CEOS_TYP_PALSAR_COMPLEX_SHORT;
    ImageDesc->NumChannels = 6;

/* -------------------------------------------------------------------- */
/*      Sanity checking                                                 */
/* -------------------------------------------------------------------- */
    if( ImageDesc->PixelsPerLine == 0 || ImageDesc->Lines == 0 ||
	ImageDesc->RecordsPerLine == 0 || ImageDesc->ImageDataStart == 0 ||
	ImageDesc->FileDescriptorLength == 0 || ImageDesc->DataType == 0 ||
	ImageDesc->NumChannels == 0 || ImageDesc->BytesPerPixel == 0 ||
	ImageDesc->ChannelInterleaving == 0 || ImageDesc->BytesPerRecord == 0)
    {
	return 0;
    } else {

	ImageDesc->ImageDescValid = TRUE;
	return 1;
    }
}

void GetCeosSARImageDesc( CeosSARVolume_t *volume )
{
    Link_t *l_link;
    RecipeFunctionData_t *rec_data;
    int (*function)(CeosSARVolume_t *volume, const void *token);

    if( RecipeFunctions == NULL )
    {
	RegisterRecipes();
    }

    if(RecipeFunctions == NULL )
    {
	return ;
    }

    for(l_link = RecipeFunctions; l_link != NULL; l_link = l_link->next)
    {
	if(l_link->object)
	{
	    rec_data = l_link->object;
            function = rec_data->function;
	    if(( *function )( volume, rec_data->token ) )
            {
                CPLDebug( "CEOS", "Using recipe '%s'.",
                          rec_data->name );
		return;
            }
	}
    }

    return ;

}


static void ExtractInt(CeosRecord_t *record, int type, unsigned int offset, unsigned int length, int *value)
{
    void *buffer;
    char format[32];

    buffer = HMalloc( length + 1 );

    switch(type)
    {
    case CEOS_REC_TYP_A:
	snprintf( format, sizeof(format), "A%u", length );
	GetCeosField( record, offset, format,  buffer );
	*value = atoi( buffer );
	break;
    case CEOS_REC_TYP_B:
	snprintf( format, sizeof(format), "B%u", length );
#ifdef notdef
	GetCeosField( record, offset, format, buffer );
	if( length <= 4 )
	    CeosToNative( value, buffer, length, length );
	else
	    *value = 0;
#else
	GetCeosField( record, offset, format, value );
#endif
	break;
    case CEOS_REC_TYP_I:
	snprintf( format, sizeof(format), "I%u", length );
	GetCeosField( record, offset, format, value );
	break;
    }

    HFree( buffer );

}

static char *ExtractString( CeosRecord_t *record, unsigned int offset, unsigned int length, char *string )
{
    char format[12];

    if(string == NULL)
    {
	string = HMalloc( length + 1 );
    }

    snprintf( format, sizeof(format), "A%u", length );

    GetCeosField( record, offset, format, string );

    return string;
}

static int GetCeosStringType(const CeosStringType_t *CeosStringType, const char *string)
{
    int i;

    for(i = 0;CeosStringType[i].String != NULL;i++)
    {
	if(strncmp(CeosStringType[i].String ,string, strlen( CeosStringType[i].String ) ) == 0 )
	{
	    return CeosStringType[i].Type;
	}
    }

    return 0;
}
