#include "diseqc.h"
#include "menu.h"
#include "menuitems.h"
#include <sys/ioctl.h>
#include <linux/dvb/frontend.h>
#include <math.h>
#include <vdr/interface.h>

cMenuSetupRotor::cMenuSetupRotor(void)
{
  Setup();
}

void cMenuSetupRotor::Setup(void)
{
  int current=Current();
  Clear();
  newDvbCard=data.DvbCard+1;
  Add(new cMenuEditIntItem (tr("Card, connected with motor"), &newDvbCard,1,cDevice::NumDevices()));
  Add(new cMenuEditBoolItem(tr("Repeat DiSEqC-Commands?"), &data.repeat));
  Add(new cMenuEditIntpItem(tr("Latitude"), &data.Lat,0,900,&data.SN,tr("North"),tr("South")));
  Add(new cMenuEditIntpItem(tr("Longitude"), &data.Long,0,1800,&data.EW,tr("West"),tr("East")));
  Add(new cMenuEditBoolItem(tr("Use GotoX on channel switch"), &data.UseGotoX));
  if (data.UseGotoX && cDevice::NumDevices()>=2)
  Add(new cOsdItem(tr("Configure"),osUser1));
//  Add(new cMenuEditBoolItem(tr("Setup.OSD$Info on channel switch"), &newShowInfo));
  SetCurrent(Get(current));
  Display();
}

void cMenuSetupRotor::Store(void)
{
  if (data.DvbCard!=newDvbCard-1)
  {
    static char buffer[PATH_MAX];
    snprintf(buffer, sizeof(buffer), "%s%d/%s%d", "/dev/dvb/adapter",newDvbCard-1,"frontend",0);
    close(data.fd_frontend);
    data.fd_frontend = open(buffer,0);
  } 
  SetupStore("DVB-Karte", data.DvbCard=newDvbCard-1);
  SetupStore("Wiederholen", data.repeat);
  SetupStore("Latitude", data.Lat);
  SetupStore("SouthNorth", data.SN);
  SetupStore("Longitude", data.Long);
  SetupStore("EastWest", data.EW);
  SetupStore("UseGotoX",data.UseGotoX);
  SetupStore("Card1", data.card[0]);
  SetupStore("Card2", data.card[1]);
  SetupStore("Card3", data.card[2]);
  SetupStore("Card4", data.card[3]);
  SetupStore("ShowInfo",data.ShowInfo);
}

eOSState cMenuSetupRotor::ProcessKey(eKeys Key)
{
  int oldUseGotoX = data.UseGotoX;
  eOSState state = cOsdMenu::ProcessKey(Key);
  if (Key != kNone && data.UseGotoX != oldUseGotoX)
     Setup();
  switch (state) {
    case osUser1:   return AddSubMenu(new cSubMenuSetupRotor);
    case osUnknown: if (Key==kOk) {
                      Store();
                      state = osBack;
                      }
                    break;
    default: ;
    }
  return state;
}

// --- cSubMenuSetupRotor -----------------------------------------------

cSubMenuSetupRotor::cSubMenuSetupRotor(void)
{
  Add(new cMenuEditBoolItem(tr("React to switching of 1.Card"), &data.card[0]));
  Add(new cMenuEditBoolItem(tr("React to switching of 2.Card"), &data.card[1]));
  if (cDevice::NumDevices()>=3)
    Add(new cMenuEditBoolItem(tr("React to switching of 3.Card"), &data.card[2]));
  if (cDevice::NumDevices()>=4)
  Add(new cMenuEditBoolItem(tr("React to switching of 4.Card"), &data.card[3]));
}

// --- cMainMenuRotor --------------------------------------------------

