/**********************************************************************
pkreclassogr.cc: program to replace field values of attributes in vector dataset
Copyright (C) 2008-2017 Pieter Kempeneers

This file is part of pktools

pktools is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

pktools 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with pktools.  If not, see <http://www.gnu.org/licenses/>.
***********************************************************************/
#include <assert.h>
#include <map>
#include "base/Optionpk.h"
#include "imageclasses/ImgReaderOgr.h"
#include "imageclasses/ImgWriterOgr.h"
#include "imageclasses/ImgReaderGdal.h"
#include "imageclasses/ImgWriterGdal.h"

/******************************************************************************/
/*! \page pkreclassogr pkreclassogr
  program to replace field values of attributes in vector dataset
  ## SYNOPSIS

  <code>
  Usage: pkreclassogr -i input [-c from -r to]* -o output
  </code>

  \section pkreclassogr_options Options
  - use either `-short` or `--long` options (both `--long=value` and `--long value` are supported)
  - short option `-h` shows basic options only, long option `--help` shows all options
  |short|long|type|default|description|
  |-----|----|----|-------|-----------|
  | i      | input                | std::string |       |Input vector dataset |
  | nodata | nodata               | int  | 0     |nodata value to put in vector dataset if not valid (0) |
  | code   | code                 | std::string |       |Recode text file (2 columns: from to) |
  | c      | class                | std::string |       |list of classes to reclass (in combination with reclass option) |
  | r      | reclass              | std::string |       |list of recoded classes (in combination with class option) |
  | o      | output               | std::string |       |Output vector dataset |
  | n      | fname                | std::string | label |field name of the shape file to be replaced |
  | v      | verbose              | short | 0     |verbose |

  Usage: pkreclassogr -i input [-c from -r to]* -o output


  Examples
  ========
  Some examples how to use pkreclassogr can be found \ref examples_pkreclassogr "here"
**/

using namespace std;

