/*
 * netconfig_menu.c:
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: netconfig_menu.c,v 1.1.1.1 2005/06/27 13:37:56 cm Exp $
 */

#include <stdio.h>
#include <net/if_arp.h>
#include "netconfig_conf.h"
#include "netconfig_menu.h"

namespace {
  const char allowedChars[] = "abcdefghijklmnopqrstuvwxyz1234567890_-,.:/+-*@";
}

extern int menuEntry;
extern cNetconfigConf oConf;

// --- cNetconfigMenuParameter -------------------------------------------------

cNetconfigMenuEditParameter::cNetconfigMenuEditParameter(cNetconfigParameter *parameter) : cOsdMenu(tr("Edit parameter"), 10) {
  po_parameter = parameter;
  memset(key, 0x00, 101);
  memset(value, 0x00, 101);

  strncpy(key, po_parameter->GetKey(), 100);
  strncpy(value, po_parameter->GetValue(), 100);

  Set();
}

cNetconfigMenuEditParameter::~cNetconfigMenuEditParameter() {
}

void cNetconfigMenuEditParameter::Set() {
  Clear();

  Add(new cMenuEditStrItem(tr("Key"), key, 100, allowedChars));
  Add(new cMenuEditStrItem(tr("Value"), value, 100, allowedChars));

  Display();
}

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

#ifdef DEBUG
  printf("%s: cNetconfigMenuEditParameter::%s state %d\n", __FILE__, __FUNCTION__, state);
#endif

  if (osContinue != state) {
    switch (Key) {
      case kOk: // fall through
        po_parameter->SetKey(key);
        po_parameter->SetValue(value);
      case kBack:
        state = osBack;
        break;
      default:
        break;
    }
  }

  return state;
}

// --- cNetconfigMenuDetails ---------------------------------------------------

cNetconfigMenuDetails::cNetconfigMenuDetails(const char *device) : cMenuText(tr("Network"), "") {
  Set(device);
}

cNetconfigMenuDetails::~cNetconfigMenuDetails() {
}

void cNetconfigMenuDetails::Set(const char *device) {
  char *p_text = NULL;
  unsigned long addr = 0, mask = 0, bcast = 0;
  struct sockaddr *p_saddr = NULL;
  cNetconfigNetDevice oNetDev;

  addr = ntohl(oNetDev.GetAddr(device));
  mask = ntohl(oNetDev.GetMask(device));
  bcast = ntohl(oNetDev.GetBcast(device));
  p_saddr = oNetDev.GetHwAddr(device);

  asprintf(&p_text, "Encap: %s Address: %02X:%02X:%02X:%02X:%02X:%02X\n"
                    "IP Address: %d.%d.%d.%d Netmask: %d.%d.%d.%d\n"
                    "Broadcast: %d.%d.%d.%d\n"
                    "%s%s%s%s%s%s%s\n"
                    "MTU: %d Metric: %d",
           ARPHRD_ETHER == p_saddr->sa_family ? "Ethernet" :
                           ARPHRD_LOOPBACK == p_saddr->sa_family ? "Loopback" : "Misc",
           (unsigned char)p_saddr->sa_data[0], (unsigned char)p_saddr->sa_data[1],
           (unsigned char)p_saddr->sa_data[2], (unsigned char)p_saddr->sa_data[3],
           (unsigned char)p_saddr->sa_data[4], (unsigned char)p_saddr->sa_data[5],
           (unsigned char)((addr & 0xFF000000) >> 24), (unsigned char)((addr & 0x00FF0000) >> 16),
           (unsigned char)((addr & 0x0000FF00) >> 8), (unsigned char)(addr & 0x000000FF),
           (unsigned char)((mask & 0xFF000000) >> 24), (unsigned char)((mask & 0x00FF0000) >> 16),
           (unsigned char)((mask & 0x0000FF00) >> 8), (unsigned char)(mask & 0x000000FF),
           (unsigned char)((bcast & 0xFF000000) >> 24), (unsigned char)((bcast & 0x00FF0000) >> 16),
           (unsigned char)((bcast & 0x0000FF00) >> 8), (unsigned char)(bcast & 0x000000FF),
           oNetDev.isLoopback(device) ? "LOOPBACK " : "",
           oNetDev.isMulticast(device) ? "MULTICAST " : "",
           oNetDev.isPointToPoint(device) ?  "POINTOPOINT " : "",
           oNetDev.isUp(device) ? "UP " : "",
           oNetDev.isRunning(device) ? "RUNNING " : "",
           oNetDev.isBroadcast(device) ? "BROADCAST " : "",
           oNetDev.isPromiscuous(device) ? "PROMISC " : "",
           oNetDev.GetMtu(device), oNetDev.GetMetric(device));
  SetText(p_text);

  free(p_saddr);
  free(p_text);
}