cMainMenuRotor::cMainMenuRotor(void):cOsdMenu("Rotor-Plugin"), cThread("Rotor-Plugin")
{
  edw=cSkinDisplay::Current()->EditableWidth();
  oldupdate=Setup.UpdateChannels;
  Setup.UpdateChannels=0;
  assign=false;
  SetHelp(tr("Store"),tr("Goto"),tr("Recalc"),tr("Limits off"));
  OldChannel=Channels.GetByNumber(cDevice::GetDevice(data.DvbCard)->CurrentChannel());
  HasSwitched=false;
  Nr=List.GetfromSource(OldChannel->Source());
  Position=List.Pos(Nr);
  Frequenz=(*OldChannel).Frequency();
  if ((*OldChannel).Polarization() == 'v' || (*OldChannel).Polarization() == 'V')
    Pol='V';
  else
    Pol='H';
  Symbolrate=(*OldChannel).Srate();
  m_Active = false;

  enum { NUMCHARS = cFont::NUMCHARS } ;
  const cFont::tCharData *data;
  
    
  for (int i = 32, n=0; i < NUMCHARS; i++) {
    data = cFont::GetFont(fontSml)->CharData(i);
    CharData[n++]=data->width;
    CharData[n++]=data->height;
    for (ulong j=0; j<data->height; j++) {
      CharData[n++]=data->lines[j];
    }
  }
  CharData[(127-32)*(cFont::GetFont(fontSml)->Height()+2)]=1;
  CharData[(128-32)*(cFont::GetFont(fontSml)->Height()+2)]=1;
  for (int i=6; i<=cFont::GetFont(fontSml)->Height()-3 ;i++)
    CharData[(128-32)*(cFont::GetFont(fontSml)->Height()+2)+i]=0x00000001;
  cFont::SetFont(fontSml, CharData);
  cFont::SetFont(fontOsd, CharData);
  
  cOsdItem* n[5];
  for (int i=0; i<5; i++)
  {
    n[i]=new cOsdItem("");
    n[i]->SetSelectable(false);
  }
  Add(n[0]);
  Add(n[1]);
  Add(n[2]);
  Add(new cMenuEditEWItem(ThreeItems(tr("Drive East"),tr("Halt"),tr("Drive West"),edw)));
  Add(new cMenuEditStepsEWItem());
  Add(new cMenuEditLimitsItem(ThreeItems(tr("Set East Limit"),tr("Enable Limits"),tr("Set West Limit"),edw)));
  Add(n[3]);
  Add(new cMenuEditSatItem(Nr,Position,assign));
  Add(n[4]);
  Add(new cMenuEditFreqItem(tr("Frequency"),&Frequenz,&Pol,osUser1));
  Add(new cMenuEditSymbItem(tr("Symbolrate"),&Symbolrate,0,50000,osUser1));
  Add(new cOsdItem(CenterText(tr("Scan Transponder"),edw),osUser2));
  CursorDown();
  Start();
}

cMainMenuRotor::~cMainMenuRotor()
{
  Setup.UpdateChannels=oldupdate;
  if (HasSwitched)
    cDevice::GetDevice(data.DvbCard)->SwitchChannel(OldChannel,true);

  cFont::SetFont(fontSml);
  cFont::SetFont(fontOsd);


  if (m_Active)
  {
    m_Active = false;
    Cancel(3);
  }
}

void cMainMenuRotor::Action(void)
{
  unsigned int SNRChip=0,SSChip=0,i;
  char buf[700];
  m_Active = true;
  while (m_Active)
  {
    if (!HasSubMenu())
    {
      CHECK(ioctl(Fd_Frontend, FE_READ_SNR, &SNRChip));
      usleep(15);
      for (i=0; i<=(SNRChip*(edw-Width("SNR:  99% = -99.9dB"))/65536);i++) 
        buf[i]='\x80';
      buf[i]='\0';
      sprintf(SNR,"SNR:\t%s\t%d%% = %.1fdB",buf,SNRChip/655,SNRChip>57000 ? (logf((double)SNRChip/6553.5)*10.0):(-3/(logf(SNRChip/65535.0))));
      CHECK(ioctl(Fd_Frontend, FE_READ_SIGNAL_STRENGTH, &SSChip));
      usleep(15);
      for (i=0; i<=(SSChip*(edw-Width("SNR:  99% = -99.9dB"))/65535);i++)
        buf[i]='\x80';
      buf[i]='\0';
      sprintf(SS,"SS:\t%s\t%d%% = %.1fdB",buf,SSChip/655,(logf((double)SSChip/65535)*10.8));
      DisplayMenu()->SetTabs((Width("SNR: ")+6)/12,(edw-Width("99% = -99.9dB"))/12-Width("SNR: ")/12,0,0);
      DisplayMenu()->SetItem(SNR,0,false,false);
      DisplayMenu()->SetItem(SS,1,false,false);
    }
    usleep(500000);
  }
}

