/*
 * diseqc.c: DiSEqC handling
 *
 * See the main source file 'vdr.c' for copyright information and
 * how to reach the author.
 *
 * $Id: diseqc.c 1.4 2005/01/09 13:05:11 kls Exp $
 */

#include <ctype.h>
#include "tools.h"
#include "diseqc.h"
#include "sources.h"
#include "thread.h"
#include "config.h"

// -- cDiseqc ----------------------------------------------------------------
//static int cDiseqc::waitMs;
//static int cDiseqc::repeat;

cDiseqc::cDiseqc(void)
{
  satName  = NULL;
  commands = NULL;
  parsing = false;
  numCodes = 0;
}


cDiseqc::~cDiseqc()
{
  free(satName);
}

cDiseqc &cDiseqc::operator=(const cDiseqc &diseqc)
{
  source = diseqc.source;
  satName =  strcpyrealloc(satName,diseqc.satName);
  polarization = diseqc.polarization;
  commands =  strcpyrealloc(commands,diseqc.commands);
  numCodes = diseqc.numCodes;

  slof = diseqc.slof;
  lof = diseqc.lof;
  lnbID = diseqc.lnbID;
  return *this;
}

bool cDiseqc::Parse(const char *s)
{
  fprintf(stderr,"PARSE\n");
  bool result = false;
  repeat = 0;
  int fields = sscanf(s, "%a[^ ] %d %c %d %a[^\n]", &satName, &slof, &polarization, &lof, &commands);
  if (fields == 4)
     commands = NULL; //XXX Apparently sscanf() doesn't work correctly if the last %a argument results in an empty string
  if (!strchr(commands,'W'))
     Diseqcs.SetWaitMs(0);
  if (4 <= fields && fields <= 5) {
     source = cSource::FromString(satName);
     if (Sources.Get(source)) {
        polarization = toupper(polarization);
        if (polarization == 'V' || polarization == 'H' || polarization == 'L' || polarization == 'R') {
           parsing = true;
           char *CurrentAction = NULL;
           while (Execute(&CurrentAction) != daNone)
                 ;
           parsing = false;
           result = !commands || !*CurrentAction;
        }
        else
           esyslog("ERROR: unknown polarization '%c'", polarization);
     }
     else
        esyslog("ERROR: unknown source '%s'", satName);
  }
  repeat == 0 ? Diseqcs.SetRepeatCmd(repeat): Diseqcs.SetRepeatCmd(repeat-1);
  fprintf(stderr,"repeat %d\n",Diseqcs.RepeatCmd());
  return result;
}

char *cDiseqc::Wait(char *s)
{
  char *p = NULL;
  errno = 0;
  int n = strtol(s, &p, 10);
  if (!errno && p != s && n >= 0) {
     Diseqcs.SetWaitMs(n);
     if (!parsing)
        cCondWait::SleepMs(n);
     return p;
     }
  esyslog("ERROR: illegal value for wait time in '%s'", s - 1);
  return NULL;
}

char *cDiseqc::Codes(char *s)
{
  char *e = strchr(s, ']');
  if (e) {
     numCodes = 0;
     char *t = s;
     char *p = s;
     while (t < e) {
           if (numCodes < MaxDiseqcCodes) {
              errno = 0;
              int n = strtol(t, &p, 16);
              if (!errno && p != t && 0 <= n && n <= 255) {
                 codes[numCodes++] = n;
                 t = skipspace(p);
                 }
              else {
                 esyslog("ERROR: illegal code at '%s'", t);
                 return NULL;
                 }
              }
           else {
              esyslog("ERROR: too many codes in code sequence '%s'", s - 1);
              return NULL;
              }
           }
     return e + 1;
     }
  else
     esyslog("ERROR: missing closing ']' in code sequence '%s'", s - 1);
  return NULL;
}

cDiseqc::eDiseqcActions cDiseqc::Execute(char **CurrentAction)
{
  if (!*CurrentAction)
     *CurrentAction = commands;
  while (*CurrentAction && **CurrentAction) {
        switch (*(*CurrentAction)++) {
          case ' ': break;
          case 't': return daToneOff;
          case 'T': return daToneOn;
          case 'v': return daVoltage13;
          case 'V': return daVoltage18;
          case 'A': return daMiniA;
          case 'B': return daMiniB;
          case 'W': *CurrentAction = Wait(*CurrentAction); break;
          case '[': *CurrentAction = Codes(*CurrentAction);
		    if (parsing)
			repeat++;
		    return *CurrentAction ? daCodes : daNone;
          default:  return daNone;
          }
        }
  return daNone;
}

