#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <exec/types.h>
#include <exec/ports.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/icon.h>

#include <MakeILBM.h>
#include <Scanner_protos.h>
#include <DriverIo.h>
#include <Scanner.h>

extern int stackSize;
extern int debugAvailable;

static struct ScannerOptions option;

static char*  unitName;
static int    unitNum = 2;

static int    color;
static int    speed;
static fixed  fx0,fy0,fx1,fy1;
static int    resolution;
static int    brightness;
static int    contrast;
static int    shadow;
static int    highlight;
static int    midtone;
static int    halftonePattern;
static int    exposureTime;   
static int    negative;
static int    gamma;
static int    source;
static int    compress;
static char*  outputFile;

static char* sodType[] = {"Boolean","Integer","Fixed","String","Button",NULL};
static char* sodUnit[] = {"No unit","Pixel","Bit","mm","DPI","Percent","µs",NULL};
static char* cmdOptionString[ID_NUMBER_OF_OPTIONS] = 
{
  "",
  "color",
  "top left x",
  "top left y",
  "bottom right x",
  "bottom right y",
  "resolution",
  "halftone pattern",
  "brightness",
  "contrast",
  "gamma",
  "shadow",
  "highlight",
  "threshold",
  "time",
  "ADF",
  "speed",
  "negative",
  "source"
};

static struct OptionValue parameterValue[ID_NUMBER_OF_OPTIONS];

int   mainStart(char **toolTypes);
void  usage(void);
void  instructions(void);

void  showOptions(struct ScannerOptions* option);
struct OptionDescriptor* descriptor(OptionId id);
short readToolTypes(char **array,struct ScannerOptions* option);
BOOL  toolValue(char** array,char* matchStr,struct OptionDescriptor* descr,int* val);

/**************************************************************************/

/**********************           M A I N           ***********************/

/**************************************************************************/
int testStart(char **toolTypes)
{
  struct MsgPort *port;
  char*  str;

  if( FindToolType(toolTypes,"help") )
  {
    usage();
    return 0;
  }

  if( FindToolType(toolTypes,"instructions") )
  {
    instructions();
    return 0;
  }

  if( !(unitName = FindToolType(toolTypes,"device")) )
    unitName = "scsi.device";
  if( str = FindToolType(toolTypes,"unit") )
    unitNum = strtol(str,NULL,10);

  if( port = CreateMsgPort() )
  {
    if( openDriverIo(unitName,unitNum,port) )
    {
      printf("%s IO port open\n\n",isSCSI()?"SCSI":"parallel/serial");
      mainStart(toolTypes);
      closeDriverIo();
    }
    else
      printf("Cannot open %s unit %d\n\n",unitName,unitNum);
     
    DeleteMsgPort(port);
  }
  return 0;
}

