#include "statusmenu.h"
#include <string.h>
#include <vdr/status.h>

// --- cStatusMenu --------------------------------------------------------------

cStatusMenu::cStatusMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
{
  title = NULL;
  SetTitle(Title);
  SetCols(c0, c1, c2, c3, c4);
  first = 0;
  current = -1;
  reserved = 0;
  helpRed = helpGreen = helpYellow = helpBlue = NULL;
  status = NULL;
  displayMenu=NULL;
}

cStatusMenu::~cStatusMenu()
{
  free(title);
  free(status);
  displayMenu->Clear();
  cStatus::MsgOsdClear();
  delete displayMenu;
}

void cStatusMenu::Show(void)
{
  displayMenu = Skins.Current()->DisplayMenu();
  displayMenuItems = displayMenu->MaxItems() - reserved;
  Display();
}

void cStatusMenu::SetCols(int c0, int c1, int c2, int c3, int c4)
{
  cols[0] = c0;
  cols[1] = c1;
  cols[2] = c2;
  cols[3] = c3;
  cols[4] = c4;
}

void cStatusMenu::SetStatus(const char *s)
{
  if (status) displayMenu->SetMessage(mtStatus,NULL);
  free(status);
  status = s ? strdup(s) : NULL;
  if (status) displayMenu->SetMessage(mtStatus,status);
}

void cStatusMenu::SetTitle(const char *Title)
{
  free(title);
  title = strdup(Title);
}

void cStatusMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
{
  // strings are NOT copied - must be constants!!!
  helpRed    = Red;
  helpGreen  = Green;
  helpYellow = Yellow;
  helpBlue   = Blue;
  //this is necessary so that st:tng will restore the osd region it saved
  //when drawing the message. It's not enough to simply avoid the call to
  //SetButtons since all other skins make good use of it
  if (status) displayMenu->SetMessage(mtStatus,NULL);
  displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
  //and then repaint the message
  if (status) displayMenu->SetMessage(mtStatus,status);
  cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
}

void cStatusMenu::SetReserved(int res)
{
  displayMenuItems += reserved;
  reserved=res;
  displayMenuItems -= reserved;
}

void cStatusMenu::Del(int Index)
{
  cList<cOsdItem>::Del(Get(Index));
  int count = Count();
  while (current < count && !SelectableItem(current)) 
        current++;
  if (current == count) {
     while (current > 0 && !SelectableItem(current))  
           current--;
     }
  if (Index == first && first > 0)
     first--;
}

void cStatusMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After)
{
  cList<cOsdItem>::Add(Item, After);
  if (Current)
     current = Item->Index();
}

void cStatusMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
{
  cList<cOsdItem>::Ins(Item, Before);
  if (Current)
     current = Item->Index();
}

void cStatusMenu::Display(void)
{
  //this is necessary so that st:tng will restore the osd region it saved
  //when drawing the message. It's not enough to simply avoid the call to
  //SetButtons since all other skins make good use of it
  if (status) displayMenu->SetMessage(mtStatus,NULL);
  displayMenu->Clear();
  cStatus::MsgOsdClear();
  displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
  displayMenu->SetTitle(title);
  cStatus::MsgOsdTitle(title);
  displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
  //repaint the message
  if (status) displayMenu->SetMessage(mtStatus,status);
  cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
  int count = Count();
  if (count > 0) {
     int ni = 0;
     for (cOsdItem *item = First(); item; item = Next(item))
         cStatus::MsgOsdItem(item->Text(), ni++);
     if (current < 0)
        current = 0; // just for safety - there HAS to be a current item!
     if (current - first >= displayMenuItems || current < first) {
        first = current - displayMenuItems / 2;
        if (first + displayMenuItems > count)
           first = count - displayMenuItems;
        if (first < 0)
           first = 0;
        }
     //text2skin doesn't cope well with out of order menu items   
     if(reserved) for (int kk=0; kk<reserved; kk++) displayMenu->SetItem("",kk,false,false);
     int i = first;
     int n = 0;
     for (cOsdItem *item = Get(first); item; item = Next(item)) {
         displayMenu->SetItem(item->Text(), i - first + reserved, i == current, item->Selectable());
         if (i == current)
            cStatus::MsgOsdCurrentItem(item->Text());
         if (++n == displayMenuItems)
            break;
         i++;
         }
     }
}

void cStatusMenu::SetCurrent(cOsdItem *Item)
{
  current = Item ? Item->Index() : -1;
}

void cStatusMenu::RefreshCurrent(void)
{
  cOsdItem *item = Get(current);
  if (item)
     item->Set();
}