// --- cNetconfigMenuConfig----------------------------------------------------------

cNetconfigMenuConfig::cNetconfigMenuConfig() : cOsdMenu(tr("Basic configuration"), 24) {
  if (0 < oConf.Interfaces()->Count())
    prevDevice = selectedDevice = oConf.Interfaces()->First()->Index();
  else
    prevDevice = selectedDevice = -1;

  memset(hostname, 0x00, 31);
  memset(domain, 0x00, 31);
  pp_devices = NULL;

  mode = imUnset;

  Set();
}

cNetconfigMenuConfig::~cNetconfigMenuConfig() {
  free(pp_devices);
}

void cNetconfigMenuConfig::Set() {
  int current = Current();
  static const char *p_modeTypes[2] = { "dhcp", tr("static") };

  if (imUnset == mode) {
    strncpy(hostname, oConf.GetHostname(), 30);
    strncpy(domain, oConf.GetDomain(), 30);
    nameserver = ntohl(oConf.GetNameserver());
    gateway = ntohl(oConf.GetGateway());
  }

  if (0 < oConf.Interfaces()->Count()) {
    if (NULL != pp_devices)
      free(pp_devices);

    pp_devices = (const char **)malloc(sizeof(char *) * oConf.Interfaces()->Count());

    if (NULL != pp_devices) {
      for (int i = 0; i < oConf.Interfaces()->Count(); i++)
        pp_devices[i] = oConf.Interfaces()->Get(i)->GetKey();
    }

    addr = ntohl(oConf.Interfaces()->Get(selectedDevice)->GetAddr());
    mask = oConf.Interfaces()->Get(selectedDevice)->GetMask();

    if (imUnset == mode || prevDevice != selectedDevice)
      mode = 0 != addr && 0 != mask ? imStatic : imDHCP;

    if (imDHCP == mode)
      addr = mask = 0;
  }

  Clear();

  Add(new cMenuEditStrItem(tr("Hostname"), hostname, 30, "abcdefghijklmnopqrstuvwxyz-"));
  Add(new cMenuEditStrItem(tr("Domain"), domain, 30, "abcdefghijklmnopqrstuvwxyz.-"));
  if (0 < oConf.Interfaces()->Count()) {
    Add(new cMenuEditStraItem(tr("Interface"), &selectedDevice, oConf.Interfaces()->Count(), pp_devices));
    Add(new cMenuEditStraItem(tr(" Mode"), &mode, 2, p_modeTypes));
    if (imStatic == mode) {
      Add(new cMenuEditAddrItem(tr("  Address"), &addr));
      Add(new cMenuEditIntItem(tr("  Netmask"), &mask, 0, 32));
      Add(new cMenuEditAddrItem(tr("Gateway Address"), &gateway));
      Add(new cMenuEditAddrItem(tr("Nameserver Address"), &nameserver));
    }
  }
  prevDevice = selectedDevice;

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

eOSState cNetconfigMenuConfig::ProcessKey(eKeys Key) {
  int current = Current();
  eOSState state = cOsdMenu::ProcessKey(Key);

#ifdef DEBUG
  printf("%s: cNetconfigMenuConfig::%s state %d\n", __FILE__, __FUNCTION__, state);
#endif

  if (osContinue != state) {
    switch (Key) {
      case kOk:
        oConf.SetHostname(hostname);
        oConf.SetDomain(domain);
        oConf.SetNameserver(htonl(nameserver));
        oConf.SetGateway(htonl(gateway));
        oConf.Interfaces()->Get(selectedDevice)->SetAddr(htonl(addr));
        oConf.Interfaces()->Get(selectedDevice)->SetMask(mask);
//      oConf.Save();
        state = osBack;
        break;
      default:
        break;
    }
  }
  if ((3 == current || 2 == current) && (kLeft == Key || kRight == Key)) {
    oConf.Interfaces()->Get(prevDevice)->SetAddr(htonl(addr));
    oConf.Interfaces()->Get(prevDevice)->SetMask(mask);
    Set();
  }

  return state;
}

// --- cNetconfigMenuState -----------------------------------------------------

cNetconfigMenuState::cNetconfigMenuState() : cOsdMenu(tr("Device state"), 35) {
  Set();
}

cNetconfigMenuState::~cNetconfigMenuState() {
}

void cNetconfigMenuState::Set(void ) {
  int current = Current();
  char *p_text = NULL;
  cNetconfigInterface *po_int = NULL;

  Clear();

  if (0 < oConf.Interfaces()->Count()) {
    SetHelp(tr("Set"), NULL, tr("Up/Down"), tr("Details"));

    for (po_int = oConf.Interfaces()->First(); NULL != po_int; po_int = oConf.Interfaces()->Next(po_int)) {
      asprintf(&p_text, "%s : %s\t%s", tr("Interface"),
                                     po_int->GetKey(),
                                     oNetDev.isUp(po_int->GetKey()) ? tr("up") : tr("down"));
      Add(new cOsdItem(p_text));
      free(p_text);
    }
  }

  SetHelp(tr("Set"), NULL, tr("Up/Down"), tr("Details"));
  SetCurrent(Get(current));
  Display();
}

eOSState cNetconfigMenuState::ProcessKey(eKeys Key) {
  static bool dirty = false;
  char *p_cmd = NULL;
  unsigned int addr = 0, bits = 0;
  unsigned long mask = 0;
  eOSState state = cOsdMenu::ProcessKey(Key);

#ifdef DEBUG
  printf("%s: cNetconfigMenuState::%s state %d\n", __FILE__, __FUNCTION__, state);
#endif

  if (! HasSubMenu() && osContinue != state  && 0 < oConf.Interfaces()->Count()) {
    switch (Key) {
      case kRed:
        addr = oConf.Interfaces()->Get(Current())->GetAddr();
        bits = oConf.Interfaces()->Get(Current())->GetMask();

        if (0 != addr && 0 != bits) {
          if (0 == oNetDev.SetAddr(oConf.Interfaces()->Get(Current())->GetKey(), addr))
            SetStatus(tr("Set interface parameters failed"));

          for (unsigned int i = 0; i < bits; i++)
            mask |= 0x80000000 >> i;

          if (0 == oNetDev.SetMask(oConf.Interfaces()->Get(Current())->GetKey(), mask))
            SetStatus(tr("Set interface parameters failed"));
        } else {
          asprintf(&p_cmd, "%s %s restart", oConf.GetDhcpScript(), oConf.Interfaces()->Get(Current())->GetKey());

          if (NULL != p_cmd && 0 != strlen(oConf.GetDhcpScript()))
            if (-1 == system(p_cmd))
              SetStatus(tr("Call to dhcp script failed"));
          else
            SetStatus(tr("Call to dhcp script failed"));
       
          free(p_cmd); 
          dirty = true;
        }
        break;
      case kYellow:
        oNetDev.SetState(oConf.Interfaces()->Get(Current())->GetKey(),
                         ! oNetDev.isUp(oConf.Interfaces()->Get(Current())->GetKey()));
        Set();
        break;
      case kOk:
      case kBlue:
        return AddSubMenu(new cNetconfigMenuDetails(oConf.Interfaces()->Get(Current())->GetKey()));
        break;
      default:
        if (dirty) {
          dirty = false;
          SetStatus(NULL);
        }
        break;
     }
   }

   return state;
}

// --- cNetconfigMenuSamba -----------------------------------------------------

cNetconfigMenuSamba::cNetconfigMenuSamba() : cOsdMenu(tr("Samba configuration"), 12) {
  Set();
}

cNetconfigMenuSamba::~cNetconfigMenuSamba() {
}

void cNetconfigMenuSamba::Set() {
  char *p_str = NULL;
  int current = Current();
  cNetconfigParameter *po_param = NULL;
  cOsdItem *po_item = NULL;

  Clear();

  for (po_param = oConf.SambaParameters()->First(); NULL != po_param; po_param = oConf.SambaParameters()->Next(po_param)) {
    if (po_param->isVisible() || po_param->isSection()) {
      if (po_param->isSection())
        asprintf(&p_str, "%s: %s", tr("Section"), po_param->GetKey());
      else
        asprintf(&p_str, "%s:\t%s", po_param->GetKey(), po_param->GetValue()); 
      po_item = new cOsdItem(p_str);
      if (po_param->isSection())
        po_item->SetSelectable(false);
      Add(po_item);
      po_item = NULL;
      free(p_str);
    }
  }

  SetHelp(tr("Edit"), NULL, NULL, NULL);
  SetCurrent(Get(0 < current ? current : 1));

  Display();
}

eOSState cNetconfigMenuSamba::ProcessKey(eKeys Key) {
  static bool dirty = false;
  int i = 0;
  cNetconfigParameter *p = NULL;
  eOSState state = cOsdMenu::ProcessKey(Key);

#ifdef DEBUG
  printf("%s: cNetconfigMenuSamba::%s state %d\n", __FILE__, __FUNCTION__, state);
#endif

  if (! HasSubMenu() && state != osContinue) {
    switch (Key) {
      case kOk:
      case kRed:
        for (p = oConf.SambaParameters()->First(); NULL != p; p = oConf.SambaParameters()->Next(p)) {
          if (p->isVisible() || p->isSection()) {
            if (Current() == i)
              break;
            i++;
          }
        }
        if (NULL != p) {
          dirty = true;
          return AddSubMenu(new cNetconfigMenuEditParameter(p));
        }
        break;
      default:
        break;
    }
    if (dirty) {
      dirty = false;
      Set();
    }
  }

  return state;
}

// --- cNetconfigMenu ----------------------------------------------------------

cNetconfigMenu::cNetconfigMenu() : cOsdMenu(tr("Network")) {
  Set();
}

cNetconfigMenu::~cNetconfigMenu() {
}

void cNetconfigMenu::Set(void ) {
  Clear();

  if (! oConf.isOk()) {
    Skins.Message(mtStatus, tr("Loading configuration file"));
    oConf.Load();
  }

  SetHasHotkeys();

  Add(new cOsdItem(hk(tr("Basic configuration")), osUser1));
  Add(new cOsdItem(hk(tr("Samba configuration")), osUser2));
  Add(new cOsdItem(hk(tr("Device state")), osUser3));

  Display();
}

eOSState cNetconfigMenu::ProcessKey(eKeys Key) {
  static bool dirty = false;
  eOSState state = cOsdMenu::ProcessKey(Key);

#ifdef DEBUG
  printf("%s: cNetconfigMenu::%s state %d\n", __FILE__, __FUNCTION__, state);
#endif

  if (! HasSubMenu()) {
    switch (state) {
      case osUser1:
        if (oConf.isOk())
          return AddSubMenu(new cNetconfigMenuConfig());
        else {
          SetStatus(tr("Bad configuration file"));
          dirty = true;
        }
        break;
      case osUser2:
        if (oConf.isOk() && 0 < oConf.SambaParameters()->Count())
          return AddSubMenu(new cNetconfigMenuSamba());
        else {
          SetStatus(tr("Bad samba configuration file"));
          dirty = true;
        }
        break;
      case osUser3:
        if (oConf.isOk())
          if (0 < oConf.Interfaces()->Count())
            return AddSubMenu(new cNetconfigMenuState());
          else
            SetStatus(tr("No network device found"));
        else {
          SetStatus(tr("Bad configuration file"));
          dirty = true;
        }
        break;
      default:
        if (dirty)
          SetStatus(NULL);
        break;
    }
  }

  return state;
}

// --- cNetconfigSetupMenu -----------------------------------------------------

cNetconfigSetupMenu::cNetconfigSetupMenu() {
  newMenuEntry = menuEntry;

  Add(new cMenuEditBoolItem(tr("Mainmenu entry"), &newMenuEntry, tr("hide"), tr("show")));
}

void cNetconfigSetupMenu::Store(void ) {
  SetupStore("menuEntry", menuEntry = newMenuEntry);
}