eOSState cMainMenuRotor::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);
  if (state == osUser1)
  {
    cChannel *SChannel = new cChannel;
    *SChannel = *OldChannel;
    SChannel->cChannel::SetSatTransponderData(List.Code(Nr),Frequenz,Pol,Symbolrate,FEC_AUTO);
    if (cDevice::GetDevice(data.DvbCard)==cDevice::ActualDevice())
      HasSwitched=true;
    bool UseDiseqc=Setup.DiSEqC;
    Setup.DiSEqC=false;
    cDevice::GetDevice(data.DvbCard)->SwitchChannel(SChannel,HasSwitched);
    Setup.DiSEqC=UseDiseqc;
  }
  if (state == osUser2)
  {
    cChannel *SChannel = new cChannel;
    *SChannel = *OldChannel;
    SChannel->cChannel::SetSatTransponderData(List.Code(Nr),Frequenz,Pol,Symbolrate,FEC_AUTO);
    if (cDevice::GetDevice(data.DvbCard)==cDevice::ActualDevice())
      HasSwitched=true;
    bool UseDiseqc=Setup.DiSEqC;
    Setup.DiSEqC=false;
    data.ActualSource=List.Code(Nr);
    cDevice::GetDevice(data.DvbCard)->SwitchChannel(SChannel,HasSwitched);
    Setup.DiSEqC=UseDiseqc;
    AddSubMenu(new cMenuScan);
  }
/*  if (!Position && !assign && Current()==7)
  {
    SetHelp(tr("Store"),tr("Goto"),tr("Recalc"),tr("Assign Source"));
  }
  else
  {
    SetHelp(tr("Store"),tr("Goto"),tr("Recalc"),tr("Limits off"));
    if (Current()!=7)
      assign=false;
  }
*/
  if (state == osUnknown)
  {
    Key = NORMALKEY(Key);
    switch (Key) {
      case kRed   :
                    if (Interface->Confirm(tr("Are you sure?"),10))
                      DiseqcCommand(Store,List.Pos(Nr));
                    break;
      case kGreen : Setup.UpdateChannels=0;
                    DiseqcCommand(Goto,List.Pos(Nr));
                    break;
      case kYellow:
                    if (Interface->Confirm(tr("Are you sure?")))
                      DiseqcCommand(Recalc,List.Pos(Nr));
                    break;
      case kBlue  :
                    if (Interface->Confirm(tr("Are you sure?")))
                      DiseqcCommand(LimitsOff);
                    break;
      default     : return state;
                 }
    state=osContinue;
  }
  return state;
}


cMenuScan::cMenuScan():cOsdMenu("Transponder-Scan")
{
  num=0;
  PFilter = new PatFilter(this);
  SFilter = new SdtFilter(PFilter,this);
  PFilter->SetSdtFilter(SFilter);
  cDevice::GetDevice(data.DvbCard)->AttachFilter(SFilter);
  cDevice::GetDevice(data.DvbCard)->AttachFilter(PFilter);
  SetHelp("Add Channel","Add All","","");
}

cMenuScan::~cMenuScan()
{
  if (PFilter)
    cDevice::GetDevice(data.DvbCard)->Detach(PFilter);
  if (SFilter)
    cDevice::GetDevice(data.DvbCard)->Detach(SFilter);
}

eOSState cMenuScan::ProcessKey(eKeys Key)
{
  eOSState state = cOsdMenu::ProcessKey(Key);
  if (state == osUnknown)
  {
    Key = NORMALKEY(Key);
    switch (Key) {
      case kRed:   AddChannel(Current());
                   break;
      case kGreen: for (int i=0; i<num; i++)
                     AddChannel(i);
                   break;
      case kOk: if (PFilter && PFilter->EndOfScan())
                {
                  cDevice::GetDevice(data.DvbCard)->Detach(PFilter);
                  PFilter=NULL;
                  cDevice::GetDevice(data.DvbCard)->Detach(SFilter);
                  SFilter=NULL;
                }     
                cDevice::GetDevice(data.DvbCard)->SwitchChannel(&Channel[Current()],true);
                break;
      default: return state;
      }
    state=osContinue;
  }
  return state;
}