void cStatusMenu::DisplayCurrent(bool Current)
{
  cOsdItem *item = Get(current);
  if (item) {
     displayMenu->SetItem(item->Text(), current - first + reserved, Current, item->Selectable());
     if (Current)
        cStatus::MsgOsdCurrentItem(item->Text());
     if (!Current)
        item->SetFresh(true); // leaving the current item resets 'fresh'
     }
}

void cStatusMenu::Clear(void)
{
  first = 0;
  current = -1;
  cList<cOsdItem>::Clear();
}

bool cStatusMenu::SelectableItem(int idx)
{
  cOsdItem *item = Get(idx);
  return item && item->Selectable();
}

void cStatusMenu::CursorUp(void)
{
  int tmpCurrent = current;
  int lastOnScreen = first + displayMenuItems - 1;
  int last = Count() - 1;
  while (--tmpCurrent != current) {
        if (tmpCurrent < 0) {
           if (Setup.MenuScrollWrap)
              tmpCurrent = last;
           else
              return;
           }
        if (SelectableItem(tmpCurrent))
           break;
        }
  if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
     DisplayCurrent(false);
  current = tmpCurrent;
  if (current < first) {
     first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current;
     Display();
     }
  else if (current > lastOnScreen) {
     first = max(0, current - displayMenuItems + 1);
     Display();
     }
  else
     DisplayCurrent(true);
}

void cStatusMenu::CursorDown(void)
{
  int tmpCurrent = current;
  int lastOnScreen = first + displayMenuItems - 1;
  int last = Count() - 1;
  while (++tmpCurrent != current) {
        if (tmpCurrent > last) {
           if (Setup.MenuScrollWrap)
              tmpCurrent = 0;
           else
              return;
           }
        if (SelectableItem(tmpCurrent))
           break;
        }
  if (first <= tmpCurrent && tmpCurrent <= lastOnScreen)
     DisplayCurrent(false);
  current = tmpCurrent;
  if (current > lastOnScreen) {
     first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1);
     if (first + displayMenuItems > last)
        first = max(0, last - displayMenuItems + 1);
     Display();
     }
  else if (current < first) {
     first = current;
     Display();
     }
  else
     DisplayCurrent(true);
}

void cStatusMenu::PageUp(void)
{
  int oldCurrent = current;
  int oldFirst = first;
  current -= displayMenuItems;
  first -= displayMenuItems;
  int last = Count() - 1;
  if (current < 0)
     current = 0;
  if (first < 0)
     first = 0;
  int tmpCurrent = current;
  while (!SelectableItem(tmpCurrent) && --tmpCurrent >= 0)
        ;
  if (tmpCurrent < 0) {
     tmpCurrent = current;
     while (++tmpCurrent <= last && !SelectableItem(tmpCurrent))
           ;
     }
  current = tmpCurrent <= last ? tmpCurrent : -1;
  if (current >= 0) {
     if (current < first)
        first = current;
     else if (current - first >= displayMenuItems)
        first = current - displayMenuItems + 1;
     }
  if (current != oldCurrent || first != oldFirst) {
     Display();
     DisplayCurrent(true);
     }
  else if (Setup.MenuScrollWrap)
     CursorUp();
}

void cStatusMenu::PageDown(void) 
{
  int oldCurrent = current;
  int oldFirst = first;
  current += displayMenuItems;
  first += displayMenuItems;
  int last = Count() - 1;
  if (current > last)
     current = last;
  if (first + displayMenuItems > last)
     first = max(0, last - displayMenuItems + 1);
  int tmpCurrent = current;
  while (!SelectableItem(tmpCurrent) && ++tmpCurrent <= last)
        ;
  if (tmpCurrent > last) {
     tmpCurrent = current;
     while (--tmpCurrent >= 0 && !SelectableItem(tmpCurrent))
           ;
     }
  current = tmpCurrent > 0 ? tmpCurrent : -1;
  if (current >= 0) {
     if (current < first)
        first = current;
     else if (current - first >= displayMenuItems)
        first = current - displayMenuItems + 1;
     }
  if (current != oldCurrent || first != oldFirst) {
     Display();
     DisplayCurrent(true);
     }
  else if (Setup.MenuScrollWrap)
     CursorDown();
}

eOSState cStatusMenu::ProcessKey(eKeys Key)
{

  cOsdItem *item = Get(current);
  if (item) {
     eOSState state = item->ProcessKey(Key);
     if (state != osUnknown) {
        DisplayCurrent(true);
        return state;
        }
     }
  switch (Key) {
    case kUp|k_Repeat:
    case kUp:   CursorUp();   break;
    case kDown|k_Repeat:
    case kDown: CursorDown(); break;
    case kLeft|k_Repeat:
    case kLeft: PageUp(); break;
    case kRight|k_Repeat:
    case kRight: PageDown(); break;
    case kBack: return osBack;
    default: return osUnknown;
    }
  return osContinue;
}

