/*=========================================================================

  Library:   CTK
 
  Copyright (c) 2010  Kitware 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.commontk.org/LICENSE

  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.
 
=========================================================================*/

// Qt includes
#include <QCoreApplication>
#include <QSettings>
#include <QStringList>
#include <QList>
#include <QFile>
#include <QDebug>

// CTK includes
#include "ctkVTKCommandOptions.h"
#include "ctkVisualizationVTKCoreCppTestsConfigure.h"

// STD includes
#include <cstdlib>
#include <iostream>

//-----------------------------------------------------------------------------
namespace
{
class ctkVTKCommandOptionsTest : public ctkVTKCommandOptions
{
public:
  typedef ctkVTKCommandOptions Superclass;
  ctkVTKCommandOptionsTest(QSettings* _settings):Superclass(_settings)
    {
    this->AwesomeTestEnabled = false;
    this->CurrentSettingsDisabled = false;
    this->TestIfCommandLineOverwriteSettings = false;
    this->TestIfDisableSettingsFlagWorks = false;
    this->GenerateTestData = false;
    this->TestIfIgnoreRestFlagWorks = false;
    }
  virtual ~ctkVTKCommandOptionsTest(){}

  // Added as public member for testing purposes ...
  bool          AwesomeTestEnabled;
  bool          CurrentSettingsDisabled;
  bool          TestIfCommandLineOverwriteSettings;
  bool          TestIfDisableSettingsFlagWorks;
  bool          TestIfIgnoreRestFlagWorks;
  bool          GenerateTestData;
  QString       WrongArgument;
  QStringList   MyListOfStrings;
  QString       MyString; 
  

  // Convenient method allowing to generate a INI file
  void generateIniFile()
    {
    std::cout << "Saving test data:" << CTKVTKCOMMANDOPTIONSTEST_SETTINGS << std::endl;
    
    QSettings newSettings(CTKVTKCOMMANDOPTIONSTEST_SETTINGS, QSettings::IniFormat);

    newSettings.clear();
    
    newSettings.setValue("enable-awesome-test", QVariant(true));

    newSettings.setValue("my-string", QVariant("Testing rocks !"));
    
    QStringList mystringlist;
    mystringlist << "Testing"; mystringlist << " is "; mystringlist << "awesome !";
    newSettings.setValue("my-list-of-strings", QVariant(mystringlist));
    }

  virtual void printAdditionalInfo()
    {
    Superclass::printAdditionalInfo();
    qDebug() << "ctkVTKCommandOptionsTest:" << this << endl
             << " CurrentSettingsDisabled:" << this->CurrentSettingsDisabled << endl
             << " WrongArgument:" << this->WrongArgument << endl
             << " TestIfCommandLineOverwriteSettings:" << this->TestIfCommandLineOverwriteSettings << endl
             << " TestIfDisableSettingsFlagWorks:" << this->TestIfDisableSettingsFlagWorks << endl
             << " TestIfIgnoreRestFlagWorks:" << this->TestIfIgnoreRestFlagWorks << endl
             << " GenerateTestData:" << this->GenerateTestData << endl
             << " SettingsFile:" << CTKVTKCOMMANDOPTIONSTEST_SETTINGS << endl
             << " MyListOfStrings:" << this->MyListOfStrings << endl
             << " MyString:" << this->MyString << endl;
    
    }
  
protected:

  /// Initialize arguments
  virtual void initialize()
    {
    this->Superclass::initialize();

    this->addBooleanArgument("--enable-awesome-test", "-eat", &this->AwesomeTestEnabled,
                             "Enables awesome test.");

    this->addBooleanArgument("--test-if-commandline-overwrite-settings", 0,
                             &this->TestIfCommandLineOverwriteSettings,
                             "Should we test if QListString setting is overwriten by the "
                             "corresponding command line argument.");

    this->addBooleanArgument("--test-if-disablesettings-flag-works", 0,
                             &this->TestIfDisableSettingsFlagWorks,
                             "Indicates if '--disable-settings' flags should be tested.");

    this->addBooleanArgument("--test-if-ignorerest-flag-works", 0,
                             &this->TestIfIgnoreRestFlagWorks,
                             "Indicates if '--ignore-rest' flags should be tested.");

    this->addBooleanArgument("--generate-test-data", 0, &this->GenerateTestData,
                             "Generates test data and save it to the file specified using '--settings-file'");

    this->addArgument("--my-list-of-strings", "-mlos", &this->MyListOfStrings,
                      "My list of strings.");

    this->addArgument("--my-string", "-ms", &this->MyString,
                      "My string.");
                      
    }

