//-----------------------------------------------------------------------------
/*
 * Description: This file contains the classes for setup
 *
 * See the file README in the main directory for a description of
 * this software, copyright information, and how to reach the author.
 *
 * Author: alex
 * Date:   04.11.2004
 *
 * Last modfied:
 *   $Author: alex $
 *   $Date: 2006-03-11 18:05:26 +0100 (Sa, 11 Mär 2006) $
 *   $Revision: 27 $
 */
//-----------------------------------------------------------------------------

//=============================================================================
//     includes
//=============================================================================

//----- swap-workaround -------------------------------------------------------
#include "AxPluginGlobals.h"

//----- C++ -------------------------------------------------------------------
#include <algorithm>

//----- C ---------------------------------------------------------------------
//----- vdr -------------------------------------------------------------------
#include <vdr/plugin.h>
#include <vdr/interface.h>

//----- local -----------------------------------------------------------------
#include "AxMenu.h"
#include "AxPluginSwitchOSDPos.h"

//----- locals ----------------------------------------------------------------

//=============================================================================
//     static
//=============================================================================
static const char *PresetNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-+.,:;?!$&#@~{}[]()_/";

//=============================================================================
//     class AxMenuSetupCommon
//=============================================================================

//-------------------------------------------------------------------------
//     AxMenuSetupCommon::AxMenuSetupCommon()
//-------------------------------------------------------------------------
AxMenuSetupCommon::AxMenuSetupCommon(AxPluginSettings &theSettings)
                 : PARENT(tr("Common settings"), 33)
                 , myEdtSettings (theSettings)
                 , myTmpSettings (theSettings)
{
  Add(new cMenuEditBoolItem(tr("Activate first preset on startup"), &myTmpSettings.ActivateOnStart));
  Add(new cMenuEditBoolItem(tr("Hide main-menu entry"            ), &myTmpSettings.HideMainMenuEntry));
  Add(new cMenuEditBoolItem(tr("Main-menu action"                ), &myTmpSettings.MainMenuAction, tr("switch to next"), tr("select from menu")));
  Add(new cMenuEditBoolItem(tr("Show message when switching"     ), &myTmpSettings.ShowStatusMsg    ));

} // AxMenuSetupCommon::AxMenuSetupCommon()

//-------------------------------------------------------------------------
//     AxMenuSetupCommon::~AxMenuSetupCommon()
//-------------------------------------------------------------------------
AxMenuSetupCommon::~AxMenuSetupCommon()
{
} // AxMenuSetupCommon::~AxMenuSetupCommon()

//-------------------------------------------------------------------------
//     AxMenuSetupCommon::ProcessKey()
//-------------------------------------------------------------------------
eOSState AxMenuSetupCommon::ProcessKey(eKeys theKey)
{
  eOSState aState = cOsdMenu::ProcessKey(theKey);

  if (!HasSubMenu() && aState == osUnknown)
  {
    switch (theKey)
    {
      case kOk:
      {
        myEdtSettings = myTmpSettings;
        aState = osBack;
        break;
      }

      default:
      {
        break;
      }
    } // switch
  } // if

  return aState;
} // AxMenuSetupCommon::ProcessKey()

//=============================================================================
//     class AxMenuSetupOSDPreset
//=============================================================================

//-------------------------------------------------------------------------
//     AxMenuSetupOSDPreset::AxMenuSetupOSDPreset()
//-------------------------------------------------------------------------
AxMenuSetupOSDPreset::AxMenuSetupOSDPreset(AxMenuSetup *theParent, AxOSDPreset &thePreset)
                    : PARENT(tr("Edit Preset"), 20)
                    , myParent    (theParent)
                    , myEdtPreset (thePreset)
                    , myTmpPreset (thePreset)
{
  strncpy (myName, thePreset.Name.c_str(), sizeof(myName) - 1);
  myName[sizeof(myName) - 1] = '\0';

  Add(new cMenuEditStrItem(tr("Name")            ,  myName               , sizeof(myName), PresetNameChars));
  Add(new cMenuEditIntItem(tr("Setup.OSD$Left")  , &myTmpPreset.OSDLeft  , 0             , MAXOSDWIDTH ));
  Add(new cMenuEditIntItem(tr("Setup.OSD$Top")   , &myTmpPreset.OSDTop   , 0             , MAXOSDHEIGHT));
  Add(new cMenuEditIntItem(tr("Setup.OSD$Width") , &myTmpPreset.OSDWidth , MINOSDWIDTH   , MAXOSDWIDTH ));
  Add(new cMenuEditIntItem(tr("Setup.OSD$Height"), &myTmpPreset.OSDHeight, MINOSDHEIGHT  , MAXOSDHEIGHT));

  SetHelp(tr("Current"), NULL, NULL, NULL);

} // AxMenuSetupOSDPreset::AxMenuSetupOSDPreset()