cString cDiseqc::ToText(const cDiseqc *Diseqc)
{
/* Syntax: 	 single LNB  		| mini | all    	  	full 		          mini 		  mini
 *   | S19.2E  | 11700 |  V  | 9750 |   t  |   v  | W15  | [E0 10 38 F0] | W15  |   A   |  W15  | t
 *   | SatCode | SLOF  | POL | LOF  | tone | volt | Wait |  diseqc code  | Wait | MinAB |  Wait | tone
 *   |
 * 1.|   --    |  low  |  V  |  low |  x   |  13  |  x   | [E0 10 38 x ]  | x    |   x   | t
 * 2.|   --    |   hi  |  V  |  hi  |  x   |  13  |  x   | [E0 10 38 x+1] | x    |   x   | T
 * 3.|   --    |  low  |  H  |  low |  x   |  18  |  x   | [E0 10 38 x+2] | x    |   x   | t
 * 3.|   --    |   hi  |  H  |  hi  |  x   |  18  |  x   | [E0 10 38 x+3] | x    |   x   | T
 */

 char *s, fullString[255] ;

	s = fullString;

	s += sprintf(s,"%s  %5d ", Diseqc->satName,Diseqc->slof);
	s += sprintf(s,"%c  %5d ", Diseqc->polarization,Diseqc->lof);
	s += sprintf(s,"%s",Diseqc->commands);
	s += sprintf(s,"\n");
   *s = 0; //?


 return cString(fullString,false);
}

void cDiseqc::NewDiseqc(const char *Sat, int *Source, int Type, int P, int ID, int Line)
{
   satName = NULL;

	line = Line;
	type = Type;
	source  = *Source;
	asprintf(&satName,"%s",Sat);
	SetCommands(Type, satName, P, ID, Line);
}

 // SetCommands(data.DiSEqC, satname, p, id, 2, 13, line);
//--- ::SetCommands setting new DiSEqC (1.x) commands for diseqc objects
void cDiseqc::SetCommands(int Type, char *SatName, int P, int ID, int Line) {
	SetID(ID);
	char ab =(ID+1)%2? 'A' : 'B';

	if ((Line+1) % 2){ //LOW
		 lof = Setup.LnbFrequLo;
		 slof = Setup.LnbSLOF;
	}
	else  { //HIGH
		 lof =  Setup.LnbFrequHi;
		 slof = 99999;
	}

	if (Diseqcs.RepeatCmd() > 3) {
		 fprintf(stderr, " RepeatCmd wrong value: %d\n",Diseqcs.RepeatCmd());
		 Diseqcs.SetRepeatCmd(3);
	//TODO errorlog
	}

	char *waitStr;
	if (Diseqcs.WaitMs() == 0)
		waitStr ="";
	else
		asprintf(&waitStr,"W%d",Diseqcs.WaitMs());
	//fprintf(stderr,"repeatCmd %d\n",Diseqcs.RepeatCmd());

	char *buffer = NULL;
	if(Type == OPTIM) { // Optimized DiSEqC 1.x
		 asprintf(&buffer,"%s %5d %c %5d", SatName, slof, P?'H':'V' ,lof);
		 for (int i =0; i<= Diseqcs.RepeatCmd(); i++) {
		    asprintf(&buffer,"%s %s [E%d 10 38 F%X]", buffer, waitStr, i==0?0:1, Line);
		 }
		 //fprintf(stderr,"optim: %s\n",buffer);
	}
	else { // Mini
	   asprintf(&buffer,"%s %5d %c %5d t %c W%d", SatName, slof, P?'H':'V', lof, P?'V':'v', Diseqcs.WaitMs());
		if(Type == COMB)  //
			asprintf(&buffer,"%s [E0 10 38 F%X] W%d",buffer,line,Diseqcs.WaitMs());
		asprintf(&buffer,"%s %c W%d %c",buffer, ab,  Diseqcs.WaitMs(), Line%2?'T':'t');
	}
	/*
	   asprintf(&buffer,"%s %5d %c %5d t %c ", SatName, slof, P?'H':'V', lof, P?'V':'v');

		if(Type == FULL) {
		 for (int i =0; i<= Diseqcs.RepeatCmd(); i++) {
		  asprintf(&buffer,"%s W%d [E%d 10 38 F%X]", buffer, Diseqcs.WaitMs(), i==0?0:1, Line);
		 }
     }
		asprintf(&buffer,"%s W%d %c W%d %c",buffer, Diseqcs.WaitMs(), ab, Diseqcs.WaitMs(), Line%2?'T':'t');

	 }*/

	//fprintf(stderr,"repeats: %s\n",buffer);
	//fprintf(stderr,"Full // Mini: %s\n",buffer);
	Parse(buffer);
}

