/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include "stdafx.h"

#include <mforms/mforms.h>
#include <algorithm>

#include <grt/grt_manager.h>

#include "grt_wizard_form.h"

using namespace grtui;
using namespace mforms;


WizardForm::WizardForm(bec::GRTManager *mgr)
           : _grtm(mgr)
{
  _active_page= 0;
  
  signal_next_clicked().connect(sigc::mem_fun(this, &WizardForm::go_to_next));
  signal_back_clicked().connect(sigc::mem_fun(this, &WizardForm::go_to_back));
  signal_extra_clicked().connect(sigc::mem_fun(this, &WizardForm::extra_clicked));
  set_cancel_handler(sigc::mem_fun(this, &WizardForm::cancel));
  
  _values= grt::DictRef(mgr->get_grt());
}


WizardForm::~WizardForm() 
{
  for (std::vector<WizardPage*>::iterator iter= _pages.begin();
       iter != _pages.end(); ++iter)
    delete *iter;
}


void WizardForm::add_page(WizardPage *page)
{
  _pages.push_back(page);
}


WizardPage *WizardForm::get_page_with_id(const std::string &id)
{
  for (std::vector<WizardPage*>::const_iterator iter= _pages.begin();
       iter != _pages.end(); ++iter)
  {
    if ((*iter)->get_id() == id)
      return *iter;
  }
  return 0;
}


void WizardForm::reset()
{
  if (_active_page)
  {
    set_content(0);
    _turned_pages.clear();
    
    _active_page= 0;
  }
}


grt::DictRef WizardForm::run_modal(grt::DictRef &values)
{
  refresh_step_list();
  
  reset();

  switch_to_page(_pages.front(), true);

  run();

  return values;
}


void WizardForm::refresh_step_list()
{
  int i= 0;
  std::vector<std::string> steps;
  
  for (std::vector<WizardPage*>::const_iterator iter= _pages.begin();
       iter != _pages.end(); ++iter, ++i)
  {
    std::string title;
    
    if (*iter == _active_page)
      title= "*";
    else if (std::find(_turned_pages.begin(), _turned_pages.end(), *iter) != _turned_pages.end())
      title= ".";
    else
      title= "-";
    
    title.append((*iter)->get_short_title());
    steps.push_back(title);
  }
  
  set_step_list(steps);
}


void WizardForm::switch_to_page(WizardPage *page, bool advancing)
{
  if (_active_page)
  {
    if (page != _active_page)
      _active_page->leave(advancing);
  }

  if (_active_page != page)
  {
    if (advancing)
    {
      try
      {
        if (!page->pre_load())
          return;
      }
      catch (std::exception &exc)
      {
        Utilities::show_error(_("Error"), exc.what(), _("OK"));
        
        update_buttons();
        return;
      }
    }
    
    set_content(page);

    _active_page= page;

    _active_page->enter(advancing);

    update_buttons();
  }

  // Set new section text and let it resize to minimal size (which then gets layed out to our rules).
  set_heading(_active_page->get_title());

  update_buttons();
  
  refresh_step_list();
}


int WizardForm::get_active_page_number()
{
  return (int)(std::find(_pages.begin(), _pages.end(), _active_page) - _pages.begin());
}


void WizardForm::set_problem(const std::string &problem)
{
  _problem= problem;
  
  update_buttons();
}


void WizardForm::clear_problem()
{
  _problem.clear();
  update_buttons();
}


void WizardForm::update_buttons()
{
  if (_active_page)
  {
    std::string caption= _active_page->next_button_caption();
    if (caption.empty())
    {
      if (_active_page->next_closes_wizard())
        caption= _active_page->close_caption();
      else
        caption= "";
    }
    set_next_caption(caption);

    caption= _active_page->extra_button_caption();
    if (caption.empty())
      set_show_extra(false);
    else
    {
      set_show_extra(true);
      set_extra_caption(caption);
    }
    
    set_allow_next(_problem.empty() && _active_page->allow_next());
    set_allow_back(!_turned_pages.empty() && _active_page->allow_back());
    set_allow_cancel(_active_page->allow_cancel());
  }
}


WizardPage *WizardForm::get_next_page(WizardPage *current)
{
  for (std::vector<WizardPage*>::const_iterator iter= _pages.begin();
       iter != _pages.end(); ++iter)
  {
    if (*iter == current)
    {
      ++iter;
      if (iter != _pages.end())
        return *iter;
      return 0;
    }
  }
  return 0;
}


void WizardForm::finish()
{
  if (_active_page)
    _active_page->leave(true);

  close();
}


void WizardForm::extra_clicked()
{
  if (_active_page)
    _active_page->extra_clicked();
}


void WizardForm::go_to_next()
{
  if (_active_page)
  {
    bool advance_ok= false;
    
    set_allow_next(false);
    set_allow_back(false);
    set_allow_cancel(false);

    // catch uncaught exceptions
    try
    {
      advance_ok= _active_page->advance();
    }
    catch (std::exception &exc)
    {
      Utilities::show_error(_("Error"), exc.what(), _("OK"));
    }
    
    if (!advance_ok)
    {
      update_buttons();
      return;
    }

    _turned_pages.push_back(_active_page);
    
    if (_active_page->next_closes_wizard())
    {
      finish();
      return;
    }

    WizardPage *next_page= get_next_page(_active_page);
    if (!next_page)
    {
      g_warning("Trying to turn past the last page of the wizard");
      finish();
      return;
    }
    switch_to_page(next_page, true);
  }
}