//-------------------------------------------------------------------------
//     AxMenuSetupOSDPreset::~AxMenuSetupOSDPreset()
//-------------------------------------------------------------------------
AxMenuSetupOSDPreset::~AxMenuSetupOSDPreset()
{
} // AxMenuSetupOSDPreset::~AxMenuSetupOSDPreset()

//-------------------------------------------------------------------------
//     AxMenuSetupOSDPreset::ProcessKey()
//-------------------------------------------------------------------------
eOSState AxMenuSetupOSDPreset::ProcessKey(eKeys theKey)
{
  int anOldCurrent = Current();
  eOSState aState = cOsdMenu::ProcessKey(theKey);

  if (!HasSubMenu() && aState == osUnknown && theKey != kNone)
  {
    switch (theKey)
    {
      case kOk: //----- save settings -----
      {
        myTmpPreset.Name = std::string(myName);;
        myEdtPreset      = myTmpPreset;
        myParent->Store();
        aState = osBack;
        break;
      }
      case kRed: //----- use current settings -----
      {
        myTmpPreset.OSDLeft   = Setup.OSDLeft;
        myTmpPreset.OSDTop    = Setup.OSDTop;
        myTmpPreset.OSDWidth  = Setup.OSDWidth;
        myTmpPreset.OSDHeight = Setup.OSDHeight;

        // update display of all items
        for (int i = 0; i < Count(); ++i) Get(i)->Set();
        Display();

        aState = osContinue;
        break;
      }
      default:
      {
        break;
      }
    } // switch
  } // if

  // Hack/workaround: enforce the buttons are displayed
  if (!HasSubMenu() && theKey != kNone && anOldCurrent != Current())
  {
    SetHelp(tr("Current"), NULL, NULL, NULL);
  } // if

  return aState;
} // AxMenuSetupOSDPreset::ProcessKey()

//=============================================================================
//     class AxMenuSetup
//=============================================================================

//-------------------------------------------------------------------------
//     AxMenuSetup::AxMenuSetup()
//-------------------------------------------------------------------------
AxMenuSetup::AxMenuSetup(AxPluginSwitchOSDPos *thePlugin)
           : myPlugin  (thePlugin  )
{
  setup();
} // AxMenuSetup::AxMenuSetup()

//-------------------------------------------------------------------------
//     AxMenuSetup::~AxMenuSetup()
//-------------------------------------------------------------------------
AxMenuSetup::~AxMenuSetup()
{
} // AxMenuSetup::AxMenuSetup()

