/*
 * menu.c: Menus for the Atmolight-plugin
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include <vdr/remote.h>
#include "defs.h"
#include "menu.h"
#include "setup.h"

extern cPlugin *AtmoPlugin;

// --- cMenuSetupAtmo --------------------------------------------------------

#define STARTMODE           "Startmode"
#define WIDESCREENMODE      "WidescreenMode"

cMenuSetupAtmo::cMenuSetupAtmo(void)
{                                               
  useStartModeTexts[0] = tr("always on");
  useStartModeTexts[1] = tr("always off");
  useStartModeTexts[2] = tr("timer dependent"); 

  Set();
}

void cMenuSetupAtmo::Set(void)
{
  int current = Current();
  Clear();

  Add(new cMenuEditStraItem(tr("Startmode"), &AtmoSetup.StartMode, 3, useStartModeTexts));
  Add(new cMenuEditBoolItem(tr("Widescreen mode"), &AtmoSetup.WidescreenMode));
  Add(new cOsdItem(tr("Mode settings..."), osUser3));
  Add(new cOsdItem(tr("Filter settings..."), osUser4));
  Add(new cOsdItem(tr("Gamma settings..."), osUser5));
  Add(new cOsdItem(tr("Color calibration..."), osUser6));

  SetCurrent(Get(current));
  Display();
}

bool cMenuSetupAtmo::SetupParse(const char *Name, const char *Value)
{
       if (!strcasecmp(Name, STARTMODE))      AtmoSetup.StartMode      = atoi(Value);
  else if (!strcasecmp(Name, WIDESCREENMODE)) AtmoSetup.WidescreenMode = atoi(Value);
  else return false;

  return true;
}

void cMenuSetupAtmo::Store(void)
{
  SetupStore(STARTMODE,      AtmoSetup.StartMode);
  SetupStore(WIDESCREENMODE, AtmoSetup.WidescreenMode);
}

eOSState cMenuSetupAtmo::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);

  switch (state)
  {
    case osUser3:
      return(AddSubMenu(new cMenuSetupMode));
    break;

    case osUser4:
      return(AddSubMenu(new cMenuSetupFilter));
    break;

    case osUser5:
      return(AddSubMenu(new cMenuSetupGamma));
    break;

    case osUser6:
         AtmoSetup.DoWhiteCalibration = true;
         cRemote::CallPlugin("atmo");
         return osBack;
    break;

    case osUnknown:
         if ((Key == kOk) || (Key == kBack))
         {
           Store();
           state = osBack;
         }
    break;

    default:
    break;
  }
  return state;
}

// --- cMenuSetupMode --------------------------------------------------------

#define MODE                 "Mode"
#define EDGEWEIGHTING        "EdgeWeighting"
#define BRIGHTNESSCORRECTION "BrightnessCorrection"
#define DARKNESSLIMIT        "DarknessLimit"
#define HUEWINSIZE           "HueWindowSize"
#define SATWINSIZE           "SatWindowSize"
#define DEFAULTCOLOR         "DefaultColor"
#define STATICCOLOR_CENTER_R "StaticColor_Center_R"
#define STATICCOLOR_CENTER_G "StaticColor_Center_G"
#define STATICCOLOR_CENTER_B "StaticColor_Center_B"
#define STATICCOLOR_LEFT_R   "StaticColor_Left_R"
#define STATICCOLOR_LEFT_G   "StaticColor_Left_G"
#define STATICCOLOR_LEFT_B   "StaticColor_Left_B"
#define STATICCOLOR_RIGHT_R  "StaticColor_Right_R"
#define STATICCOLOR_RIGHT_G  "StaticColor_Right_G"
#define STATICCOLOR_RIGHT_B  "StaticColor_Right_B"
#define STATICCOLOR_TOP_R    "StaticColor_Top_R"
#define STATICCOLOR_TOP_G    "StaticColor_Top_G"
#define STATICCOLOR_TOP_B    "StaticColor_Top_B"
#define STATICCOLOR_BOTTOM_R "StaticColor_Bottom_R"
#define STATICCOLOR_BOTTOM_G "StaticColor_Bottom_G"
#define STATICCOLOR_BOTTOM_B "StaticColor_Bottom_B"

cMenuSetupMode::cMenuSetupMode(void) : cOsdMenu(tr("Mode settings"), 25)
{
  useModeTexts[live_picture_hsv] = tr("live picture (HSV)"); // generate colors out of live picture
  useModeTexts[default_color]    = tr("default color");      // select default color
  useModeTexts[static_color]     = tr("static colors");      // generate static color

  useDefaultColorsTexts[black]   = tr("black");
  useDefaultColorsTexts[white]   = tr("white");
  useDefaultColorsTexts[red]     = tr("red");
  useDefaultColorsTexts[green]   = tr("green");
  useDefaultColorsTexts[blue]    = tr("blue");
  useDefaultColorsTexts[yellow]  = tr("yellow");
  useDefaultColorsTexts[cyan]    = tr("cyan");
  useDefaultColorsTexts[magenta] = tr("magenta");

  Set();
}

void cMenuSetupMode::Set(void)
{
  int current = Current();
  Clear();

  Add(new cMenuEditStraItem(tr("Mode"), (int*)&AtmoSetup.Mode, 3, useModeTexts));
  if (AtmoSetup.Mode == live_picture_hsv)
  {
    Add(new cMenuEditIntItem(tr("  Edge weighting"), &AtmoSetup.EdgeWeighting, 1, 30));
    Add(new cMenuEditIntItem(tr("  Brightness [%]"), &AtmoSetup.BrightCorrect, 50, 300));
    Add(new cMenuEditIntItem(tr("  Darkness limit"), &AtmoSetup.DarknessLimit, 0, 10));
    Add(new cMenuEditIntItem(tr("  Hue windowing"),  &AtmoSetup.HueWinSize, 0, 5));
    Add(new cMenuEditIntItem(tr("  Sat windowing"),  &AtmoSetup.SatWinSize, 0, 5));
  }
  else if (AtmoSetup.Mode == default_color)
  {
    Add(new cMenuEditStraItem(tr("  Default color"), (int*)&AtmoSetup.DefaultColor, 8, useDefaultColorsTexts));
  }
  else if (AtmoSetup.Mode == static_color)
  {
    Add(new cMenuEditUCharItem(tr("  Red center"),   &AtmoSetup.StaticColor.channel[center].r, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Green center"), &AtmoSetup.StaticColor.channel[center].g, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Blue center"),  &AtmoSetup.StaticColor.channel[center].b, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Red left"),     &AtmoSetup.StaticColor.channel[left].r, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Green left"),   &AtmoSetup.StaticColor.channel[left].g, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Blue left"),    &AtmoSetup.StaticColor.channel[left].b, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Red right"),    &AtmoSetup.StaticColor.channel[right].r, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Green right"),  &AtmoSetup.StaticColor.channel[right].g, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Blue right"),   &AtmoSetup.StaticColor.channel[right].b, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Red top"),      &AtmoSetup.StaticColor.channel[top].r, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Green top"),    &AtmoSetup.StaticColor.channel[top].g, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Blue top"),     &AtmoSetup.StaticColor.channel[top].b, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Red bottom"),   &AtmoSetup.StaticColor.channel[bottom].r, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Green bottom"), &AtmoSetup.StaticColor.channel[bottom].g, 0, 255));
    Add(new cMenuEditUCharItem(tr("  Blue bottom"),  &AtmoSetup.StaticColor.channel[bottom].b, 0, 255));
  }

  SetCurrent(Get(current));
  Display();
}

bool cMenuSetupMode::SetupParse(const char *Name, const char *Value)
{
       if (!strcasecmp(Name, MODE))                 AtmoSetup.Mode          = (tMode)atoi(Value);
  else if (!strcasecmp(Name, EDGEWEIGHTING))        AtmoSetup.EdgeWeighting = atoi(Value);
  else if (!strcasecmp(Name, DARKNESSLIMIT))        AtmoSetup.DarknessLimit = atoi(Value);
  else if (!strcasecmp(Name, BRIGHTNESSCORRECTION)) AtmoSetup.BrightCorrect = atoi(Value);
  else if (!strcasecmp(Name, HUEWINSIZE))           AtmoSetup.HueWinSize    = atoi(Value);
  else if (!strcasecmp(Name, SATWINSIZE))           AtmoSetup.SatWinSize    = atoi(Value);
  else if (!strcasecmp(Name, DEFAULTCOLOR))         AtmoSetup.DefaultColor  = (tDefaultColors)atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_CENTER_R)) AtmoSetup.StaticColor.channel[center].r = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_CENTER_G)) AtmoSetup.StaticColor.channel[center].g = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_CENTER_B)) AtmoSetup.StaticColor.channel[center].b = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_LEFT_R))   AtmoSetup.StaticColor.channel[left].r   = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_LEFT_G))   AtmoSetup.StaticColor.channel[left].g   = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_LEFT_B))   AtmoSetup.StaticColor.channel[left].b   = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_RIGHT_R))  AtmoSetup.StaticColor.channel[right].r  = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_RIGHT_G))  AtmoSetup.StaticColor.channel[right].g  = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_RIGHT_B))  AtmoSetup.StaticColor.channel[right].b  = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_TOP_R))    AtmoSetup.StaticColor.channel[top].r    = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_TOP_G))    AtmoSetup.StaticColor.channel[top].g    = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_TOP_B))    AtmoSetup.StaticColor.channel[top].b    = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_BOTTOM_R)) AtmoSetup.StaticColor.channel[bottom].r = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_BOTTOM_G)) AtmoSetup.StaticColor.channel[bottom].g = atoi(Value);
  else if (!strcasecmp(Name, STATICCOLOR_BOTTOM_B)) AtmoSetup.StaticColor.channel[bottom].b = atoi(Value);
  else return false;

  return true;
}

void cMenuSetupMode::Store(void)
{
  AtmoPlugin->SetupStore(MODE,                 AtmoSetup.Mode);
  AtmoPlugin->SetupStore(EDGEWEIGHTING,        AtmoSetup.EdgeWeighting);
  AtmoPlugin->SetupStore(BRIGHTNESSCORRECTION, AtmoSetup.BrightCorrect);
  AtmoPlugin->SetupStore(DARKNESSLIMIT,        AtmoSetup.DarknessLimit);
  AtmoPlugin->SetupStore(HUEWINSIZE,           AtmoSetup.HueWinSize);
  AtmoPlugin->SetupStore(SATWINSIZE,           AtmoSetup.SatWinSize);
  AtmoPlugin->SetupStore(DEFAULTCOLOR,         AtmoSetup.DefaultColor);
  AtmoPlugin->SetupStore(STATICCOLOR_CENTER_R, AtmoSetup.StaticColor.channel[center].r);
  AtmoPlugin->SetupStore(STATICCOLOR_CENTER_G, AtmoSetup.StaticColor.channel[center].g);
  AtmoPlugin->SetupStore(STATICCOLOR_CENTER_B, AtmoSetup.StaticColor.channel[center].b);
  AtmoPlugin->SetupStore(STATICCOLOR_LEFT_R,   AtmoSetup.StaticColor.channel[left].r);
  AtmoPlugin->SetupStore(STATICCOLOR_LEFT_G,   AtmoSetup.StaticColor.channel[left].g);
  AtmoPlugin->SetupStore(STATICCOLOR_LEFT_B,   AtmoSetup.StaticColor.channel[left].b);
  AtmoPlugin->SetupStore(STATICCOLOR_RIGHT_R,  AtmoSetup.StaticColor.channel[right].r);
  AtmoPlugin->SetupStore(STATICCOLOR_RIGHT_G,  AtmoSetup.StaticColor.channel[right].g);
  AtmoPlugin->SetupStore(STATICCOLOR_RIGHT_B,  AtmoSetup.StaticColor.channel[right].b);
  AtmoPlugin->SetupStore(STATICCOLOR_TOP_R,    AtmoSetup.StaticColor.channel[top].r);
  AtmoPlugin->SetupStore(STATICCOLOR_TOP_G,    AtmoSetup.StaticColor.channel[top].g);
  AtmoPlugin->SetupStore(STATICCOLOR_TOP_B,    AtmoSetup.StaticColor.channel[top].b);
  AtmoPlugin->SetupStore(STATICCOLOR_BOTTOM_R, AtmoSetup.StaticColor.channel[bottom].r);
  AtmoPlugin->SetupStore(STATICCOLOR_BOTTOM_G, AtmoSetup.StaticColor.channel[bottom].g);
  AtmoPlugin->SetupStore(STATICCOLOR_BOTTOM_B, AtmoSetup.StaticColor.channel[bottom].b);
}

eOSState cMenuSetupMode::ProcessKey(eKeys Key)
{
  int prev_Mode = AtmoSetup.Mode;
  eOSState state = cOsdMenu::ProcessKey(Key);

  if(prev_Mode != AtmoSetup.Mode)
  {
    Set();
    state = osContinue;
  }

  if (state == osUnknown)
  {
    if ((Key == kOk) || (Key == kBack))
    {
      Store();
      state = osBack;
    }
  }
  return state;
}

// --- cMenuSetupFilter ------------------------------------------------------

#define FILTER_MODE          "Filter_Mode"
#define FILTER_MEANLENGTH    "Filter_MeanLength"
#define FILTER_MEANTHRESHOLD "Filter_MeanThreshold"
#define FILTER_PERCENTNEW    "Filter_PercentNew"

cMenuSetupFilter::cMenuSetupFilter(void) : cOsdMenu(tr("Filter settings"), 25)
{
  useFilterTexts[0] = tr("off");        
  useFilterTexts[1] = tr("combined");   
  useFilterTexts[2] = tr("percentage"); 

  Set();
}

void cMenuSetupFilter::Set(void)
{
  int current = Current();
  Clear();

  Add(new cMenuEditStraItem(tr("Filter"), (int*)&AtmoSetup.Filter_Mode, 3, useFilterTexts));
  if (AtmoSetup.Filter_Mode == combined)
  {
    Add(new cMenuEditIntItem(tr("  Filter length [ms]"),    &AtmoSetup.Filter_MeanLength, 300, 5000));
    Add(new cMenuEditIntItem(tr("  Filter threshold [%]"),  &AtmoSetup.Filter_MeanThreshold, 1, 100));
    Add(new cMenuEditIntItem(tr("  Filter smoothness [%]"), &AtmoSetup.Filter_PercentNew, 1, 100));
  }
  else if (AtmoSetup.Filter_Mode == percent)
  {
    Add(new cMenuEditIntItem(tr("  Filter smoothness [%]"), &AtmoSetup.Filter_PercentNew, 1, 100));
  }

  SetCurrent(Get(current));
  Display();
}

bool cMenuSetupFilter::SetupParse(const char *Name, const char *Value)
{
       if (!strcasecmp(Name, FILTER_MODE))          AtmoSetup.Filter_Mode          = (tFilter)atoi(Value);
  else if (!strcasecmp(Name, FILTER_MEANLENGTH))    AtmoSetup.Filter_MeanLength    = atoi(Value);
  else if (!strcasecmp(Name, FILTER_MEANTHRESHOLD)) AtmoSetup.Filter_MeanThreshold = atoi(Value);
  else if (!strcasecmp(Name, FILTER_PERCENTNEW))    AtmoSetup.Filter_PercentNew    = atoi(Value);
  else return false;

  return true;
}

void cMenuSetupFilter::Store(void)
{
  AtmoPlugin->SetupStore(FILTER_MODE,          AtmoSetup.Filter_Mode);
  AtmoPlugin->SetupStore(FILTER_MEANLENGTH,    AtmoSetup.Filter_MeanLength);
  AtmoPlugin->SetupStore(FILTER_MEANTHRESHOLD, AtmoSetup.Filter_MeanThreshold);
  AtmoPlugin->SetupStore(FILTER_PERCENTNEW,    AtmoSetup.Filter_PercentNew);
}

eOSState cMenuSetupFilter::ProcessKey(eKeys Key)
{
  int prev_Filter_Mode = AtmoSetup.Filter_Mode;
  eOSState state = cOsdMenu::ProcessKey(Key);

  if(prev_Filter_Mode != AtmoSetup.Filter_Mode)
  {
    Set();
    state = osContinue;
  }

  if (state == osUnknown)
  {
    if ((Key == kOk) || (Key == kBack))
    {
      Store();
      state = osBack;
    }
  }
  return state;
}

// --- cMenuSetupGamma -------------------------------------------------------

#define GAMMA_MODE  "Gamma_Mode"
#define GAMMA_RED   "Gamma_Red"
#define GAMMA_GREEN "Gamma_Green"
#define GAMMA_BLUE  "Gamma_Blue"

cMenuSetupGamma::cMenuSetupGamma(void) : cOsdMenu(tr("Gamma settings"), 25)
{
  useGammaTexts[0] = tr("off");      
  useGammaTexts[1] = tr("by color");

  Set();
}

void cMenuSetupGamma::Set(void)
{
  int current = Current();
  Clear();

  Add(new cMenuEditStraItem(tr("Gamma correction"), (int*)&AtmoSetup.Gamma_Mode, 2, useGammaTexts));
  if (AtmoSetup.Gamma_Mode == by_color)
  {
    Add(new cMenuEditUCharItem(tr("  Gamma red"),   &AtmoSetup.Gamma.r, 10, 30));
    Add(new cMenuEditUCharItem(tr("  Gamma green"), &AtmoSetup.Gamma.g, 10, 30));
    Add(new cMenuEditUCharItem(tr("  Gamma blue"),  &AtmoSetup.Gamma.b, 10, 30));
  }

  SetCurrent(Get(current));
  Display();
}

bool cMenuSetupGamma::SetupParse(const char *Name, const char *Value)
{
       if (!strcasecmp(Name, GAMMA_MODE))  AtmoSetup.Gamma_Mode = (tGamma) atoi(Value);
  else if (!strcasecmp(Name, GAMMA_RED))   AtmoSetup.Gamma.r    = atoi(Value);
  else if (!strcasecmp(Name, GAMMA_GREEN)) AtmoSetup.Gamma.g    = atoi(Value);
  else if (!strcasecmp(Name, GAMMA_BLUE))  AtmoSetup.Gamma.b    = atoi(Value);
  else return false;

  return true;
}

void cMenuSetupGamma::Store(void)
{
  AtmoPlugin->SetupStore(GAMMA_MODE,  AtmoSetup.Gamma_Mode);
  AtmoPlugin->SetupStore(GAMMA_RED,   AtmoSetup.Gamma.r);
  AtmoPlugin->SetupStore(GAMMA_GREEN, AtmoSetup.Gamma.g);
  AtmoPlugin->SetupStore(GAMMA_BLUE,  AtmoSetup.Gamma.b);
}

eOSState cMenuSetupGamma::ProcessKey(eKeys Key)
{
  int prev_Gamma_Mode = AtmoSetup.Gamma_Mode;
  eOSState state = cOsdMenu::ProcessKey(Key);

  if(prev_Gamma_Mode != AtmoSetup.Gamma_Mode)
  {
    Set();
    state = osContinue;
  }

  if (state == osUnknown)
  {
    if ((Key == kOk) || (Key == kBack))
    {
      Store();
      state = osBack;
    }
  }
  return state;
}

// --- cMenuEditUCharItem -----------------------------------------------------

cMenuEditUCharItem::cMenuEditUCharItem(const char *Name, unsigned char *Value, unsigned char Min, unsigned char Max)
:cMenuEditItem(Name)
{
  value = Value;
  min = Min;
  max = Max;
       if (*value < min) *value = min;
  else if (*value > max) *value = max;
  Set();
}

void cMenuEditUCharItem::Set(void)
{
  char buf[16];
  snprintf(buf, sizeof(buf), "%d", *value);
  SetValue(buf);
}

eOSState cMenuEditUCharItem::ProcessKey(eKeys Key)
{
  eOSState state = cMenuEditItem::ProcessKey(Key);

  if (state == osUnknown)
  {
    int newValue = *value;
    bool IsRepeat = Key & k_Repeat;
    Key = NORMALKEY(Key);
    switch (Key)
    {
      case kNone:
      break;
      case k0 ... k9:
           if (fresh)
           {
             newValue = 0;
             fresh = false;
           }
           newValue = newValue * 10 + (Key - k0);
      break;
      case kLeft: // TODO might want to increase the delta if repeated quickly?
           newValue = *value - 1;
           fresh = true;
           if (!IsRepeat && newValue < min && max != 255) newValue = max;
      break;
      case kRight:
           newValue = *value + 1;
           fresh = true;
           if (!IsRepeat && newValue > max && min != 0) newValue = min;
      break;
      default:
           if (*value < min) { *value = min; Set(); }
           if (*value > max) { *value = max; Set(); }
           return state;
    }
    if (newValue != *value && (!fresh || min <= newValue) && newValue <= max)
    {
      *value = newValue;
      Set();
    }
    state = osContinue;
  }
  return state;
}