void WizardForm::go_to_back()
{
  if (!_turned_pages.empty())
  {
    WizardPage *back_page= _turned_pages.back();
    _turned_pages.pop_back();

    clear_problem();
    
    switch_to_page(back_page, false);
  }
}


bool WizardForm::cancel()
{
//close();
  return true;
}


/** Set a option value in the application options dict. 
 
 The key will be prepended by a module specific prefix to avoid name collisions.
 */
void WizardForm::set_wizard_option(const std::string &key, const std::string &value)
{
}


std::string WizardForm::string_wizard_option(const std::string &key, const std::string &default_value)
{
  return default_value;
}

void WizardForm::set_wizard_option(const std::string &key, int value)
{
}


int WizardForm::int_wizard_option(const std::string &key, int default_value)
{
  return default_value;
}

//--------------------------------------------------------------------------------

WizardPage::WizardPage(WizardForm *form, const std::string &id)
: Box(false), _form(form), _id(id)
{
  set_padding(14);
  set_spacing(12);
}


void WizardPage::set_title(const std::string &title)
{
  _title= title;
}


void WizardPage::set_short_title(const std::string &title)
{
  _short_title= title;
}


void WizardPage::enable_file_browsing(mforms::TextEntry *entry, mforms::Button *button, mforms::FileChooserType type,
                                      const std::string &extensions, const std::string &directory_save_key)
{
  entry->signal_changed().connect(sigc::bind(sigc::mem_fun(this, &WizardPage::filename_changed), entry));
  button->signal_clicked().
    connect(sigc::bind(sigc::mem_fun(this, &WizardPage::browse_file_callback),
                       entry, type, extensions));
}


void WizardPage::filename_changed(mforms::TextEntry *entry)
{
  _file_entries_set_from_browse_dialog.erase(entry);
}


void WizardPage::browse_file_callback(mforms::TextEntry *entry, mforms::FileChooserType type,
                                      const std::string &extensions)
{
  FileChooser fsel(type);
  
  if (!extensions.empty())
    fsel.set_extensions(extensions, "");

  std::string path= entry->get_string_value();
  if (g_file_test(path.c_str(), G_FILE_TEST_IS_DIR))
    fsel.set_directory(path);
  else
  {
    // assume its a filename and get the dirname for the path
    gchar *dirname= g_path_get_dirname(path.c_str());
    fsel.set_directory(dirname);
    g_free(dirname);
  }
  
  if (fsel.run_modal())
  {
    entry->set_value(fsel.get_path());
    
    // keep all entries that had its contents set using the file dialog
    // so that when checking for filenames that would be overwritten, the ones from such
    // entries are skipped. paths picked with file dialog are already confirmed against overwrite
    _file_entries_set_from_browse_dialog.insert(entry);
  }
  validate();
}


bool WizardPage::check_and_confirm_file_overwrite(mforms::TextEntry *entry)
{
  //g_message("WizardPage::check_and_confirm_file_overwrite '%s'", entry->get_string_value().c_str());
  if (_file_entries_set_from_browse_dialog.find(entry) != _file_entries_set_from_browse_dialog.end())
    return true;
  
  if (g_file_test(entry->get_string_value().c_str(), G_FILE_TEST_EXISTS))
  {
    if (mforms::Utilities::show_warning(_("A file with the selected name already exists, do you want to replace it?"), 
                                        strfmt(_("The file '%s' already exists. Replacing it will overwrite its contents."),
                                               entry->get_string_value().c_str()), 
                                        _("Replace"), _("Cancel")) == mforms::ResultCancel)
      return false;
  }
  return true;
}



/** Validate the current page and update navigation buttons accordingly.
 *
 * This method will call the do_validate() method which must be overriden by subclasses.
 * The subclass may either call set_problem() to indicate a problem that prevents
 * the wizard to continue or provide that allows_next() returns false. clear_problem()
 * must be called if the problem raised by set_problem() is cleared.
 */
void WizardPage::validate()
{
  if (_form->get_active_page() == this)
  {
    do_validate();
  }
  _form->update_buttons();
}



/** Called before the previous page exits. (override)
 *
 * Pages may override this method when something must be executed before the previous
 * page leaves. This gives a chance to cancel leaving the previous page and denying entry to the
 * new page by returning false in case there was invalid input given by the user in a previous page.
 */
bool WizardPage::pre_load()
{
  return true; 
}


/** Called when the Next/Continue button is called. (override)
 *
 * Pages may override this if they want something to be executed when the user presses Next.
 * If true is returned the page will be switched to the next one, otherwise the current page
 * will remain.
 */
bool WizardPage::advance() 
{ 
  return true; 
}


/** Called the page is shown on screen.
 *
 * Pages may override this to perform some action when a page is entered and shown on screen.
 * The advancing argument will be true if the page was hit by pressing Next. If it's false, it
 * was entered by pressing Back.
 */
void WizardPage::enter(bool advancing)
{
  _file_entries_set_from_browse_dialog.clear();
  
  _signal_enter.emit(advancing);
}


/** Called the page is taken away from screen.
 *
 * Pages may override this to perform some action when a page is existed and taken away from screen.
 * The advancing argument will be true if the page was hit by pressing Next. If it's false, it
 * was entered by pressing Back.
 */
void WizardPage::leave(bool advancing)
{
  _signal_leave.emit(advancing);
}