//-------------------------------------------------------------------------
//     AxMenuSetup::ProcessKey()
//-------------------------------------------------------------------------
eOSState AxMenuSetup::ProcessKey(eKeys theKey)
{
  int  anOldCurrent = Current();
  bool fHadSubMenu  = HasSubMenu();

  eOSState aState = cOsdMenu::ProcessKey(theKey);

  if (fHadSubMenu && !HasSubMenu() && Current() > 1)
  {
    // Submenu disappeared -> update current item
    Get(Current())->SetText(myPlugin->Setup().Presets[Current() - 2].Name.c_str());
    Display();
  } // if

  if (!HasSubMenu() && aState == osUnknown)
  {
    int anIndex = Current() - 2; // Offset is 2 because of Common-Settings and separator
    switch (theKey)
    {
      case kOk: //----- edit -----
      {
        if (Current() == 0)
        {
          aState = AddSubMenu(new AxMenuSetupCommon(myPlugin->Setup().Settings));
        }
        else if (0 <= anIndex && anIndex < int(myPlugin->Setup().Presets.size()))
        {
          aState = AddSubMenu(new AxMenuSetupOSDPreset(this, myPlugin->Setup().Presets[anIndex]));
        } // if
        break;
      } //
      case kRed: //----- duplicate -----
      {
        if (0 <= anIndex && anIndex < int(myPlugin->Setup().Presets.size()))
        {
          // add new preset as copy of current
          myPlugin->Setup().Presets.push_back(myPlugin->Setup().Presets[anIndex]);
          anIndex = myPlugin->Setup().Presets.size() - 1;

          // save the current preset-list
          Store();

          // create new item for the new (last) preset
          cOsdItem *anItem = new cOsdItem(myPlugin->Setup().Presets[anIndex].Name.c_str());
          Add(anItem);

          // select the new item
          SetCurrent(anItem);

          // open edit osd
          aState = AddSubMenu(new AxMenuSetupOSDPreset(this, myPlugin->Setup().Presets[anIndex]));
        } // if
        break;
      } //
      case kGreen: //----- new  -----
      {
        AxOSDPreset aNewPreset;
        aNewPreset.Name = tr("New");

        myPlugin->Setup().Presets.push_back(aNewPreset);
        anIndex = myPlugin->Setup().Presets.size() - 1;

        // save the current preset-list
        Store();

        // create new item for the new preset
        cOsdItem *anItem = new cOsdItem(myPlugin->Setup().Presets[anIndex].Name.c_str());
        Add(anItem);

        // select the new item
        SetCurrent(anItem);

        // open edit osd
        aState = AddSubMenu(new AxMenuSetupOSDPreset(this, myPlugin->Setup().Presets[anIndex]));
        break;
      } //
      case kYellow: //----- delete -----
      {
        if (0 <= anIndex && anIndex < int(myPlugin->Setup().Presets.size()))
        {
          char *aMsg = 0;
          asprintf(&aMsg, tr("Do you really want to delete preset %s?"), myPlugin->Setup().Presets[anIndex].Name.c_str());

          if (Interface->Confirm(aMsg))
          {
            Del(Current());

            std::rotate( myPlugin->Setup().Presets.begin() + anIndex     // begin
                       , myPlugin->Setup().Presets.begin() + anIndex + 1 // new begin
                       , myPlugin->Setup().Presets.end()                 // end
                       );
            myPlugin->Setup().Presets.resize(myPlugin->Setup().Presets.size() - 1);

            // save the current preset-list
            Store();

            Display();
          } // if

          free (aMsg);

          aState = osContinue;
        } // if
        break;
      } //
      case kBlue: //----- move -----
      {
        if (0 <= anIndex && anIndex < int(myPlugin->Setup().Presets.size()) && myPlugin->Setup().Presets.size() > 1)
        {
          Mark();
        } // if
        break;
      }
      default:
      {
        aState = osContinue;
        break;
      }
    } // switch
  } // if

  if (!HasSubMenu() && (anOldCurrent != Current() || fHadSubMenu))
  {
    setupColorButtons();
  } // if

  return aState;
} // AxMenuSetup::ProcessKey()

//-------------------------------------------------------------------------
//     AxMenuSetup::Store()
//-------------------------------------------------------------------------
void AxMenuSetup::Store(void)
{
  myPlugin->Setup().Settings.storeSetup(myPlugin);
  myPlugin->Setup().savePresets(cPlugin::ConfigDirectory(myPlugin->Name()));

//  Setup.Save();

} // AxMenuSetup::Store()

//-------------------------------------------------------------------------
//     AxMenuSetup::Move()
//-------------------------------------------------------------------------
void AxMenuSetup::Move(int From, int To)
{
  From -= 2; // Offset is 2 because of Common-Settings and separator
  To   -= 2; // Offset is 2 because of Common-Settings and separator

  // moved before separator? -> move at the beginning
  if (To < 0) To = 0;

  // nothing to do
  if (From == To)
  {
    return;
  } // if

  // invalid values (this should not happen)
  if (From < 0 || int(myPlugin->Setup().Presets.size()) <= From)
  {
    return;
  } // if

  // invalid values (this should not happen)
  if (To < 0 || int(myPlugin->Setup().Presets.size()) <= To)
  {
    return;
  } // if

  if (From < To)
  {
    std::rotate( myPlugin->Setup().Presets.begin() + From     // begin
               , myPlugin->Setup().Presets.begin() + From + 1 // new begin
               , myPlugin->Setup().Presets.begin() + To   + 1 // end
               );
  }
  else
  {
    std::rotate( myPlugin->Setup().Presets.begin() + To       // begin
               , myPlugin->Setup().Presets.begin() + From     // new begin
               , myPlugin->Setup().Presets.begin() + From + 1 // end
               );
  } // if

  // save the current preset-list
  Store();

  setup(To + 2);
  Display();

} // AxMenuSetup::Move()