int mainStart(char **toolTypes)
{
  int    i;
  BYTE   status;
  
  memset(&option,0,sizeof(struct ScannerOptions));
  openScanner(&option,&status);

  if( FindToolType(toolTypes,"options") )
  {
    if( status )
      puts("No scanner available.\n");
    else
    {
      showOptions(&option);
      closeScanner();
    }
    
    return status;
  }

  if( status == 0)
  {
    struct ScanInformation inform;
    struct OptionValue     value[2];
   
    // value[1].sp_optionID = ID_NONE;
    for( i = 0 ; i < option.so_optionNum ; i++ )
    {
      value[0].sp_optionID = option.so_descriptor[i].od_optionID;
      value[0].sp_flags = CONTROL_GET;
      controlOption(value,&status);
      
      if( !(value[0].sp_flags & CONTROL_INVALID) )
      {
        switch( option.so_descriptor[i].od_optionID )
        {
          case ID_SCANMODE:
            color = value[0].sp_value;
            break;
          case ID_TL_X:             /* upper left corner of scan area     */
            fx0 = DOUBLE_FIX(25.0); // value[0].sp_value;
            break;
          case ID_TL_Y:             /* upper left corner of scan area     */
            fy0 = DOUBLE_FIX(50.0); // value[0].sp_value;
            break;
          case ID_BR_X:             /* bottom right corner of scan area   */
            fx1 = DOUBLE_FIX(75.0); // value[0].sp_value;
            break;
          case ID_BR_Y:             /* bottom right corner of scan area   */
            fy1 = DOUBLE_FIX(100.0);// value[0].sp_value;
            break;
          case ID_RESOLUTION:       /* scan resolution (combined x and y) */
            resolution = value[0].sp_value;
            break;
          case ID_HALFTONEPATTERN:
            halftonePattern = value[0].sp_value;
            break;
          case ID_BRIGHTNESS:
            brightness = value[0].sp_value;
            break;
          case ID_CONTRAST:
            contrast = value[0].sp_value;
            break;
          case ID_ANALOGGAMMA:
            gamma = value[0].sp_value;
            break;
          case ID_BLACKLEVEL:  /* some times called shadow           */
            shadow = value[0].sp_value;
            break;
          case ID_WHITELEVEL:  /* some times called highlight        */
            highlight = value[0].sp_value;
            break;
          case ID_THRESHOLD:
            midtone = value[0].sp_value;
            break;
          case ID_EXPOSURETIME:
            exposureTime = value[0].sp_value;
            break;
          case ID_NEGATIVE:
            negative = value[0].sp_value;
            break;
          case ID_SCAN_SOURCE:
            source = value[0].sp_value;
            break;
          default:
            break;
        }
      }
    }
    
    if( status = readToolTypes(toolTypes,&option) )
    {
      usage();
      closeScanner();
      return status;
    }
 
    parameterValue[ID_SCANMODE-1].sp_optionID = ID_SCANMODE;
    parameterValue[ID_SCANMODE-1].sp_value = color;
    parameterValue[ID_SCANMODE-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_SCANMODE-1],&status);
    
    parameterValue[ID_SPEED-1].sp_optionID = ID_SPEED;
    parameterValue[ID_SPEED-1].sp_value = speed;
    parameterValue[ID_SPEED-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_SPEED-1],&status);
    
    parameterValue[ID_TL_X-1].sp_optionID = ID_TL_X;
    parameterValue[ID_TL_X-1].sp_value = fx0;
    parameterValue[ID_TL_X-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_TL_X-1],&status);
    
    parameterValue[ID_TL_Y-1].sp_optionID = ID_TL_Y;
    parameterValue[ID_TL_Y-1].sp_value = fy0;
    parameterValue[ID_TL_Y-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_TL_Y-1],&status);
    
    parameterValue[ID_BR_X-1].sp_optionID = ID_BR_X;
    parameterValue[ID_BR_X-1].sp_value = fx1;
    parameterValue[ID_BR_X-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_BR_X-1],&status);
    
    parameterValue[ID_BR_Y-1].sp_optionID = ID_BR_Y;
    parameterValue[ID_BR_Y-1].sp_value = fy1;
    parameterValue[ID_BR_Y-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_BR_Y-1],&status);
    
    parameterValue[ID_RESOLUTION-1].sp_optionID = ID_RESOLUTION;
    parameterValue[ID_RESOLUTION-1].sp_value = resolution;
    parameterValue[ID_RESOLUTION-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_RESOLUTION-1],&status);
    
    parameterValue[ID_HALFTONEPATTERN-1].sp_optionID = ID_HALFTONEPATTERN;
    parameterValue[ID_HALFTONEPATTERN-1].sp_value = halftonePattern;
    parameterValue[ID_HALFTONEPATTERN-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_HALFTONEPATTERN-1],&status);
    
    parameterValue[ID_BRIGHTNESS-1].sp_optionID = ID_BRIGHTNESS;
    parameterValue[ID_BRIGHTNESS-1].sp_value = brightness;
    parameterValue[ID_BRIGHTNESS-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_BRIGHTNESS-1],&status);
    
    parameterValue[ID_CONTRAST-1].sp_optionID = ID_CONTRAST;
    parameterValue[ID_CONTRAST-1].sp_value = contrast;
    parameterValue[ID_CONTRAST-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_CONTRAST-1],&status);
    
    parameterValue[ID_ANALOGGAMMA-1].sp_optionID = ID_ANALOGGAMMA;
    parameterValue[ID_ANALOGGAMMA-1].sp_value = gamma;
    parameterValue[ID_ANALOGGAMMA-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_ANALOGGAMMA-1],&status);
    
    parameterValue[ID_BLACKLEVEL-1].sp_optionID = ID_BLACKLEVEL;
    parameterValue[ID_BLACKLEVEL-1].sp_value = shadow;
    parameterValue[ID_BLACKLEVEL-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_BLACKLEVEL-1],&status);
    
    parameterValue[ID_WHITELEVEL-1].sp_optionID = ID_WHITELEVEL;
    parameterValue[ID_WHITELEVEL-1].sp_value = highlight;
    parameterValue[ID_WHITELEVEL-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_WHITELEVEL-1],&status);
    
    parameterValue[ID_THRESHOLD-1].sp_optionID = ID_THRESHOLD;
    parameterValue[ID_THRESHOLD-1].sp_value = midtone;
    parameterValue[ID_THRESHOLD-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_THRESHOLD-1],&status);
    
    parameterValue[ID_EXPOSURETIME-1].sp_optionID = ID_EXPOSURETIME;
    parameterValue[ID_EXPOSURETIME-1].sp_value = exposureTime;
    parameterValue[ID_EXPOSURETIME-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_EXPOSURETIME-1],&status);
    
    parameterValue[ID_NEGATIVE-1].sp_optionID = ID_NEGATIVE;
    parameterValue[ID_NEGATIVE-1].sp_value = negative;
    parameterValue[ID_NEGATIVE-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_NEGATIVE-1],&status);
    
    parameterValue[ID_SCAN_SOURCE-1].sp_optionID = ID_SCAN_SOURCE;
    parameterValue[ID_SCAN_SOURCE-1].sp_value = source;
    parameterValue[ID_SCAN_SOURCE-1].sp_flags = CONTROL_SET;
    controlOption(&parameterValue[ID_SCAN_SOURCE-1],&status);
    
    // parameterValue[ID_NUMBER_OF_OPTIONS-1].sp_optionID = ID_NONE;
    
    // controlOption(parameterValue,&status);
   
    if( status == 0 )
    {
      startScanning(&inform,&status);
     
      if( status == 0 )
      {
        struct ILBMFile *fh;
        struct ScanLine line;
        
        if( (line.sl_data = malloc(inform.sv_bytesPerLine)) && (fh = openILBM(outputFile,compress,&inform)) )
        {
          line.sl_color = 0;
          
          while( status == 0 )
          {
            readScanLine(&line,&status);
            if( status == 0 )
              writeILBM(fh,&line);
          }
          
          if( status == SCAN_STATUS_EOF )
          {
            closeILBM(fh,0);
            status = 0;
          }
          else
            closeILBM(fh,1);
        }

        stopScanning();
        
        if( line.sl_data )
          free(line.sl_data);
      }
    }
 
    closeScanner();
  }
  else
    puts("No scanner available.\n");
 
  return status;
}