  // Disable current settings
  virtual void disableCurrentSettings()
    {
    this->CurrentSettingsDisabled = true;
    }

  /// This method is called when wrong argument is found. If it returns False, then
  /// the parsing will fail.
  virtual bool wrongArgument(const char* argument)
    {
    this->WrongArgument = QLatin1String(argument);
    return false;
    }
    
};
}

//-----------------------------------------------------------------------------
int ctkVTKCommandOptionsTest1(int argc, char * argv [] )
{
  QCoreApplication app(argc, argv);

  app.setOrganizationName("ctkVisualizationVTKCoreCppTests");
  app.setApplicationName("ctkVTKCommandOptionsTest1");

  // Check if settings file exists
  if (!QFile::exists(CTKVTKCOMMANDOPTIONSTEST_SETTINGS))
    {
    std::cerr << "Problem with configuration of the test - "
              << "Settings file doesn't exist: [" << CTKVTKCOMMANDOPTIONSTEST_SETTINGS << "]" << std::endl;
    return EXIT_FAILURE;
    }

  //std::cout << "CTKVTKCOMMANDOPTIONSTEST_SETTINGS:" << CTKVTKCOMMANDOPTIONSTEST_SETTINGS << std::endl;

  QSettings setting(CTKVTKCOMMANDOPTIONSTEST_SETTINGS, QSettings::IniFormat);

  ctkVTKCommandOptionsTest commandOptions(&setting);

  if (!commandOptions.parse(argc, argv))
    {
    commandOptions.printAdditionalInfo();
    std::cerr << "Problem parsing command line arguments.  Try with --help." << std::endl;
    return EXIT_FAILURE;
    }

  if (commandOptions.helpSelected())
    {
    std::cout << commandOptions.help().toStdString() << std::endl;
    return EXIT_SUCCESS;
    }
    
  // Convenient flags allowing to generate test data
  if (commandOptions.GenerateTestData)
    {
    commandOptions.generateIniFile();
    return EXIT_SUCCESS;
    }

  // Test if '--ignore-rest' flag works
  if (commandOptions.TestIfIgnoreRestFlagWorks)
    {
    if (!commandOptions.ignoreRest())
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - ignoreRest() should return True" << std::endl;
      return EXIT_FAILURE;
      }
    QStringList _ignoreArguments = commandOptions.ignoredArguments(); 
    if (_ignoreArguments.size() != 3)
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - "
                << "ignoredArguments() returns an incorrect QStringList." << std::endl
                << " expected size: 3" << std::endl
                << " current size: " << _ignoreArguments.size() << std::endl;
      
      return EXIT_FAILURE;
      }
    if (_ignoreArguments[0] != "--nothing=foo" ||
        _ignoreArguments[1] != "extra1" ||
        _ignoreArguments[2] != "/?")
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - "
                << "Content of list returned by ignoredArguments() is incorrect" << std::endl
                << "expected[0] => [--nothing=foo]" << std::endl
                << "expected[1] => [extra1]" << std::endl
                << "expected[2] => [/?]" << std::endl;
      for(int i=0; i < _ignoreArguments.size(); ++i)
        {
        std::cerr << "ignoreArguments[" << i << "] => ["
                  << qPrintable(_ignoreArguments[i]) << "]" << std::endl;
        }
      return EXIT_FAILURE;
      }
    }
  else
    {
    if (commandOptions.ignoreRest())
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - ignoreRest() should return False" << std::endl;
      return EXIT_FAILURE;
      }
    QStringList _ignoreArguments = commandOptions.ignoredArguments(); 
    if (_ignoreArguments.size() != 0)
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - "
                << "ignoredArguments() returns an incorrect QStringList." << std::endl
                << " expected size: 0" << std::endl
                << " current size: " << _ignoreArguments.size() << std::endl;
      
      return EXIT_FAILURE;
      }
    }

  // Test if '--disable-settings' flag works
  if (commandOptions.TestIfDisableSettingsFlagWorks)
    {
    if (!commandOptions.CurrentSettingsDisabled)
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - Settings should be disabled" << std::endl;
      return EXIT_FAILURE;
      }
    return EXIT_SUCCESS;
    }

  // Test case when a wrong argument is a parsing error