int main(int argc, char *argv[])
{
  Optionpk<string> input_opt("i", "input", "Input vector dataset");
  Optionpk<string> output_opt("o", "output", "Output file");
  Optionpk<int> nodata_opt("nodata", "nodata", "nodata value to put in vector dataset if not valid (0)", 0);
  Optionpk<string> code_opt("code", "code", "Recode text file (2 columns: from to)");
  Optionpk<string> class_opt("c", "class", "list of classes to reclass (in combination with reclass option)");
  Optionpk<string> reclass_opt("r", "reclass", "list of recoded classes (in combination with class option)");
  Optionpk<string> fieldname_opt("n", "fname", "field name of the shape file to be replaced", "label");
  Optionpk<short> verbose_opt("v", "verbose", "verbose", 0);

  bool doProcess;//stop process when program was invoked with help option (-h --help)
  try{
    doProcess=input_opt.retrieveOption(argc,argv);
    nodata_opt.retrieveOption(argc,argv);
    code_opt.retrieveOption(argc,argv);
    class_opt.retrieveOption(argc,argv);
    reclass_opt.retrieveOption(argc,argv);
    output_opt.retrieveOption(argc,argv);
    fieldname_opt.retrieveOption(argc,argv);
    verbose_opt.retrieveOption(argc,argv);
  }
  catch(string predefinedString){
    std::cout << predefinedString << std::endl;
    exit(0);
  }
  if(!doProcess){
    cout << endl;
    cout << "Usage: pkreclassogr -i input [-c from -r to]* -o output" << endl;
    cout << endl;
    std::cout << "short option -h shows basic options only, use long option --help to show all options" << std::endl;
    exit(0);//help was invoked, stop processing
  }

  if(input_opt.empty()){
    std::cerr << "No input file provided (use option -i). Use --help for help information" << std::endl;
    exit(0);
  }
  if(output_opt.empty()){
    std::cerr << "No output file provided (use option -o). Use --help for help information" << std::endl;
    exit(0);
  }

  // vector<short> bandVector;
  map<string,string> codemapString;//map with codes: codemapString[theKey(from)]=theValue(to)
  map<double,double> codemap;//map with codes: codemap[theKey(from)]=theValue(to)
  if(code_opt.size()){
    if(verbose_opt[0])
      cout << "opening code text file " << code_opt[0] << endl;
    ifstream codefile;
    codefile.open(code_opt[0].c_str());
    string theKey;
    string theValue;
    while(codefile>>theKey){
      codefile >> theValue;
      codemapString[theKey]=theValue;
      codemap[string2type<double>(theKey)]=string2type<double>(theValue);
    }
    codefile.close();
  }
  else{//use combination of class_opt and reclass_opt
    assert(class_opt.size()==reclass_opt.size());
    for(int iclass=0;iclass<class_opt.size();++iclass){
      codemapString[class_opt[iclass]]=reclass_opt[iclass];
      codemap[string2type<double>(class_opt[iclass])]=string2type<double>(reclass_opt[iclass]);
    }
  }
  assert(codemapString.size());
  assert(codemap.size());
  //if verbose true, print the codes to screen
  if(verbose_opt[0]){
    map<string,string>::iterator mit;
    cout << codemapString.size() << " codes used: " << endl;
    for(mit=codemapString.begin();mit!=codemapString.end();++mit)
      cout << (*mit).first << " " << (*mit).second << endl;
  }
  bool refIsRaster=false;
  ImgReaderOgr ogrReader;
  try{
    ogrReader.open(input_opt[0]);
  }
  catch(string errorString){
    refIsRaster=true;
  }
  if(!refIsRaster){
    if(verbose_opt[0])
      cout << "opening " << input_opt[0] << " for reading " << endl;
    // ImgReaderOgr ogrReader(input_opt[0]);
    if(verbose_opt[0])
      cout << "opening " << output_opt[0] << " for writing " << endl;
    ImgWriterOgr ogrWriter(output_opt[0],ogrReader);
    if(verbose_opt[0])
      cout << "copied layer from " << input_opt[0] << endl << flush;
    OGRFeatureDefn *poFDefn = ogrWriter.getLayer()->GetLayerDefn();
    //start reading features from the layer
    if(verbose_opt[0])
      cout << "reset reading" << endl;
    ogrReader.getLayer()->ResetReading();
    unsigned long int ifeature=0;
    if(verbose_opt[0])
      cout << "going through features" << endl << flush;
    while(true){
      //     while( (poFeature = ogrWriter.getLayer()->GetNextFeature()) != NULL ){
      OGRFeature *poFeature;
      poFeature=ogrReader.getLayer()->GetNextFeature();
      if(poFeature== NULL)
        break;
      OGRFeatureDefn *poFDefn = ogrWriter.getLayer()->GetLayerDefn();
      string featurename;
      for(int iField=0;iField<poFDefn->GetFieldCount();++iField){
        OGRFieldDefn *poFieldDefn = poFDefn->GetFieldDefn(iField);
        string fieldname=poFieldDefn->GetNameRef();
        if(fieldname==fieldname_opt[0]){
          string fromClass=poFeature->GetFieldAsString(iField);
          string toClass=fromClass;
          if(codemapString.find(fromClass)!=codemapString.end())
            toClass=codemapString[fromClass];
          poFeature->SetField(iField,toClass.c_str());
          if(verbose_opt[0])
            cout << "feature " << ifeature << ": " << fromClass << "->" << poFeature->GetFieldAsInteger(iField) << endl << flush;
          //             cout << "feature " << ifeature << ": " << fromClass << "->" << toClass << endl << flush;
        }
      }
      //do not forget to actually write feature to file!!!
      ogrWriter.createFeature(poFeature);
      OGRFeature::DestroyFeature( poFeature );
      ++ifeature;
    }
    if(verbose_opt[0])
      cout << "replaced " << ifeature << " features" << endl;
    ogrReader.close();
    ogrWriter.close();
  }
}