cString cDiseqc::ToText(void)
{
	    return ToText(this);
}

bool cDiseqc::Save(FILE *f)
{
   return fprintf(f, "%s", *ToText()) > 0;

}

void cDiseqc::SetID(int LnbID)
{
	lnbID = static_cast<ID> (A +LnbID);
	Diseqcs.SetModified(true);
}


// -- cDiseqcs ---------------------------------------------------------------

cDiseqcs Diseqcs;


cDiseqc *cDiseqcs::Get(int Source, int Frequency, char Polarization)
{
  for (cDiseqc *p = First(); p; p = Next(p)) {
      if (p->Source() == Source && p->Slof() > Frequency && p->Polarization() == toupper(Polarization))
        return p;
      }
  return NULL;
}

bool cDiseqcs::Load(const char *FileName, bool AllowComments, bool MustExist)
{
  if (cConfig<cDiseqc>::Load(FileName, AllowComments, MustExist)) {
    ReNumber();
	return true;
	}
  return false;
}


void cDiseqcs::ReNumber(void)
{
 int id = 0, line = 0;
 for(cDiseqc *diseqc = First(); diseqc; diseqc = Next(diseqc)){

    diseqc->SetLine(line);
    diseqc->SetID(id);
    if (!((line+1)%4)) id++;  // 4 Lines == 1 LNB

    line++;
 }
}


cDiseqc *cDiseqcs::Get(const char *SatName)
{
  for (cDiseqc *p = First(); p; p = Next(p)) {
	  if (!(strcmp(p->SatName(),SatName)));
        return p;
      }
  return NULL;
}


cDiseqc *cDiseqcs::Get(int Line)
{
  for (cDiseqc *p = First(); p; p = Next(p)) {
	  if (p->Line() ==  Line);
        return p;
      }
  return NULL;
}


void cDiseqcs::SetOszilatorFrequenzies(int LofStat)
{
	if (LofStat == 0){
		::Setup.LnbFrequHi = 10600;
		::Setup.LnbFrequLo = 9750;
	}
	else {
		::Setup.LnbFrequHi = 11250;
		::Setup.LnbFrequLo = 10750;
	}
}

int  cDiseqcs::OszilatorFrequenzies(void)
{
   if (::Setup.LnbFrequHi == 10600)
		 return 0;
   else if (::Setup.LnbFrequHi == 11250)
		 return 1;

 // default
 return 0;
}

void cDiseqcs::SetModified(bool ByUser)
{
	modified = ByUser ? DISEQCMOD_USER : !modified ? DISEQCMOD_FILE : modified;
}


int cDiseqcs::Modified(void)
{
	  int Result = modified;
	  modified = DISEQCSMOD_NONE;
	  return Result;
}

bool cDiseqcs::IsUnique(int *Src, int Lnbs)
{
  for(int i = 0; i<Lnbs*4; i+=4){
    int k = i+4;
    while (k < Lnbs*4){
	if( Src[i] == Src[k]){
	  return false;
	}
	k+=4;
    }
 }
  return true;
}

/*
bool cDiseqcs::ConfigLocked (void)
{

   bool ret = true;
   if (FileWriteble(FileName())){
	ret = false;
   }
   fprintf(stderr,"cDiseqcs::ConfigLocked? %s\n",ret?"locked":"writable");
  return ret;

} */