//   if (commandOptions.TestIfDisableSettingsFlagWorks)
//     {
//     if (!commandOptions.CurrentSettingsDisabled)
//       {
//       std::cerr << "Error with parse() function - Settings should be disabled" << std::endl;
//       return EXIT_FAILURE;
//       }
//     return EXIT_SUCCESS;
//     }

  // Since MyListOfStringss should have been updated using the settings file,
  // let's check if MyListOfStrings has the expected size
  if (commandOptions.MyListOfStrings.size() != 3)
    {
    commandOptions.printAdditionalInfo();
    std::cerr << "Error with parse() function - "
              << "MyListOfStrings expected size is 3 - Current size: "
              << commandOptions.MyListOfStrings.size() << std::endl;
    for(int i=0; i < commandOptions.MyListOfStrings.size(); ++i)
      {
      std::cerr << "MyListOfStrings[" << i << "] => ["
                << qPrintable(commandOptions.MyListOfStrings[i]) << "]" << std::endl;
      }
    return EXIT_FAILURE;
    }
      
  // Test if command line argument overwrite settings
  if (commandOptions.TestIfCommandLineOverwriteSettings)
    {
    if (commandOptions.MyListOfStrings[0] != "Testing with" ||
        commandOptions.MyListOfStrings[1] != " cmake is " ||
        commandOptions.MyListOfStrings[2] != "super awesome !")
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - "
                << "Content of MyListOfStrings is incorrect" << std::endl
                << "expected[0] => [Testing with]" << std::endl
                << "expected[1] => [ cmake is ]" << std::endl
                << "expected[2] => [super awesome !]" << std::endl;
      for(int i=0; i < commandOptions.MyListOfStrings.size(); ++i)
        {
        std::cerr << "MyListOfStrings[" << i << "] => ["
                  << qPrintable(commandOptions.MyListOfStrings[i]) << "]" << std::endl;
        }
      return EXIT_FAILURE;
      }
    // Check MyString value 
    if (commandOptions.MyString != "CMake rocks !")
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - Content of MyString is incorrect" << std::endl
                << "expected value is [CMake rocks !]" << std::endl
                << "current value is [" << qPrintable(commandOptions.MyString) << "]" << std::endl;
      }
    return EXIT_SUCCESS;
    }
  else
    {
    if (commandOptions.MyListOfStrings[0] != "Testing" ||
        commandOptions.MyListOfStrings[1] != " is " ||
        commandOptions.MyListOfStrings[2] != "awesome !")
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - "
                << "Content of MyListOfStrings is incorrect" << std::endl
                << "expected[0] => [Testing]" << std::endl
                << "expected[1] => [ is ]" << std::endl
                << "expected[2] => [awesome !]" << std::endl;
      for(int i=0; i < commandOptions.MyListOfStrings.size(); ++i)
        {
        std::cerr << "MyListOfStrings[" << i << "] => ["
                  << qPrintable(commandOptions.MyListOfStrings[i]) << "]" << std::endl;
        }
      return EXIT_FAILURE;
      }
    // Check MyString value 
    if (commandOptions.MyString != "Testing rocks !")
      {
      commandOptions.printAdditionalInfo();
      std::cerr << "Error with parse() function - Content of MyString is incorrect" << std::endl
                << "expected value is [Testing rocks !]" << std::endl
                << "current value is [" << qPrintable(commandOptions.MyString) << "]" << std::endl;
      }
    }

  if (commandOptions.CurrentSettingsDisabled)
    {
    commandOptions.printAdditionalInfo();
    std::cerr << "Error with parse() function - Settings should *NOT* be disabled" << std::endl;
    return EXIT_FAILURE;
    }

  if (!commandOptions.WrongArgument.isEmpty())
    {
    commandOptions.printAdditionalInfo();
    std::cerr << "Error with parse() function - "
              << "WrongArgument should *NOT* be set to ["
              << qPrintable(commandOptions.WrongArgument) << "]" << std::endl;
    return EXIT_FAILURE;
    }

  if (!commandOptions.AwesomeTestEnabled)
    {
    commandOptions.printAdditionalInfo();
    std::cerr << "Error with parse() function - AwesomeTestEnabled should be True" << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}