//-------------------------------------------------------------------------
//     AxMenuSetup::setup()
//-------------------------------------------------------------------------
void AxMenuSetup::setup(int theCurrent)
{
  Clear();

  Add(new cOsdItem(tr("Common settings")));

  char *szMsg = 0;
  asprintf( &szMsg
          , "--- %s -----------------------------------------------------------------"
          , tr("Presets")
          );

  cOsdItem *anItem = new cOsdItem(szMsg);
  free(szMsg);

  anItem->SetSelectable(false);
  Add(anItem);

  for (AxOSDPresetCltn::const_iterator anIter  = myPlugin->Setup().Presets.begin();
                                       anIter != myPlugin->Setup().Presets.end();
                                     ++anIter)
  {
    Add(new cOsdItem((*anIter).Name.c_str()));
  } // for

  if (theCurrent >= 0 && Get(theCurrent)) SetCurrent(Get(theCurrent));

  setupColorButtons();
} // AxMenuSetup::setup()

//-------------------------------------------------------------------------
//     AxMenuSetup::setupColorButtons()
//-------------------------------------------------------------------------
void AxMenuSetup::setupColorButtons()
{
    if      (Current() == 0) /* Common Settings */  SetHelp(0              , tr("New"), 0,            0              );
    else if (Current() == 1) /* separator       */  SetHelp(0              , tr("New"), 0,            0              );
    else if (Current()  > 1)
      if (myPlugin->Setup().Presets.size() > 1)
                             /* preset          */  SetHelp(tr("Duplicate"), tr("New"), tr("Delete"), tr("Mark")     );
      else
                             /* preset          */  SetHelp(tr("Duplicate"), tr("New"), tr("Delete"), 0              );
} // AxMenuSetup::setupColorButtons()

//=============================================================================
//     class AxMenuSelect
//=============================================================================

//-------------------------------------------------------------------------
//     AxMenuSelect::AxMenuSelect()
//-------------------------------------------------------------------------
AxMenuSelect::AxMenuSelect(AxPluginSwitchOSDPos *thePlugin)
            : PARENT(tr("switch OSD-positions/sizes"))
            , myPlugin  (thePlugin  )
{
  SetHasHotkeys();
  int i = 0;

  int aCurrentPreset = thePlugin->Setup().guessActive();

  for (AxOSDPresetCltn::const_iterator anIter  = myPlugin->Setup().Presets.begin();
                                       anIter != myPlugin->Setup().Presets.end();
                                     ++anIter, ++i)
  {
    cOsdItem *anItem = new cOsdItem(hk((*anIter).Name.c_str()));
    Add(anItem);

    if (i == aCurrentPreset)
    {
      SetCurrent(anItem);
    } // if
  } // for
} // AxMenuSelect::AxMenuSelect()

//-------------------------------------------------------------------------
//     AxMenuSelect::~AxMenuSelect()
//-------------------------------------------------------------------------
AxMenuSelect::~AxMenuSelect()
{
} // AxMenuSelect::AxMenuSelect()

//-------------------------------------------------------------------------
//     AxMenuSelect::ProcessKey()
//-------------------------------------------------------------------------
eOSState AxMenuSelect::ProcessKey(eKeys theKey)
{
  eOSState aState = cOsdMenu::ProcessKey(theKey);

  switch (theKey)
  {
    case kOk:
    {
      myPlugin->Setup().activatePreset(Current(), myPlugin->Setup().Settings.ShowStatusMsg);
      aState = osEnd;
      break;
    }

    default:
    {
      break;
    }
  } // switch

  return aState;
} // AxMenuSelect::ProcessKey()
