int
main(int argc, char* argv[])
{
  const char* const my_name = path_tail(argv[0]);
  if (argc < 7 || argc > 8)
  {
    usage(cout, my_name);
    return EXIT_FAILURE;
  }
  try
  {
    const char* const edge_size_string = argv[1];
    vet_as_int(edge_size_string, "N", "the length of the sides of the"
      " flattened probe cube");
    size_t N = atoi(edge_size_string);
    if (N < 1)
      throw ICC_tool_exception("length of the sides of the flattened probe must"
                               " be positive (and really should be at least 11,"
                               " preferably considerably more than that.)");
    
    const char * const grabbed_probe_values_pathname = argv[2];
    vet_input_file_pathname(grabbed_probe_values_pathname,
      "probe", "the pathname of the readable,"
      " nonzero-length text file containing the values extracted from the"
      " screen grab of the flattened probe cube");
    vector<DeviceRGB> device_RGBs
      (RGBs_from_probe_pathname(grabbed_probe_values_pathname));
    
    const char* const monitor_profile_pathname = argv[3];
    vet_input_file_pathname(monitor_profile_pathname,
      "monitor", "the pathname of the ICC profile which was active"
      " for the screen on which the flattened probe cube screen grab"
      " was made");
                                                               
    const char* const input_profile_description = argv[4];
    
    const char* const input_profile_pathname = argv[5];
    vet_output_file_pathname(input_profile_pathname,
      "path", "the pathname of the file to which the created CLUT input"
      " profile will be written, with the directory component of that"
      " pathname being writable by the current user");

    const char* const copyright_holder = argv[6];
    char year[5];
    const time_t now = time(NULL);
    strftime(year, 5, "%G", localtime(&now));
    const char* const copyright_pattern("copyright (c) %s %s");
    char copyright[256];
    sprintf(copyright, copyright_pattern, copyright_holder, year);
    
    bool pretransform_pathname_specified = argc == 8;
    const char* pretransform_pathname = "";
    if (pretransform_pathname_specified)
    {
      pretransform_pathname = argv[7];
      vet_input_file_pathname(pretransform_pathname,
        "pretransform",
        "pathname containing pretransform curve expressed as lines of triplets"
        " of shaper LUT contents in [0, 1024)");
    }
    
    CIccProfile* monitor_profile = ReadIccProfile(monitor_profile_pathname);
    // +++ check result here
    
    CIccCmm* cmm = new CIccCmm();
    icRenderingIntent monitor_rendering_intent
      = static_cast<icRenderingIntent>(monitor_profile->m_Header.renderingIntent);
    if (cmm->AddXform(monitor_profile_pathname,
                      monitor_rendering_intent) != icCmmStatOk)
    {
      ostringstream s;
      s << "Can't set profile `" << monitor_profile_pathname
        << "' as initial CMM profile";
      throw runtime_error(s.str());
    }
    if (cmm->Begin() != icCmmStatOk)
    {
      throw runtime_error("Can't start CMM");
    }
    
    vector<CIEXYZ> PCS_XYZs_from_probe(XYZs_from_monitor_RGBs(device_RGBs, cmm));
    
    CIccProfile input_profile;
    input_profile.InitHeader();
    input_profile.m_Header.deviceClass     = icSigInputClass;
    input_profile.m_Header.platform        = icSigMacintosh;
    input_profile.m_Header.colorSpace      = icSigRgbData;
    input_profile.m_Header.pcs             = icSigXYZData;
    input_profile.m_Header.attributes      = static_cast<icUInt64Number>(icTransparency);
    input_profile.m_Header.renderingIntent = monitor_profile->m_Header.renderingIntent;
    
    // Required tags for an N-component LUT-based input profile, as layed out in
    // the ICC spec [sections 8.2 and 8.3.2] are:
    //   profileDescriptionTag
    //   copyrightTag
    //   mediaWhitePointTag
    //   chromaticAdaptationTag
    //   A2B0 tag
    // As it happens, not only are those ordered by their appearance in section
    // 8.2 and 8.3.2, they are pretty much also ordered in increasing complexity.
    
    // profileDescriptionTag
    CIccLocalizedUnicode USAEnglishDesc;
    USAEnglishDesc.SetText((string("(grabbed) ") + input_profile_description).c_str());
    CIccTagMultiLocalizedUnicode* descriptionTag = new CIccTagMultiLocalizedUnicode;
    descriptionTag->m_Strings = new CIccMultiLocalizedUnicode; // dtor does deletion
    descriptionTag->m_Strings->push_back(USAEnglishDesc);
    input_profile.AttachTag(icSigProfileDescriptionTag, descriptionTag);
    
    // copyrightTag
    CIccLocalizedUnicode USAEnglishCopyright;
    USAEnglishCopyright.SetText(copyright);
    CIccTagMultiLocalizedUnicode* copyrightTag = new CIccTagMultiLocalizedUnicode;
    copyrightTag->m_Strings = new CIccMultiLocalizedUnicode; // dtor does deletion
    copyrightTag->m_Strings->push_back(USAEnglishCopyright);
    input_profile.AttachTag(icSigCopyrightTag, copyrightTag);
    
    CIccTagXYZ* white_point_tag
      = static_cast<CIccTagXYZ*>(monitor_profile->FindTag(icSigMediaWhitePointTag));
    input_profile.AttachTag(icSigMediaWhitePointTag, white_point_tag);
    
    CIccTagXYZ* black_point_tag
      = static_cast<CIccTagXYZ*>(monitor_profile->FindTag(icSigMediaBlackPointTag));
    // This is not a required tag, but if it's there, carry it over.
    if (black_point_tag != NULL)
      input_profile.AttachTag(icSigMediaBlackPointTag, black_point_tag);
    
    CIccTagXYZ* luminance_tag
      = static_cast<CIccTagXYZ*>(monitor_profile->FindTag(icSigLuminanceTag));
    if (luminance_tag != NULL)
      input_profile.AttachTag(icSigLuminanceTag, luminance_tag);
      
    
    CIccTagS15Fixed16* monitor_chromatic_adaptation_tag
      = static_cast<CIccTagS15Fixed16*>(monitor_profile->FindTag(icSigChromaticAdaptationTag));
    input_profile.AttachTag(icSigChromaticAdaptationTag,
                            monitor_chromatic_adaptation_tag);
    
    if (PCS_XYZs_from_probe.size() != N * N * N)
    {
      ostringstream s;
      s << "Edge size " << N << " implies a probe file with " << N *N * N
        << " entries, but the file provided contains "
        << PCS_XYZs_from_probe.size();
      throw runtime_error(s.str());
    }
    // +++ input shaper LUT someday
    CIccTagLut16* A2B0_tag = make_A2Bx_tag(N, PCS_XYZs_from_probe,
                                           pretransform_pathname);
    input_profile.AttachTag(icSigAToB0Tag, A2B0_tag);
    
    
    //  CLUT* AToB0CLUT = new CLUT();
    //  CIccTagLut16* AToB0Tag
    //    = AToB0CLUT->makeAToBxTag(size, measuredXYZ, flare, illuminant, CATToD50,
    //                              inputShaperGamma, inputShaperFilename,
    //                              adaptedMediaWhite, LABPCS);
    //  BlackScaler blackScaler(size, measuredXYZ, adaptedMediaBlack, adaptedMediaWhite);
    //  AToB0CLUT->Iterate(&blackScaler);
    //  profile.AttachTag(icSigAToB0Tag, AToB0Tag); // the A2B0 tag
    
    //Verify things
    string validationReport;
    icValidateStatus validationStatus = input_profile.Validate(validationReport);
    
    switch (validationStatus)
    {
      case icValidateOK:
        break;
        
      case icValidateWarning:
        clog << "Profile validation warning" << endl
             << validationReport;
        break;
        
      case icValidateNonCompliant:
        clog << "Profile non compliancy" << endl
             << validationReport;
        break;
        
      case icValidateCriticalError:
      default:
        clog << "Profile Error" << endl
             << validationReport;
    }
    
    // Out it goes
    CIccFileIO out;
    out.Open(input_profile_pathname, "w+");
    input_profile.Write(&out);
    out.Close();
    return EXIT_SUCCESS;
  }
  catch (const exception& e)
  {
    cout << "error: " << e.what() << endl;
    return EXIT_FAILURE;
  }
}
bool Apply(const char *szSrcImage,
           const char *szSrcProfile,
           const char *szDstProfile,
           const char *szDstImage,
           int nIntent)
{
  unsigned long i, j, k, sn, sphoto, dn, photo, space;
  CTiffImg SrcImg, DstImg;
  CIccCmm cmm;
  unsigned char *sptr, *dptr;
  bool bSuccess = true;
  bool bConvert = false;

  if (cmm.AddXform(szSrcProfile, nIntent<0 ? icUnknownIntent : (icRenderingIntent)nIntent/*, icInterpTetrahedral*/)) {
    printf("Invalid Profile:  %s\n", szSrcProfile);
    return false;
  }
  
  if (szDstProfile && *szDstProfile && cmm.AddXform(szDstProfile/*, icUnknownIntent, icInterpTetrahedral*/)) {
    printf("Invalid Profile:  %s\n", szDstProfile);
    return false;
  }

  if (cmm.Begin() != icCmmStatOk) {
    printf("Invalid Profile:\n  %s\n  %s'\n", szSrcProfile, szDstProfile);
    return false;
  }

  if (!SrcImg.Open(szSrcImage)) {
    printf("Invalid Tiff file - '%s'\n", szSrcImage);
    return false;
  }
  sn = SrcImg.GetSamples();
  sphoto = SrcImg.GetPhoto();
  space = cmm.GetSourceSpace();

  if (SrcImg.GetBitsPerSample()!=8 ||
      !((space==icSigRgbData && sn==3 && sphoto==PHOTO_MINISBLACK) ||
        (space==icSigLabData && sn==3 && sphoto==PHOTO_CIELAB) ||
        (space==icSigXYZData && sn==3 && sphoto==PHOTO_CIELAB) ||
        (space==icSigCmykData && sn==4 && sphoto==PHOTO_MINISWHITE) ||
        (space==icSigMCH4Data && sn==4 && sphoto==PHOTO_MINISWHITE) ||
        (space==icSigMCH5Data && sn==5 && sphoto==PHOTO_MINISWHITE) ||
        (space==icSigMCH6Data && sn==6 && sphoto==PHOTO_MINISWHITE))) {
    printf("Invalid source profile/image pixel format\n");
    return false;
  }

  switch (cmm.GetDestSpace()) {
  case icSigRgbData:
    photo = PHOTO_MINISBLACK;
    dn = 3;
    break;

  case icSigCmyData:
    photo = PHOTO_MINISWHITE;
    dn = 3;
    break;

  case icSigXYZData:
    bConvert = true;
    //Fall through - No break here

  case icSigLabData:
    photo = PHOTO_CIELAB;
    dn = 3;
    break;

  case icSigCmykData:
  case icSig4colorData:
    photo = PHOTO_MINISWHITE;
    dn = 4;
    break;

  case icSig5colorData:
    photo = PHOTO_MINISWHITE;
    dn = 5;
    break;

  case icSig6colorData:
    photo = PHOTO_MINISWHITE;
    dn = 6;
    break;

  case icSig7colorData:
    photo = PHOTO_MINISWHITE;
    dn = 7;
    break;

  case icSig8colorData:
    photo = PHOTO_MINISWHITE;
    dn = 8;
    break;

  default:
    printf("Invalid destination profile/image pixel format\n");
    return false;
  }

  if (!DstImg.Create(szDstImage, SrcImg.GetWidth(), SrcImg.GetHeight(), 8, photo, dn, SrcImg.GetXRes(), SrcImg.GetYRes(), false)) {
    printf("Unable to create Tiff file - '%s'\n", szDstImage);
    return false;
  }

  unsigned char *pSBuf = (unsigned char *)malloc(SrcImg.GetBytesPerLine());
  unsigned char *pDBuf = (unsigned char *)malloc(DstImg.GetBytesPerLine());
  icFloatNumber Pixel[16];

  if (!pSBuf) {
    printf("Out of Memory!\n");
    return false;
  }

  if (!pDBuf) {
    printf("Out of Memory!\n");
    free(pSBuf);
    return false;
  }

  for (i=0; i<SrcImg.GetHeight(); i++) {
    if (!SrcImg.ReadLine(pSBuf)) {
      bSuccess = false;
      break;
    }
    for (sptr=pSBuf, dptr=pDBuf, j=0; j<SrcImg.GetWidth(); j++, sptr+=sn, dptr+=dn) {
      if (sphoto==PHOTO_CIELAB) {
        Pixel[0] = (icFloatNumber)sptr[0] / 255.0f;
        Pixel[1] = ((icFloatNumber)((signed char)sptr[1]) + 128.0f) / 255.0f;
        Pixel[2] = ((icFloatNumber)((signed char)sptr[2]) + 128.0f) / 255.0f;

        if (space==icSigXYZData) {
          icLabFromPcs(Pixel);

          icLabtoXYZ(Pixel);

          icXyzToPcs(Pixel);
        }
      }
      else {
        for (k=0; k<sn; k++) {
          Pixel[k] = (icFloatNumber)sptr[k] / 255.0f;
        }
      }

      cmm.Apply(Pixel, Pixel);

      if (photo==PHOTO_CIELAB) {
        if (bConvert) {
          icXyzFromPcs(Pixel);

          icXYZtoLab(Pixel);

          icLabToPcs(Pixel);
        }
        dptr[0] = (unsigned char)(UnitClip(Pixel[0]) * 255.0 + 0.5);
        dptr[1] = (unsigned char)(UnitClip(Pixel[1]) * 255.0 - 128.0);
        dptr[2] = (unsigned char)(UnitClip(Pixel[2]) * 255.0 - 128.0);
      }
      else {
        for (k=0; k<dn; k++) {
          dptr[k] = (unsigned char)(UnitClip(Pixel[k]) * 255.0 + 0.5);
        }
      }
    }
    if (!DstImg.WriteLine(pDBuf)) {
      bSuccess = false;
      break;
    }
  }

  SrcImg.Close();

  free(pSBuf);
  free(pDBuf);

  return bSuccess;
}