void usage(void)
{
  char  buf[128];
  char* prgName;
  
  if( GetProgramName(buf,127) )
    prgName = FilePart(buf);
  else
    prgName ="Scanner";
        
  printf("\nUsage: %s [options]\n\n",prgName);
        
  puts("where options are:\n");
  
  puts("help                print this usage\n");

  puts("instructions        short instruction manual\n");

  puts("options             show scanner options\n");

  puts("device=<name>       default is: device=scsi.device\n");
    
  puts("unit=n              default is: unit=2\n");

  if( debugAvailable )
  {
    puts("debug=n             set debug level to n");
    puts("                    default is: debug=0\n");
  }

  puts("color=<color name>  default is driver default\n");

  puts("speed=s             default is driver default\n");

  puts("frame=x0,y0,x1,y1   upper left and lower right corner in mm");
  puts("                    default is: frame=25,50,75,100\n");

  puts("resolution=r        resolution in dpi");
  puts("                    default is driver default\n");

  puts("brightness=b        default is driver default\n");

  puts("contrast=c          default is driver default\n");

  puts("shadow=s            default is driver default\n");

  puts("highlight=h         default is driver default\n");

  puts("threshold=t         default is driver default\n");

  puts("pattern=p           default is driver default\n");

  puts("time=t              exposure time");
  puts("                    default is driver default\n");

  puts("negative=on|off     default is: negative=off\n");

  puts("gamma=g             default is: gamma=1.0\n");

  puts("source=<doc source> default is driver default\n");

  puts("compress=on|off     default is: compress=on\n");

  puts("file=<name>         default is: file=ram:scanner.ilbm\n");


  printf("Example: %s\n\n",prgName);
  printf("         %s device=squirrelscsi.device unit=1 options\n\n",prgName);
  printf("         %s color=Gray frame=0,0,10,10 compress=off file=dh0:test.ilbm\n\n",prgName);
}

