//-----------------------------------------------------------------------------
/*
 * Description: This file contains the class AxPluginSetup
 *
 * 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>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>

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

//----- local -----------------------------------------------------------------
#include "AxPluginSetup.h"

//=============================================================================
//     consts
//=============================================================================
const std::string PresetsFileName = std::string("OSDPresets.conf");

//=============================================================================
//     static
//=============================================================================

//---------------------------------------------------------------------
//     stripWhiteSpaces()
//---------------------------------------------------------------------
static std::string stripWhiteSpaces(const std::string &theSrc)
{
  std::string aResult;
  std::string::size_type aPosFirst = theSrc.find_first_not_of(" \t");
  std::string::size_type aPosLast  = theSrc.find_last_not_of (" \t");

  if ( aPosFirst != std::string::npos
    && aPosLast  != std::string::npos
     )
  {
    aResult = theSrc.substr(aPosFirst, aPosLast - aPosFirst + 1);
  }
  else if (aPosLast != std::string::npos)
  {
    aResult = theSrc.substr(0, aPosLast + 1);
  }
  else if (aPosFirst != std::string::npos)
  {
    aResult = theSrc.substr(aPosFirst);
  }
  else
  {
    // both failed -> the string doesn't contain any readable characters
    aResult = "";
  } // if

  return aResult;
} // stripWhiteSpaces()


//=============================================================================
//     class AxPluginSettings
//=============================================================================

//-------------------------------------------------------------------------
//     AxPluginSettings::AxPluginSettings()
//-------------------------------------------------------------------------
AxPluginSettings::AxPluginSettings()
                : HideMainMenuEntry(false)
                , MainMenuAction   (false)
                , ShowStatusMsg    (true )
{
} // AxPluginSettings::AxPluginSettings()

//-------------------------------------------------------------------------
//     AxPluginSettings::parseSetup()
//-------------------------------------------------------------------------
bool AxPluginSettings::parseSetup(const char *theName, const char *theValue)
{
  bool fConsumed = true;

  if      (!strcasecmp(theName, "ActivateOnStart"  )) ActivateOnStart   = atoi(theValue);
  else if (!strcasecmp(theName, "HideMainMenuEntry")) HideMainMenuEntry = atoi(theValue);
  else if (!strcasecmp(theName, "MainMenuAction"   )) MainMenuAction    = atoi(theValue);
  else if (!strcasecmp(theName, "ShowStatusMsg"    )) ShowStatusMsg     = atoi(theValue);

  else fConsumed = false;

  return fConsumed;
} // AxPluginSettings::parseSetup()

//-------------------------------------------------------------------------
//     AxPluginSettings::storeSetup()
//-------------------------------------------------------------------------
void AxPluginSettings::storeSetup(cPlugin *thePlugin)
{
  thePlugin->SetupStore("ActivateOnStart"  , ActivateOnStart  );
  thePlugin->SetupStore("HideMainMenuEntry", HideMainMenuEntry);
  thePlugin->SetupStore("MainMenuAction"   , MainMenuAction   );
  thePlugin->SetupStore("ShowStatusMsg"    , ShowStatusMsg    );
} // AxPluginSettings::storeSetup()

//=============================================================================
//     class AxOSDPreset
//=============================================================================

//-------------------------------------------------------------------------
//     AxOSDPreset::AxOSDPreset()
//-------------------------------------------------------------------------
AxOSDPreset::AxOSDPreset()
           : Name     ("")
           , OSDLeft  (Setup.OSDLeft  )
           , OSDTop   (Setup.OSDTop   )
           , OSDWidth (Setup.OSDWidth )
           , OSDHeight(Setup.OSDHeight)
{
} // AxOSDPreset::AxOSDPreset()

//-------------------------------------------------------------------------
//     AxOSDPreset::activate()
//-------------------------------------------------------------------------
void AxOSDPreset::activate()
{
  if (isValid())
  {
    dsyslog("setting OSD-layout to preset %s", getInfoString().c_str());

    Setup.OSDLeft   = OSDLeft  ;
    Setup.OSDTop    = OSDTop   ;
    Setup.OSDWidth  = OSDWidth ;
    Setup.OSDHeight = OSDHeight;
  }
  else
  {
    esyslog("preset %s is not valid -> not switched", getInfoString().c_str());
  } // if
} // AxOSDPreset::activate()

//-------------------------------------------------------------------------
//     AxOSDPreset::invalidate()
//-------------------------------------------------------------------------
void AxOSDPreset::invalidate()
{
  Name      = "";
  OSDLeft   = -1;
  OSDTop    = -1;
  OSDWidth  = -1;
  OSDHeight = -1;
} // AxOSDPreset::invalidate()

//-------------------------------------------------------------------------
//     AxOSDPreset::useCurrent()
//-------------------------------------------------------------------------
void AxOSDPreset::useCurrent()
{
  OSDLeft   = Setup.OSDLeft  ;
  OSDTop    = Setup.OSDTop   ;
  OSDWidth  = Setup.OSDWidth ;
  OSDHeight = Setup.OSDHeight;
} // AxOSDPreset::AxOSDPreset()

//-------------------------------------------------------------------------
//     AxOSDPreset::isEmpty()
//-------------------------------------------------------------------------
bool AxOSDPreset::isEmpty() const
{
  return Name      == ""
      && OSDLeft   == -1
      && OSDTop    == -1
      && OSDWidth  == -1
      && OSDHeight == -1
       ;
} // AxOSDPreset::isEmpty()

//-------------------------------------------------------------------------
//     AxOSDPreset::isValid()
//-------------------------------------------------------------------------
bool AxOSDPreset::isValid() const
{
  bool fIsValid = Name.length() > 0
                && 0            <= OSDLeft   && OSDLeft   <= MAXOSDWIDTH
                && 0            <= OSDTop    && OSDTop    <= MAXOSDHEIGHT
                && MINOSDWIDTH  <= OSDWidth  && OSDWidth  <= MAXOSDWIDTH
                && MINOSDHEIGHT <= OSDHeight && OSDHeight <= MAXOSDHEIGHT
                ;
  if (!fIsValid)
  {
    dsyslog("Preset '%s' is not valid", Name.c_str());
    dsyslog("  OSDLeft  : %3d <= %3d <= %3d", 0           , OSDLeft  , MAXOSDWIDTH );
    dsyslog("  OSDTop   : %3d <= %3d <= %3d", 0           , OSDTop   , MAXOSDHEIGHT);
    dsyslog("  OSDWidth : %3d <= %3d <= %3d", MINOSDWIDTH , OSDWidth , MAXOSDWIDTH );
    dsyslog("  OSDHeigth: %3d <= %3d <= %3d", MINOSDHEIGHT, OSDHeight, MAXOSDHEIGHT);
  } // if

  return fIsValid;
} // AxOSDPreset::isValid()

//-------------------------------------------------------------------------
//     AxOSDPreset::isActive()
//-------------------------------------------------------------------------
bool AxOSDPreset::isActive() const
{
  return  OSDLeft   == Setup.OSDLeft
       && OSDTop    == Setup.OSDTop
       && OSDWidth  == Setup.OSDWidth
       && OSDHeight == Setup.OSDHeight
        ;
} // AxOSDPreset::isActive()

//-------------------------------------------------------------------------
//     AxOSDPreset::dump()
//-------------------------------------------------------------------------
void AxOSDPreset::dump() const
{
  dsyslog("Preset: %s", getInfoString().c_str());
} // AxOSDPreset::dump()

//-------------------------------------------------------------------------
//     AxOSDPreset::getInfoString()
//-------------------------------------------------------------------------
std::string AxOSDPreset::getInfoString() const
{
  std::ostringstream s;

  s << "Name: '" << Name << "'" << std::string(std::max(0, 20 - int(Name.length())), ' ')
    << "  L: "   << std::setw(3) << OSDLeft
    << "  T: "   << std::setw(3) << OSDTop
    << "  W: "   << std::setw(3) << OSDWidth
    << "  H: "   << std::setw(3) << OSDHeight
    ;

  return s.str();
} // AxOSDPreset::getInfoString()

//-------------------------------------------------------------------------
//     readKeyVal()
//-------------------------------------------------------------------------
static bool readKeyVal(const::std::string theLine, std::string &theKey, std::string &theVal)
{
  std::string::size_type aPosSep = theLine.find_first_of('=');

  if ( aPosSep > 0                     // - not at the first pos
    && aPosSep < theLine.length() - 1  // - not at the last  pos
     )
  {
    theKey = stripWhiteSpaces(theLine.substr(0, aPosSep));
    theVal = stripWhiteSpaces(theLine.substr(aPosSep + 1));
  }
  else
  {
    theKey = "";
    theVal = "";
  } // if

  return theKey.length() > 0 && theVal.length() > 0;
} // readKeyVal()

//=============================================================================
//     class AxPluginSetup
//=============================================================================

//-------------------------------------------------------------------------
//     AxPluginSetup::AxPluginSetup()
//-------------------------------------------------------------------------
AxPluginSetup::AxPluginSetup()
             : myCurrentPreset(-1)
{
} // AxPluginSetup::AxPluginSeSortOrdertup()

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

//-------------------------------------------------------------------------
//     convVal()
//-------------------------------------------------------------------------
template <typename T>
static void convVal(const std::string &theStr, T &theVal)
{
  T aVal;
  std::istringstream s(theStr);

  if (s >> aVal)
  {
    theVal = aVal;
  } // if
} // convVal()

//-------------------------------------------------------------------------
//     AxPluginSetup::loadPresets()
//-------------------------------------------------------------------------
bool AxPluginSetup::loadPresets(const std::string &theConfigFolder)
{
  bool        fOK       = false;
  std::string aFileName = std::string(theConfigFolder)
                        + std::string("/")
                        + PresetsFileName
                        ;
  try
  {
    std::ifstream aFile(aFileName.c_str());
    if (!aFile)
    {
      throw std::string("unable to open file for reading");
    } // if

    AxOSDPresetCltn aPresetCltn;
    AxOSDPreset     aPreset;
    AxOSDPreset     aPresetEmpty;
    aPreset.invalidate();

    int aLineNum = 0;
    std::string aLine;

    while (std::getline(aFile, aLine).good())
    {
      ++aLineNum;
      if (aLine.length() == 0 || aLine.find_first_of('#') == 1)
      {
        continue; // skip empty lines or lines beginning with #
      } // if

      std::string aKey, aVal;
      bool fConsumed = true;
      if (aLine.find("[OSDPreset]") == 0)
      {
        if (!aPreset.isEmpty())
        {
          if (aPreset.isValid())
          {
            aPresetCltn.push_back(aPreset);
//            aPreset.dump();
          } // if
          aPreset.invalidate();
        } // if
      }
      else if (readKeyVal(aLine, aKey, aVal))
      {
        if      (aKey == "Name"  )               aPreset.Name = aVal;
        else if (aKey == "Left"  ) convVal(aVal, aPreset.OSDLeft   );
        else if (aKey == "Top"   ) convVal(aVal, aPreset.OSDTop    );
        else if (aKey == "Width" ) convVal(aVal, aPreset.OSDWidth  );
        else if (aKey == "Height") convVal(aVal, aPreset.OSDHeight );
        else                       fConsumed         = false;
      }
      else
      {
        fConsumed = false;
      } // if

      if (!fConsumed)
      {
        isyslog("unknown line %d '%s' in %s", aLineNum, aLine.c_str(), PresetsFileName.c_str());
      } // if
    } // while

    if (!aPreset.isEmpty() && aPreset.isValid())
    {
      aPresetCltn.push_back(aPreset);
    } // if

    Presets = aPresetCltn;
    fOK = true;
  }
  catch (const std::string &aMsg)
  {
    esyslog("error while reading %s: %s", aFileName.c_str(), aMsg.c_str());
  }
  catch (...)
  {
    esyslog("error while reading %s", aFileName.c_str());
  } // catch

  if (fOK)
  {
    isyslog("%d osd-presets present", Presets.size());
  } // if

  return fOK;
} // AxPluginSetup::loadPresets()

//-------------------------------------------------------------------------
//     AxPluginSetup::savePresets()
//-------------------------------------------------------------------------
bool AxPluginSetup::savePresets(const std::string &theConfigFolder)
{
  bool fOK = false;
  std::string aFileName = std::string(theConfigFolder)
                        + std::string("/")
                        + PresetsFileName
                        ;
  try
  {
    std::ofstream aFile(aFileName.c_str());
    if (!aFile)
    {
      throw std::string("unable to open file for writing");
    } // if

    for (AxOSDPresetCltn::const_iterator anIter  = Presets.begin();
                                         anIter != Presets.end();
                                       ++anIter)
    {
      const AxOSDPreset &aPreset = *anIter;

      aFile << "[OSDPreset]"                      << std::endl;
      aFile << "  Name   = " << aPreset.Name      << std::endl;
      aFile << "  Left   = " << aPreset.OSDLeft   << std::endl;
      aFile << "  Top    = " << aPreset.OSDTop    << std::endl;
      aFile << "  Width  = " << aPreset.OSDWidth  << std::endl;
      aFile << "  Height = " << aPreset.OSDHeight << std::endl;
      aFile                                       << std::endl;

//      aPreset.dump();
    } // for

    fOK = true;
  }
  catch (const std::string &aMsg)
  {
    esyslog("error while writing %s: %s", aFileName.c_str(), aMsg.c_str());
  }
  catch (...)
  {
    esyslog("error while writing %s", aFileName.c_str());
  } // catch

  if (fOK)
  {
    isyslog("%d osd-presets saved", Presets.size());
  } // if

  return fOK;
} // AxPluginSetup::savePresets()

//-------------------------------------------------------------------------
//     AxPluginSetup::guessActive()
//-------------------------------------------------------------------------
int AxPluginSetup::guessActive()
{
  int anIndex = -1;

  if ( myCurrentPreset >= 0
   && myCurrentPreset < int(Presets.size())
   && Presets[myCurrentPreset].isActive()
    )
  {
    anIndex = myCurrentPreset;
  } // if

  if (anIndex < 0)
  {
    for (AxOSDPresetCltn::size_type i = 0; i < Presets.size(); ++i)
    {
      if (Presets[i].isActive())
      {
        myCurrentPreset = i;
        anIndex = myCurrentPreset;
        break;
      } // if
    } // for
  } // if

  return anIndex;
} // AxPluginSetup::guessActive()

//-------------------------------------------------------------------------
//     AxPluginSetup::getNext()
//-------------------------------------------------------------------------
int AxPluginSetup::getNext() const
{
  int anIndex = -1;

  if (Presets.size() > 0)
  {
    if (myCurrentPreset < 0)
    {
      anIndex = 0;
    }
    else
    {
      anIndex = (myCurrentPreset + 1) % Presets.size();
    } // if
  } // if

  return anIndex;
} // AxPluginSetup::getNext()

//-------------------------------------------------------------------------
//     AxPluginSetup::activateNext()
//-------------------------------------------------------------------------
void AxPluginSetup::activateNext(bool fShowMsg, bool fShowWarnMsg)
{
  int anIndex = getNext();
  if (anIndex >= 0)
  {
    activatePreset(anIndex, fShowMsg, fShowWarnMsg);
  } // if
} // AxPluginSetup::activateNext()

//-------------------------------------------------------------------------
//     AxPluginSetup::activatePreset()
//-------------------------------------------------------------------------
int AxPluginSetup::activatePreset(int theIndex, bool fShowMsg, bool fShowWarnMsg)
{
  char *szMsg = 0;

  if (theIndex >= 0 && theIndex < int(Presets.size()))
  {
    if (Presets[theIndex].isValid())
    {
      Presets[theIndex].activate();
      myCurrentPreset = theIndex;

      if (fShowMsg)
      {
        asprintf(&szMsg, tr("OSD-preset %s activated"), Presets[theIndex].Name.c_str());
        Skins.QueueMessage(mtInfo, szMsg, 1);
      } // if
    }
    else
    {
      if (Presets[theIndex].Name.length() > 0)
      {
        asprintf(&szMsg, tr("OSD-preset %s is not valid"), Presets[theIndex].Name.c_str());
      }
      else
      {
        asprintf( &szMsg, tr("OSD-preset %d is not valid"), theIndex + 1);
      } // if

      Skins.QueueMessage(mtWarning, szMsg, 1);
    } // if
  }
  else
  {
    asprintf( &szMsg, tr("OSD-preset %d not present"), theIndex + 1);
    Skins.QueueMessage(mtWarning, szMsg, 1);
  } // if

  if (szMsg)
  {
    free(szMsg);
  } // if

  return myCurrentPreset;
} // AxPluginSetup::activatePreset()

//-------------------------------------------------------------------------
//     AxPluginSetup::findPreset()
//-------------------------------------------------------------------------
int AxPluginSetup::findPreset(const std::string &theName) const
{
  int anIndex = -1;

  for (AxOSDPresetCltn::size_type i = 0; i < Presets.size(); ++i)
  {
    if (Presets[i].Name == theName)
    {
      anIndex = i;
      break;
    } // if
  } // for

  return anIndex;
} // AxPluginSetup::findPreset()
