text: // Copyright 2017 Google Inc.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //      http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  
  #include <stdio.h>
  #include <fstream>
  #include <notify.h>
  #include <khConstants.h>
  #include <khGetopt.h>
  #include <khSpawn.h>
  #include <khFileUtils.h>
  #include <khStringUtils.h>
  #include <autoingest/.idl/storage/AssetDefs.h>
  #include <autoingest/.idl/gstProvider.h>
  #include <autoingest/khAssetManagerProxy.h>
  #include <khgdal/khgdal.h>
  
  
  AssetDefs::Type AssetType = AssetDefs::Invalid;
  
  void
  usage(const std::string &progn, const char *msg = 0, ...)
  {
    if (msg) {
      va_list ap;
      va_start(ap, msg);
      vfprintf(stderr, msg, ap);
      va_end(ap);
      fprintf(stderr, "\n");
    }
  
    std::string ext = (AssetType == AssetDefs::Imagery) ? ".kip" : ".ktp";
  
    fprintf(stderr,
            "\nusage: %s [options] [--meta <key>=<value>]... -o <assetname> { --filelist <file> | <sourcefile> ...}\n"
            "   Source filenames may be specified on the commandline or in a filelist.\n"
            "   Supported options are:\n"
            "      --help | -?:      Display this usage message\n",
            progn.c_str());
    if (AssetType == AssetDefs::Terrain) {
      fprintf(stderr,
              "      --scale=<num>:    Multiply elevation samples by this number\n"
              "                        to convert them to meters.\n"
              "      --feet:           Shorthand for --scale=0.3048\n"
  	    "      --nonegatives:    Change all negative elevations to 0\n");
    }
    fprintf(stderr,
            "      --mercator : use mercator projection\n"
            "      --flat : use flat(Plate Carre) projection (default)\n"
            "      --nomask:         Don't use an alpha mask\n"
            "      --havemask:       Use existing \"-mask\" file (single source only)\n"
            "      --srs <srs>:      Use <srs> instead of embedded SRS\n"
            "      --lut <lutfile>:  LUT file to apply\n"
            "      --provider <key>: Key from provider database\n"
            "      --sourcedate <string>:  String representing UTC date of source.\n"
            "                              Default: \"0000-00-00T00:00:00Z\" (YYYY-MM-DDTHH:MM:SSZ).\n"
            "   If multiple source files are specified, they will be mosaiced together.\n"
            "   If no source files is specified, it will detect change of original source files, \n"
            "   and update the asset if necessary.\n"
            "   In that case the following options are also available:\n"
            "      --mosaicfill a,b,... :  Band values to use as fill during mosaic\n"
            "      --mosaictolerance <num>: Tolerance used when comparing pixels against\n"
            "                               mosaic fill value\n"
            "   If neither --nomask nor --havemask is specified a mask will be\n"
            "   automatically generated. In that case, the following options are\n"
            "   also available:\n"
            "      --band <num>:     Band number to use to generate mask\n"
            "      --feather <num>:  Pixel size of feather used in autogenerated mask\n"
            "      --holesize <num>: Regions larger than this that match the fill values\n"
            "                        will be masked out\n"
            "   To limit extent, the following options are also available:\n"
            "      --north_boundary <double> Crop to north_boundary"
            " (latitude in decimal deg)\n"
            "      --south_boundary <double> Crop to south_boundary"
            " (latitude in decimal deg)\n"
            "      --east_boundary <double> Crop to east_boundary"
            " (longitude in decimal deg)\n"
            "      --west_boundary <double> Crop to west_boundary"
            " (longitude in decimal deg)\n"
            );
    if (AssetType == AssetDefs::Terrain) {
      fprintf(stderr,
              "      --fillvalues <list>: Comma-separated list of fill elevations and\n"
              "                           ranges with no spaces (e.g. \"-9999,-999.9:0\")\n");
    } else {
      fprintf(stderr,
              "      --masktolerance <num>: Tolerance used when comparing pixels\n"
              "                             against mask fill value\n"
              "      --whitefill:      Look for white as well as fill color when\n"
              "                        generating mask\n");
    }
    fprintf(stderr,
            "   A single %s file can be specified as <sourcefile> in order to\n"
            "   import existing Keyhole Raster Products into the system. In this case\n"
            "   the mosaic and automask options are irrelevant\n",
            ext.c_str());
    exit(1);
  }
  
  
  int
  main(int argc, char *argv[]) {
  
    try {
      // figure out if I'm doing Imagery or Terrain
      std::string progname = argv[0];
      if (progname.find("terrain") != std::string::npos)
        AssetType = AssetDefs::Terrain;
      else
        AssetType = AssetDefs::Imagery;
  
  
      // process commandline options
      int argn;
      bool help = false;
      bool debug = false;
      bool flat = false;
      bool mercator = false;
      double north_boundary = 90.0;
      double south_boundary = -90.0;
      double east_boundary = 180.0;
      double west_boundary = -180.0;
  
      RasterProductImportRequest req(AssetType);
      std::string providerkey;
      // By default set source date to all zeros (format: YYYY-MM-DD).
      std::string sourcedate = kUnknownDate;
      std::string filelistname;
      khGetopt options;
      options.flagOpt("debug", debug);
      options.flagOpt("help", help);
      options.flagOpt("?", help);
      options.opt("output", req.assetname);
      options.opt("provider", providerkey);
      options.opt("sourcedate", sourcedate);
      options.mapOpt("meta", req.meta.map);
      options.opt("filelist", filelistname, &khGetopt::FileExists);
      options.flagOpt("mercator", mercator);
      options.flagOpt("flat", flat);
      options.setExclusive("flat", "mercator");
      options.flagOpt("nomask", req.config.nomask);
      options.flagOpt("havemask", req.config.havemask);
      options.opt("srs", req.config.srs);
  
      // maskgen stuff
      options.opt("feather", req.config.maskgenConfig.feather);
      options.opt("band",    req.config.maskgenConfig.band);
      options.opt("masktolerance", req.config.maskgenConfig.threshold);
      options.opt("holesize", req.config.maskgenConfig.holesize);
      if (AssetType != AssetDefs::Terrain) {
        options.flagOpt("whitefill", req.config.maskgenConfig.whitefill);
      }
  
      // mosaic stuff
      options.opt("fill", req.config.fill);
      options.opt("mosaicfill", req.config.fill);
      options.opt("mosaictolerance", req.config.mosaicFillTolerance);
  
      // virtual raster stuff
      options.opt("lut", req.config.lutfile, &khGetopt::FileExists);
  
      if (AssetType == AssetDefs::Terrain) {
        options.setOpt("feet", req.config.scale, 0.3048);
        options.opt("scale", req.config.scale);
        options.flagOpt("nonegatives", req.config.clampNonnegative);
        options.opt("fillvalues", req.config.maskgenConfig.nodata);
      }
  
      options.opt("north_boundary", north_boundary);
      options.opt("south_boundary", south_boundary);
      options.opt("east_boundary", east_boundary);
      options.opt("west_boundary", west_boundary);
  
      if (!options.processAll(argc, argv, argn)) {
        usage(progname);
      }
      if (help) {
        usage(progname);
      }
  
      // simple option validation
      if (req.assetname.empty()) {
        usage(progname, "<assetname> not specified");
      }
      if ((!flat && !mercator) || AssetType == AssetDefs::Terrain) {
        flat = true;
      }
      req.config.useMercatorProjection = mercator;
      if (north_boundary <= south_boundary) {
        notify(NFY_FATAL, "north_boundary <= south_boundary!.\n");
      }
      if (east_boundary <= west_boundary) {
        notify(NFY_FATAL, "east_boundary <= west_boundary!.\n");
      }
      req.config.north_boundary = north_boundary;
      req.config.south_boundary = south_boundary;
      req.config.east_boundary = east_boundary;
      req.config.west_boundary = west_boundary;
  
      req.assetname = AssetDefs::NormalizeAssetName(req.assetname, AssetType,
          mercator ? kMercatorProductSubtype : kProductSubtype);
      if (providerkey.size()) {
        gstProviderSet providers;
        if (!providers.Load()) {
          notify(NFY_FATAL, "Unable to load provider list");
        }
        for (uint i = 0 ; i < providers.items.size(); ++i) {
          if (providers.items[i].key == providerkey) {
            req.config.provider_id_ = providers.items[i].id;
          }
        }
        if (req.config.provider_id_ == 0) {
          notify(NFY_FATAL, "Provider '%s' not found", providerkey.c_str());
        }
      }
      if (sourcedate.size()) {
        // Parse "sourcedate" to standard UTC ISO 8601 time; "ParseUTCTime"
        // also accepts old time string such as "YYYY-MM-DD" in order not to
        // break customers' old scripts.
        struct tm ts;
        if (sourcedate == kUnknownDate) {
          // default input, set to standard metadata value.
          req.meta.SetValue("sourcedate", kUnknownDateTimeUTC);
        } else if (ParseUTCTime(sourcedate, &ts)) {
          // Reformat into standard UTC ISO 8601 time string.
          req.meta.SetValue("sourcedate", GetUTCTimeString(ts));
        } else {
          notify(NFY_FATAL, "Sourcedate '%s' is not a valid UTC ISO 8601 time"
              " string.", sourcedate.c_str());
        }
      }
      if (req.config.lutfile.size()) {
        req.config.lutfile = khAbspath(req.config.lutfile);
      }
  
      if (!req.config.srs.empty()) {
        OGRSpatialReference ogrSRS;
        // will throw on error
        InterpretSRSString(req.config.srs, ogrSRS);
      }
  
  
      // get the source files, will do existence validation
      khFileList filelist;
      try {
        if (filelistname.size())
          filelist.AddFromFilelist(filelistname);
        if (argn < argc)
          filelist.AddFiles(&argv[argn], &argv[argc]);
      } catch (const std::exception &e) {
        usage(progname, e.what());
      } catch (...) {
        usage(progname, "Unknown error with source files");
      }
      if (filelist.empty()) {
        bool updated = false;
        QString error;
        if (!khAssetManagerProxy::ProductReImport(req.assetname, updated,
                                                  error)) {
          notify(NFY_FATAL, "%s", error.latin1());
        }
        if (updated) {
          printf("The asset is updated: %s\n", req.assetname.c_str());
        } else {
          printf("The asset is up-to-date: %s\n", req.assetname.c_str());
        }
        return 0;
      }
  
      // add the source files, will normallize & do existence validation
      req.sources.AddFilesAndOrURIs(filelist.begin(), filelist.end());
  
      // ***** do some more validation *****
      if ((filelist.size() > 1) || req.config.lutfile.size()) {
        if (req.config.havemask) {
          usage(progname, "--havemask only supported with single source files");
        }
  
        // validate mosaic inputs
        CmdLine cmdline;
        cmdline << "gevirtualraster" << "--validate" << "-o" << "foo.khvr";
        if (req.config.fill.size()) {
          cmdline << "--fill" << req.config.fill;
        }
        if (req.config.lutfile.size()) {
          cmdline << "--lut" << req.config.lutfile;
        }
  
        // write tmp filelist and pass files to gevirtualraster
        // via --filelist
        khTmpFileGuard fileguard;
        {
          std::ofstream out(fileguard.name().c_str());
          for (std::vector<std::string>::const_iterator f
                 = filelist.begin(); f != filelist.end(); ++f) {
            out << *f << std::endl;
          }
        }
        cmdline << "--filelist" << fileguard.name();
  
        if (!cmdline.System()) {
          (void)khUnlink(fileguard.name());
          notify(NFY_FATAL, "Source files incompatible for mosaic");
        }
  
      } else {
        if (req.config.fill.size()) {
          usage(progname, "--fill only supported with multiple source files");
        }
  
        // see if we're importing product files
        if (!khIsURI(filelist[0]) &&
            khHasExtension(filelist[0], (AssetType == AssetDefs::Imagery) ?
                           ".kip" : ".ktp")) {
          req.config.sourceIsProduct = true;
        }
      }
  
      // now send the request
      if (debug) {
        std::string reqstr;
        req.SaveToString(reqstr, "");
        printf("%s\n", reqstr.c_str());
      } else {
        QString error;
        bool return_status =
          mercator ? khAssetManagerProxy::MercatorRasterProductImport(req, error)
                   : khAssetManagerProxy::RasterProductImport(req, error);
        if (!return_status) {
          notify(NFY_FATAL, "%s", error.latin1());
        }
      }
    } catch (const std::exception &e) {
      notify(NFY_FATAL, "%s", e.what());
    } catch (...) {
      notify(NFY_FATAL, "Unknown error");
    }
    return 0;
  }
  