void  instructions(void)
{
  char  buf[128];
  char* prgName;
  
  if( GetProgramName(buf,127) )
    prgName = FilePart(buf);
  else
    prgName ="Scanner";
  
  printf("\n");      
  printf("Short Instrunction Manual\n");
  printf("-------------------------\n\n");
        
  printf("First try to get the available scanner options. Type some thing like\n\n");
  
  printf("  %s device=GVPscsi.device unit=3 options\n\n",prgName);
  
  printf("with the name of your io-device in place of 'GVPscsi.device' and the\n"
         "number of your io-port unit in stead of '3'.\n"
         "\n"
         "In some cases it is necessary to set the shell stack to a higher value.\n"
         "Type:\n"
         "\n"
        );
        
  printf("  stack %d\n\n",stackSize);

  if( debugAvailable )
  {
    printf("If it fails or if the shown options seems unlikely you should turn\n"
           "on debugging. Type:\n"
           "\n"
          );

    printf("  %s device=GVPscsi.device unit=3 debug=30 options\n\n",prgName);
  
    printf("The higher number the more debugging output. There might be a lot\n"
           "of output. In this case it is practical to send output to a file\n"
           "using redirection. Type:\n"
           "\n"
          );

    printf("  %s >ram:debug.output device=GVPscsi.device unit=3 debug=30 options\n\n",prgName);
  }
  
  printf("If every thing seems to be ok then try to scan a picture. To do this\n"
         "remove 'options' from the command line:\n"
         "\n"
        );

  printf("  %s device=GVPscsi.device unit=3\n\n",prgName);
  
  printf("If it works an ilbm picture is in 'ram:'. The resolution is the\n"
         "default resolution of the scanner and the frame has upper left corner\n"
         "(25mm,50mm) and lower right corner (75mm,100mm). Use help-option to\n"
         "see how these and other default values are changed:\n"
         "\n"
        );

  printf("  %s help\n\n",prgName);
}
          