void cMenuScan::AddChannel(int Num)
{
  cChannel *channel=Channels.GetByServiceID(Channel[Num].Source(),Channel[Num].Transponder(), Channel[Num].Sid());
  if (channel)
  {
    channel->SetName(Channel[Num].Name(),Channel[Num].ShortName(),Channel[Num].Provider());
    channel->SetId(Channel[Num].Nid(),Channel[Num].Tid(),Channel[Num].Sid(),channel->Rid());
    int Apids[MAXAPIDS + 1] = { 0 };
    int Dpids[MAXDPIDS + 1] = { 0 };
    char ALangs[MAXAPIDS + 1][4] = { "" };
    char DLangs[MAXDPIDS + 1][4] = { "" };
    int CaIds[MAXCAIDS+1] = { 0 };
    for (int i=0; i<=MAXAPIDS; i++)
    {
      Apids[i]=Channel[Num].Apid(i);
      strcpy(ALangs[i],Channel[Num].Alang(i));
    }
    for (int i=0; i<=MAXDPIDS; i++)
    {
      Dpids[i]=Channel[Num].Dpid(i);
      strcpy(DLangs[i],Channel[Num].Dlang(i));
    }
    for (int i=0; i<=MAXCAIDS; i++)
      CaIds[i]=Channel[Num].Ca(i);
    channel->SetPids(Channel[Num].Vpid(),Channel[Num].Ppid(),Apids,ALangs,Dpids,DLangs,Channel[Num].Tpid());
    channel->SetCaIds(CaIds);
  }
  else
  {
    for (int i=0; i<1000; i++)
      if (Channels.GetByChannelID(tChannelID(Channel[i].Source(), Channel[i].Nid(), Channel[i].Tid(), Channel[i].Sid(),i)))
        channel=Channels.NewChannel(&Channel[Num], Channel[Num].Name(), Channel[Num].ShortName(), Channel[Num].Provider(), Channel[Num].Nid(), Channel[Num].Tid(), Channel[Num].Sid(),i);
  }
}

void cMenuScan::NewChannel(const cChannel *Transponder, const char *Name, const char *ShortName, const char *Provider, int Nid, int Tid, int Sid)
{
  for (int i=0; i<num; i++)
    if (Sid==Channel[i].Sid())
      return;
  Channel[num]=*Transponder;
  Channel[num].SetName(Name,ShortName,Provider);
  Channel[num].SetId(Nid,Tid,Sid);
  n[num]=new cOsdItem("no Info");
  Add(n[num]);
  display(num);
  num++;
}

void cMenuScan::SetPids(int Sid,int Vpid, int Ppid, int *Apids, char ALangs[][4], int *Dpids, char DLangs[][4], int Tpid)
{
  for (int i=0; i<num; i++)
    if (Sid==Channel[i].Sid())
    {
      Channel[i].SetPids(Vpid,Ppid,Apids,ALangs,Dpids,DLangs,Tpid);
      display(i);
    }
}

void cMenuScan::SetCaIds(int Sid,const int *CaIds)
{
  for (int i=0; i<num; i++)
    if (Sid==Channel[i].Sid())
    {
      Channel[i].SetCaIds(CaIds);
      display(i);
    }
}

void cMenuScan::SetCaDescriptors(int Sid,int Level)
{
  for (int i=0; i<num; i++)
    if (Sid==Channel[i].Sid())
    {
      Channel[i].SetCaDescriptors(Level);
      display(i);
    }
}

cChannel* cMenuScan::GetChannel(int Sid)
{
  for (int i=0; i<num; i++)
    if (Sid==Channel[i].Sid())
      return &Channel[i];
  return NULL;
}


void cMenuScan::display(int num)
{
  char buf[200]="";
  sprintf(buf,"%s - %d - %d - %d (%s) %c",Channel[num].Name(),Channel[num].Sid(),Channel[num].Vpid(),Channel[num].Apid(0),*cSource::ToString(Channel[num].Source()),Channel[num].Ca(0) ? '*' : ' ');
  n[num]->SetText(buf);
  cOsdMenu::Display();
}


config data;