void showOptions(struct ScannerOptions* option)
{
  struct OptionValue value[2];
  int  i;
  BYTE status;
  
  printf("\nScanner: %s %s \n\n",option->so_scannerVendor,option->so_scannerModel);

  printf("Driver version %d.%d\n\n",option->so_driverVersion,option->so_driverRevision);

  printf("Document width:  %5.1f mm\n",option->so_docWidth);
  printf("Document height: %5.1f mm\n\n",option->so_docHeight);
  
  // value[1].sp_optionID = ID_NONE;
  for( i = 0 ; i < option->so_optionNum ; i++ )
  {
    value[0].sp_optionID = option->so_descriptor[i].od_optionID;
    value[0].sp_flags = CONTROL_GET;
    controlOption(value,&status);
      
    printf("%s:\n",cmdOptionString[option->so_descriptor[i].od_optionID]);
    printf("  type:  %s\n",sodType[option->so_descriptor[i].od_valueType]);
    printf("  unit:  %s\n",sodUnit[option->so_descriptor[i].od_valueUnit]);

    switch( option->so_descriptor[i].od_constraintType )
    {
      case CONSTRAINT_NONE:
        printf("  default=%d\n",value[0].sp_value);
        break;
      case CONSTRAINT_STRING_LIST:
        {
          char** p;
                   
          p = option->so_descriptor[i].od_constraint.stringList;
          
          printf("  values: %s\n",*p);
          p++;         
          while( *p )
          {
            printf("          %s\n",*p);
            p++;
          }

          printf("  default=%s\n",option->so_descriptor[i].od_constraint.stringList[value[0].sp_value]);
        }
        break;
      case CONSTRAINT_WORD_LIST:
        {
          int x;

          printf("  list: ");  //(%d) : ",option->so_descriptor[i].od_numberList[0]);
                for(x = 1; x <= option->so_descriptor[i].od_constraint.numberList[0]; x++)
          {
            if( option->so_descriptor[i].od_valueType == TYPE_INT )
              printf(" %d, ",option->so_descriptor[i].od_constraint.numberList[x]);
            else if( option->so_descriptor[i].od_valueType == TYPE_FIXED )
              printf(" %1.1f, ",FIX_DOUBLE(option->so_descriptor[i].od_constraint.numberList[x]));
          }
          printf("\n");

          if( option->so_descriptor[i].od_valueType == TYPE_INT )
            printf("  default=%d\n",value[0].sp_value);
          else if( option->so_descriptor[i].od_valueType == TYPE_FIXED )
            printf("  default=%1.1f\n",FIX_DOUBLE(value[0].sp_value));
        }
              break;
      case CONSTRAINT_RANGE:
        if( option->so_descriptor[i].od_valueType == TYPE_INT )
          printf("  range: %d .. %d with steps: %d \n",
                  option->so_descriptor[i].od_constraint.numberRange->min,
                  option->so_descriptor[i].od_constraint.numberRange->max,
                  option->so_descriptor[i].od_constraint.numberRange->quant
                );
        else if( option->so_descriptor[i].od_valueType == TYPE_FIXED )
          printf("  range: %1.1f .. %1.1f with steps: %1.1f \n",
                 FIX_DOUBLE(option->so_descriptor[i].od_constraint.numberRange->min),
                 FIX_DOUBLE(option->so_descriptor[i].od_constraint.numberRange->max),
                 (option->so_descriptor[i].od_constraint.numberRange->quant?FIX_DOUBLE(option->so_descriptor[i].od_constraint.numberRange->quant):0.1)
                );

        if( option->so_descriptor[i].od_valueType == TYPE_INT )
          printf("  default=%d\n",value[0].sp_value);
        else if( option->so_descriptor[i].od_valueType == TYPE_FIXED )
          printf("  default=%1.1f\n",FIX_DOUBLE(value[0].sp_value));

              break;
    }
    
    if( option->so_descriptor[i].od_specialInfo )
    {
      switch( option->so_descriptor[i].od_optionID )
      {
        case ID_SCANMODE:
        case ID_HALFTONEPATTERN:
        case ID_THRESHOLD:
          {
            int j = 0;
            
            while( (j < option->so_optionNum) && (option->so_descriptor[j].od_optionID != ID_SCANMODE) )
              j++;
            
            if( (option->so_descriptor[j].od_optionID == ID_SCANMODE) && (option->so_descriptor[j].od_constraintType == CONSTRAINT_STRING_LIST) )
            {
              char** modeStr = option->so_descriptor[j].od_constraint.stringList;
              int*   arr = option->so_descriptor[i].od_specialInfo;
            
              printf("  specialInfo: %d",*(arr++));
              while( *(++modeStr) )
                printf(", %d",*(arr++));
              printf("\n");
            }
          }
          break;
        case ID_BRIGHTNESS:
        case ID_CONTRAST:
          {
            char** str = option->so_descriptor[i].od_constraint.stringList;
            int*   arr = option->so_descriptor[i].od_specialInfo;
            
            printf("  specialInfo: %d",*(arr++));
            while( *(++str) )
              printf(", %d",*(arr++));
            printf("\n");
            
          }
          break;
      }
    }
    
    printf("\n");
  }
}

short readToolTypes(char **array,struct ScannerOptions* option)
{
  char* str;
    
  // Get frame coordinates
  if( str = FindToolType(array,"frame") )
  {
    fx0 = DOUBLE_FIX(strtod(str,&str));
    if( *(str++) != ',' )
      return 2;
    fy0 = DOUBLE_FIX(strtod(str,&str));
    if( *(str++) != ',' )
      return 2;
    fx1 = DOUBLE_FIX(strtod(str,&str));
    if( *(str++) != ',' )
      return 2;
    fy1 = DOUBLE_FIX(strtod(str,&str));
    if( (*str != ' ') && (*str != 0) )
      return 1;
  }

  if( !toolValue(array,"color",descriptor(ID_SCANMODE),&color) )
    return 2;

  if( !toolValue(array,"speed",descriptor(ID_SPEED),&speed) )
    return 2;

  if( !toolValue(array,"resolution",descriptor(ID_RESOLUTION),&resolution) )
    return 3;

  if( !toolValue(array,"brightness",descriptor(ID_BRIGHTNESS),&brightness) )
    return 4;

  if( !toolValue(array,"contrast",descriptor(ID_CONTRAST),&contrast) )
    return 5;

  if( !toolValue(array,"shadow",descriptor(ID_BLACKLEVEL),&shadow) )
    return 6;

  if( !toolValue(array,"highlight",descriptor(ID_WHITELEVEL),&highlight) )
    return 7;

  if( !toolValue(array,"pattern",descriptor(ID_HALFTONEPATTERN),&halftonePattern) )
    return 8;

  if( !toolValue(array,"time",descriptor(ID_EXPOSURETIME),&exposureTime) )
    return 9;

  if( !toolValue(array,"negative",descriptor(ID_NEGATIVE),&negative) )
    return 9;

  if( !toolValue(array,"threshold",descriptor(ID_THRESHOLD),&midtone) )
    return 11;

  if( !toolValue(array,"gamma",descriptor(ID_ANALOGGAMMA),&gamma) )
    return 12;

  if( !toolValue(array,"source",descriptor(ID_SCAN_SOURCE),&source) )
    return 13;

  if( str = FindToolType(array,"compress") )
  {
    if( MatchToolValue(str,"on") )
      compress = 1;
    else if( MatchToolValue(str,"off") )
      compress = 0;
    else
      return 14;
  }
  else
    compress = 1;

  if( !(outputFile = FindToolType(array,"file")) )
    outputFile = "ram:scanner.ilbm";
    
  return 0;
}

struct OptionDescriptor* descriptor(OptionId id)
{
  int i;
  
  for( i = 0 ; i < option.so_optionNum ; i++ )
  {
    if( id == option.so_descriptor[i].od_optionID )
      return &option.so_descriptor[i];
  }

  return NULL;
}

BOOL toolValue(char** array,char* matchStr,struct OptionDescriptor* descr,int* val)
{
  char* toolStr;
  
  if( descr == NULL )
    return TRUE;      
  
  toolStr = FindToolType(array,matchStr);
  
  if( toolStr == NULL )
    return TRUE;
    
  switch( descr->od_valueType )
  {
    case TYPE_BOOL:
      if( MatchToolValue(toolStr,"on") )
      {
        *val = 1;
        return TRUE;
      }
      else if( MatchToolValue(toolStr,"off") )
      {
        *val = 0;
        return TRUE;
      }
      else
        return FALSE;
    case TYPE_INT:
    case TYPE_FIXED:
      if( descr->od_valueType == TYPE_INT )
        *val = strtol(toolStr,NULL,10);
      else
        *val = DOUBLE_FIX(strtod(toolStr,NULL));
        
      switch( descr->od_constraintType )
      {
        case CONSTRAINT_RANGE:
          if( *val < descr->od_constraint.numberRange->min )
            *val = descr->od_constraint.numberRange->min;
          else if( *val > descr->od_constraint.numberRange->max )
            *val = descr->od_constraint.numberRange->max;
          else if( descr->od_constraint.numberRange->quant > 0 )
          {
            int n = descr->od_constraint.numberRange->min;
            
            while( *val > n + descr->od_constraint.numberRange->quant )
              n += descr->od_constraint.numberRange->quant;
            *val = n;
          }
          return TRUE;
        case CONSTRAINT_WORD_LIST:
          {
            int i;
            
            for( i = 1 ; i < descr->od_constraint.numberList[0] ; i++ )
            {
              if( *val < descr->od_constraint.numberList[i+1] )
              {
                *val = descr->od_constraint.numberList[i];
                return TRUE;
              }
            }
            *val = descr->od_constraint.numberList[descr->od_constraint.numberList[0]];
            return TRUE;
          }
        default:
          return FALSE;
      }
      
      break;
    case TYPE_STRING:
      *val = 0;
      
      while( descr->od_constraint.stringList[*val] && strcmpi(toolStr,descr->od_constraint.stringList[*val]) )
        (*val)++;
      if( descr->od_constraint.stringList[*val] )
        return TRUE;
      else
        return FALSE;
  }
}
