
#include "autogroup.h"
#include "autogroups.h"
#include <vdr/plugin.h>
#include "autosort_menu.h"
#include <vdr/interface.h>

class cNewAutoSort {
private:
  cAutoGroup *workingGroup;
public:
  cNewAutoSort(void);
  virtual ~cNewAutoSort();
  void Dump(int from, int to);
  bool UserBreak (bool CompleteRun = false);
  void CheckDelimiters (void);
  void CheckChannels (bool completeRun = false);
  void Sort(void);
  bool ChannelFitsGroup (int ChIndex, cAutoGroup *group);
  void MoveChannelToGroup (int ChIndex, cAutoGroup *group);
  void MoveGroupToEnd (int ChIndex);
  void MoveGroupBehindGroup (int ChIndex, cAutoGroup *group);
  void AddGroup (cAutoGroup *group);
  void InsGroup (cAutoGroup *group, int ChIndex);
  void DeleteGroup (int ChIndex);
  void ModifyChannelToGroup (int ChIndex, cAutoGroup *group, bool Out = false);
  void SetTimeStamp (int ChIndex);
  const char *InGroup (int ChIndex);
  };

cNewAutoSort::cNewAutoSort(void)
{
  AutoGroups.Load(AddDirectory(cPlugin::ConfigDirectory(), "autosort.conf"), true, true);
  CheckDelimiters();
  workingGroup = AutoGroups.GetFirstByPrio();
  if ( (time(NULL) - timeStampSetDelta) > lastTimeStampSet) writeNewTimeStamp = true;
  dsyslog ("AutoSort: Next TimeStamp writing: %s", *TimeToString(timeStampSetDelta + lastTimeStampSet));
  if (0){ // ToDo: replace by config option "On Startup"
    CheckChannels();
    }
}

cNewAutoSort::~cNewAutoSort()
{
}

void cNewAutoSort::Sort(void)
{
  cAutoGroup *group = workingGroup;
  int firstChIndex;
  int lastChIndex;
  int moves = 0;
  //while (group) {
    if (group->Sort()){
      firstChIndex = group->GetChIndex() + 1;
      //if (AutoGroups.GetNextByOrder(group->OIdx())){
        //lastChIndex = AutoGroups.GetNextByOrder(group->OIdx())->GetChIndex() - 1;
      if (Channels.GetNextGroup(group->GetChIndex())){ // XXX
        lastChIndex = Channels.GetNextGroup(group->GetChIndex()) - 1;
      } else {
	lastChIndex = Channels.Last()->Index();
	}
      if ((lastChIndex - firstChIndex) > 0){
        //dsyslog("AutoSort: sorting group %s",group->Name());
	for (cChannel *channel1 = Channels.Get(firstChIndex); channel1->Index() < lastChIndex; channel1 = Channels.Next(channel1)) {
	  int ch2Index = channel1->Index() +1;
	  if (versionSorting) {
	    while ( (ch2Index <= lastChIndex) && (strverscmp(UpString(KillSpecial(strdup(channel1->Name()))), UpString(KillSpecial(strdup(Channels.Get(ch2Index)->Name())))) > 0 ) ) {
	      ch2Index++;
	      }
	  } else {
	    while ( (ch2Index <= lastChIndex) && (strcasecmp(channel1->Name(), Channels.Get(ch2Index)->Name()) > 0 ) ) {
	      ch2Index++;
	      }
	    }
	  ch2Index--;
	  if (channel1->Index() != ch2Index ){
	    if (channel1->Index() < AutoGroups.GetFirstByOrder()->GetChIndex() ){
	      dsyslog("AutoSort: -ERROR- ::Sort() trys working before Auto-Sort-Mark  -ABORTED-");
	      return;
	      }	    
	    dsyslog("AutoSort: sort channel '%s' behind '%s'", channel1->Name(), Channels.Get(ch2Index)->Name());
	    if (ch2Index != Channels.Last()->Index()) { 
	      Channels.Move(channel1, Channels.Get(ch2Index));
              Channels.ReNumber();
              Channels.SetModified();
	      if (strcmp(InGroup(ch2Index), group->Name())!=0) {
                dsyslog ("ERROR: (Sort) Moved %s to %s instead of %s ", Channels.Get(ch2Index)->Name(), InGroup(ch2Index), group->Name());
                Dump(ch2Index -3,ch2Index +3);
                }
	      moves++;
	      alarm(60);
	      if (moves > maxMoves) return;
	      }
            }
	  }
	}
      }
    //group = AutoGroups.GetNextByOrder(group->OIdx());
    //}
}

//#undef DELETECA

void cNewAutoSort::ModifyChannelToGroup (int ChIndex, cAutoGroup *group, bool Out)
{
  cChannel *channel = Channels.Get(ChIndex);
  const int CaFF[] = {255,0};
#ifndef DELETECA
  const int Ca0[] = {0};
#endif
  if (!Out) { // channel will go into group
    if (group->SetCaFF()) {
#ifdef DELETECA
      channel->DeleteCaIds();
#else      
      if ((channel->Ca(0) >= 0x0001) && (channel->Ca(0) <= 0x00FF)){
        isyslog("AutoSort: -WARNING- You need the 'deleteca'-patch for '%s'", channel->Name());
      } else {
        channel->SetCaIds(Ca0);      
        }
#endif      
      channel->SetCaIds(CaFF);
      }
    if (group->SetCa0()) {
#ifdef DELETECA
      channel->DeleteCaIds();
#else      
      if ((channel->Ca(0) >= 0x0001) && (channel->Ca(0) <= 0x00FF)){
        isyslog("AutoSort: -WARNING- You need the 'deleteca'-patch for '%s'", channel->Name());
      } else {
        channel->SetCaIds(Ca0);      
        }
#endif      
      }
  } else { // channel will go out of group
    if (group->SetCaFF()) {
#ifdef DELETECA
      channel->DeleteCaIds();
#else      
      if ((channel->Ca(0) >= 0x0001) && (channel->Ca(0) <= 0x00FF)){
        isyslog("AutoSort: -WARNING- You need the 'deleteca'-patch for '%s'", channel->Name());
      } else {
        channel->SetCaIds(Ca0);      
        }
#endif      
      }
    }
}

bool cNewAutoSort::ChannelFitsGroup (int ChIndex, cAutoGroup *group)
{
  cChannel *channel = Channels.Get(ChIndex);
  int i = 0;
  int j = 0;
  bool fits = false;
  // Tri-states
  // -1 : must NOT have
  //  0 : don't care
  //  1 : must have  
  if (( group->HasApid() > 0) && (channel->Apid(0) == 0 )) return false;
  if (( group->HasApid() < 0) && (channel->Apid(0) != 0 )) return false;  
  if (( group->HasVpid() > 0) && (channel->Vpid()  == 0 )) return false;
  if (( group->HasVpid() < 0) && (channel->Vpid()  != 0 )) return false;  
  if (( group->HasTpid() > 0) && (channel->Tpid()  == 0 )) return false;
  if (( group->HasTpid() < 0) && (channel->Tpid()  != 0 )) return false;
  if (( group->HasCa()   > 0) && (channel->Ca(0)   == 0 )) return false;
  if (( group->HasCa()   < 0) && (channel->Ca(0)   != 0 )) return false;

  if (strcmp(group->StartName(), "") != 0) {
    if (strncasecmp(group->StartName(), channel->Name(), 1) != 0) return false;
    }

  if (group->TimeStampDays() > 0) {
    if (strstr(channel->ShortName(),"ASTS")){
      //dsyslog ("found TimeStamp");
      char *buf = strdup(channel->ShortName());
      strsep(&buf,"-");
      int stamp = atoi(buf);
      if ((time(NULL) - stamp) < ( group->TimeStampDays() * 86400 )) return false;
      //dsyslog("not old enought");
    } else {
      return false; // no TimeStamp
      }
    }
    
  if (strcmp(group->HasName(0),"NotSet")){
    i = 0;
    fits = false;
    while (strcmp(group->HasName(i),"NotSet") != 0) {
      if (strstr(UpString(strdup(channel->Name())),group->HasName(i))) {
        fits = true;
        }
      i++;
      }
    if (!fits) return false;
    }

  if (strcmp(group->NotName(0),"NotSet")){
    i = 0;
    while (strcmp(group->NotName(i),"NotSet") != 0) {
      if (strstr(UpString(strdup(channel->Name())),group->NotName(i))) {
        fits = true; //ToDo: delete this ?
	return false;
        }
      i++;
      }
    }
  if (strcmp(group->HasSource(0),"NotSet")){
    i = 0;
    fits = false;
    while (strcmp(group->HasSource(i),"NotSet") != 0) {
      if (channel->Source() == cSource::FromString(group->HasSource(i))) {
        fits = true;
        }	
      i++;
      }
    if (!fits) return false;
    }

  if (strcmp(group->HasProvider(0),"NotSet")){
    i = 0;
    fits = false;
    while (strcmp(group->HasProvider(i),"NotSet") != 0) {
      if (strstr(UpString(strdup(channel->Provider())),group->HasProvider(i))) {
        fits = true;
        }
      i++;
      }
    if (!fits) return false;
    }
        
  if (strcmp(group->NotProvider(0),"NotSet")){
    i = 0;
    while (strcmp(group->NotProvider(i),"NotSet") != 0) {
      if (strstr(UpString(strdup(channel->Provider())),group->NotProvider(i))) {
	return false;
        }
      i++;
      }
    }

  char *caHex;  
  if (strcmp(group->CaValue(0),"NotSet") != 0){
    i = 0;
    fits = false;
    while (strcmp(group->CaValue(i),"NotSet") != 0) {
      j = 0;      
      while (channel->Ca(j) != 0) {
        caHex="";	
	asprintf(&caHex, "%X", channel->Ca(j));	
        if (strstr(strdup(caHex),group->CaValue(i))) {
          fits = true;
          }	  
	j++;
	}	
      i++;
      }
    if (!fits) return false;
    }

  if (strcmp(group->NotCaValue(0),"NotSet") != 0){
    i = 0;
    while (strcmp(group->NotCaValue(i),"NotSet") != 0) {
      j = 0;      
      while (channel->Ca(j) != 0) {
        caHex="";	
	asprintf(&caHex, "%X", channel->Ca(j));	
        if (strstr(strdup(caHex),group->NotCaValue(i))) {
          return false;
          }	  
	j++;
	}	
      i++;
      }
    }

  if (strcmp(group->AudioLang(0),"NotSet") != 0){
    i = 0;
    fits = false;
    while (strcmp(group->AudioLang(i),"NotSet") != 0) {
      j = 0;
      while (strcmp(channel->Alang(j),"") != 0) {
        if (strstr(UpString(strdup(channel->Alang(j))),group->AudioLang(i))) {
          fits = true;
          }
	j++;
	}
      i++;
      }
    if (!fits) return false;
    }
  
  return true;
}


void cNewAutoSort::CheckChannels ( bool completeRun )
{
  char *buf;
  //cAutoGroup *workingGroup = AutoGroups.GetFirstByPrio();
  
  if (completeRun) workingGroup = AutoGroups.GetFirstByPrio();
  //if (completeRun) workingGroup = AutoGroups.GetByName("Sport");

  cAutoGroup *FromGroup = AutoGroups.GetNextByPrio(workingGroup->PIdx());
  cChannel *channel1;
  int i;
  int moves = 0;

  if ((completeRun) && (messageLevel >= 1)) Skins.Message(mtStatus,"AutoSort: Checking groups", messageTime);

  while ((workingGroup) && ((moves < maxMoves) || (completeRun))){
    if (UserBreak(completeRun)) {
      if (messageLevel >= 2) Skins.Message(mtStatus,NULL,0);
      return;
      }
    dsyslog("AutoSort: following rules for group %s", workingGroup->Name());
    if (messageLevel >= 2) {
      asprintf(&buf, "AutoSort: Checking group '%s'", workingGroup->Name());
      Skins.Message(mtStatus, buf, messageTime);    
      }
    if ( !workingGroup->KeepHere() && !workingGroup->IsSpecial()) {
      //dsyslog("Check channels in group %s for re-sort", workingGroup->Name());
      channel1 = Channels.Get(workingGroup->GetChIndex());
      if (Channels.GetNextGroup(channel1->Index()) > 0){	
        i = Channels.GetNextGroup(channel1->Index()) - 1;
      } else {
        i = Channels.Last()->Index();
        }
      //while ((i > channel1->Index()) && ((moves < maxMoves) || (completeRun))){
      while ((i > workingGroup->GetChIndex()) && ((moves < maxMoves) || (completeRun))){
        if (UserBreak(completeRun)) {
          if (messageLevel >= 2) Skins.Message(mtStatus,NULL,0);
          return;
          }	  
	if ((writeTimeStamp) && (writeNewTimeStamp)) SetTimeStamp(i); // found no better place for this
	alarm(60);
        if (!ChannelFitsGroup(i,workingGroup)){	
	  ModifyChannelToGroup(i, workingGroup, true);
          cAutoGroup *ToGroup = AutoGroups.GetByName("Unsorted");
          MoveChannelToGroup(i, ToGroup);
          moves++;
          }
        i--;
        }
      }
    if (!workingGroup->IsSpecial()) {
      FromGroup = AutoGroups.GetNextByPrio(workingGroup->PIdx());
      //while (FromGroup &&    //Check here if higher Prio Group isn't allowed to override KeepHere
      //      (strcmp(FromGroup->Name(),"New Channels") != 0) &&
      //      (strcmp(FromGroup->Name(),"Auto Sort") != 0)){ 
      while ((FromGroup) && (strcmp(FromGroup->Name(),"New Channels") != 0)){
        if (UserBreak(completeRun)) {
          if (messageLevel >= 2) Skins.Message(mtStatus,NULL,0);
          return;
          }
        //dsyslog("Check channels in group %s for group %s",FromGroup->Name(), workingGroup->Name());
        channel1 = Channels.Get(FromGroup->GetChIndex());
	if (Channels.GetNextGroup(channel1->Index()) > 0){	
	  i = Channels.GetNextGroup(channel1->Index()) - 1;
        } else {
	  i = Channels.Last()->Index();
          }
       while ((i > channel1->Index()) && ((moves < maxMoves) || (completeRun))){
         if (UserBreak(completeRun)) {
           if (messageLevel >= 2) Skins.Message(mtStatus,NULL,0);
           return;
           }
	 alarm(60);
         if (ChannelFitsGroup(i,workingGroup)){
           if ((writeTimeStamp) && (writeNewTimeStamp)) SetTimeStamp(i); // found no better place for this
	   ModifyChannelToGroup(i, FromGroup, true);
	   ModifyChannelToGroup(i, workingGroup, false);
	   MoveChannelToGroup(i, workingGroup);
	   moves++;	   	   
	   }
	 i--;
         }      
        FromGroup = AutoGroups.GetNextByPrio(FromGroup->PIdx());
        }	
      }               
    if (messageLevel >= 2) Skins.Message(mtStatus,NULL,0);
    if ((moves >= maxMoves) && !completeRun) return;
    Sort();
    workingGroup = AutoGroups.GetNextByPrio(workingGroup->PIdx());
    if (messageLevel >= 1) Skins.Message(mtStatus,NULL,0);
    if (!workingGroup){ // We have all done
      if (messageLevel >= 1) Skins.Message(mtInfo,"AutoSort: Run Completed", messageTime);
      workingGroup = AutoGroups.GetFirstByPrio();
      if (writeNewTimeStamp) {
        //dsyslog("Run complete - timestamps wrote - i wish to save my setup");
        lastTimeStampSet = time(NULL);
	saveSetup = true;
	writeNewTimeStamp = false;
        }
      if ( time(NULL) - timeStampSetDelta > lastTimeStampSet) {
        writeNewTimeStamp = true;
      } else {
	dsyslog ("AutoSort: Next TimeStamp writing: %s", *TimeToString(timeStampSetDelta + lastTimeStampSet));
	}
      return;
      }
    if (!completeRun) return; // Just one Group per Run
    if (UserBreak(completeRun)) return;
    }
}

bool cNewAutoSort::UserBreak (bool CompleteRun)
{
  eKeys key = cRemote::Get(0);
  if (NORMALKEY(key) == kNone) {
    return false;
  } else {
    if (!CompleteRun) {
      //dsyslog("UserBreak on Housekeeping");
      return true;
    } else {
      if (NORMALKEY(key) == kBack) {
        //dsyslog("UserBreak on CompleteRun");
        return true;
	}
      }
    }
  return false;
}

void cNewAutoSort::CheckDelimiters (void)
{
  if (messageLevel >= 1) Skins.Message(mtStatus,"AutoSort: Checking Group Delimiters", messageTime);
  cAutoGroup *group = AutoGroups.GetFirstByOrder();
  char *buf;  
  bool foundStart = false;
  int firstGroupChIdx = 1;
  for (cChannel *channel = Channels.First(); (channel && !foundStart); channel = Channels.Get(Channels.GetNextGroup(channel->Index()))) 
    {
    if (strcmp(channel->Name(),group->Name()) == 0) 
      {
      foundStart = true;
      firstGroupChIdx = channel->Index();
      dsyslog("AutoSort: found group '%s' @ Index %d ",group->Name(), channel->Index());
      }
    }
  if (!foundStart){
    // Create all New
  if (messageLevel >= 1) Skins.Message(mtStatus,"AutoSort: Creating Group Delimiters", messageTime);
    group = AutoGroups.GetFirstByOrder();    
    while (group) {
      if (messageLevel >= 2) {
        asprintf(&buf, "AutoSort: Creating group '%s'", group->Name());
        Skins.Message(mtStatus, buf, messageTime);    
        }
      AddGroup(group);
      group= AutoGroups.GetNextByOrder(group->OIdx());
      }
  } else {
    // Is everybody there?
    if (messageLevel >= 1) Skins.Message(mtStatus,"AutoSort: Checking for missing Group Delimiters", messageTime);
    group = AutoGroups.GetFirstByOrder();    
    // Test for fixed Pos 'Auto Sort'
    if (group->FixedPos() > 0){
      if (group->FixedPos() != Channels.Get(firstGroupChIdx)->Number()){
        dsyslog ("AutoSort: Changing FixedPosition of 'Auto Sort' to %d", group->FixedPos());
        Channels.Get(firstGroupChIdx)->SetNumber(group->FixedPos());
        }
      }
    bool foundNext;
    while ((group = AutoGroups.GetNextByOrder(group->OIdx()))){
      foundNext = false;
      //dsyslog("AutoSort: Searching Group %s", group->Name());
      for (cChannel *channel = Channels.Get(Channels.GetNextGroup(firstGroupChIdx)); (channel && !foundNext); channel = Channels.Get(Channels.GetNextGroup(channel->Index()))) 
        {
        if (strcmp(channel->Name(),group->Name()) == 0) 
          {
          foundNext = true;
	  // Test for fixed Pos
	  if (group->FixedPos() > 0){
	    if (group->FixedPos() != channel->Number()){
	      dsyslog ("AutoSort: Changing FixedPosition of '%s' to %d",channel->Name(), group->FixedPos());
	      channel->SetNumber(group->FixedPos());
	      }
	    }	  
          }
	}  
        if (!foundNext) {
          if (messageLevel >= 2) {
            asprintf(&buf, "AutoSort: Creating group '%s'", group->Name());
            Skins.Message(mtStatus, buf, messageTime);    
            }
	  //dsyslog("AutoSort: Creating group '%s'", group->Name());
	  AddGroup(group);	  
	  }
      } 
    // Is anyone to much?    
    if (messageLevel >= 1) Skins.Message(mtStatus,"AutoSort: Checking for old Group Delimiters", messageTime);    
    for (cChannel *channel = Channels.Get(Channels.GetNextGroup(firstGroupChIdx)); channel; channel = Channels.Get(Channels.GetNextGroup(channel->Index()))) 
      {      
      foundNext = false;
      //dsyslog("AutoSort: Found Group %s", channel->Name());
      group = AutoGroups.GetFirstByOrder();    
      while ((group = AutoGroups.GetNextByOrder(group->OIdx()))){
        //dsyslog("AutoSort: Compare with %s",group->Name());
        if (strcmp(channel->Name(),group->Name()) == 0) 
          {
          foundNext = true;
          }	
        }
        if (!foundNext) {
	  int saveIndex = channel->Index();
          if (messageLevel >= 2) {
            asprintf(&buf, "AutoSort: Deleting group '%s'", channel->Name());
            Skins.Message(mtStatus, buf, messageTime);    
            }
	  DeleteGroup(channel->Index());
	  channel = Channels.Get(saveIndex-1);
	  }
      }
    // Check Order
    //dsyslog("AutoSort: Checking order now");
    if (messageLevel >= 1) Skins.Message(mtStatus,"AutoSort: Checking Order of Group Delimiters", messageTime);
    group = AutoGroups.GetFirstByOrder();
    cAutoGroup *nextGroup = group;
    cChannel *channel = Channels.Get(group->GetChIndex());

    while ((nextGroup = AutoGroups.GetNextByOrder(group->OIdx()))){
      channel = Channels.Get(Channels.GetNextGroup(group->GetChIndex()));
      alarm(60);
      //dsyslog ("AutoSort: Have %s search %s found %s",group->Name(), nextGroup->Name(), channel->Name());
      if (strcmp(channel->Name(),nextGroup->Name()) == 0){
        group = nextGroup;
      } else {
        if (strcmp(Channels.Get(Channels.GetNextGroup(channel->Index()))->Name(),nextGroup->Name()) == 0){
	  //dsyslog("It's the overnext");
	  dsyslog("Moving Group '%s' to end",channel->Name());
	  if (messageLevel >= 2) {
            asprintf(&buf, "AutoSort: Moving '%s' to end", channel->Name());
            Skins.Message(mtStatus, buf, messageTime);    
            }
          //MoveGroupBehindGroup(nextGroup->GetChIndex(),group);	  
          MoveGroupToEnd(channel->Index());	            
	} else {
	  dsyslog("Moving Group '%s' behind '%s'",nextGroup->Name(),group->Name());
	  if (messageLevel >= 2) {
            asprintf(&buf, "AutoSort: Moving '%s' behind '%s'", nextGroup->Name(), group->Name());
            Skins.Message(mtStatus, buf, messageTime);    
            }
          MoveGroupBehindGroup(nextGroup->GetChIndex(),group);	  
          } // if overnext
	}
      } //while NextByOrder
     
  } //  if (!foundStart){
  if (messageLevel >= 1) Skins.Message(mtStatus,NULL,0);
}

void cNewAutoSort::MoveGroupBehindGroup (int ChIndex, cAutoGroup *group)
{
  cChannel *channel1 = Channels.Get(ChIndex);
  cChannel *channel2 = Channels.Get(Channels.GetNextGroup(ChIndex)-1);
  if (!channel2) channel2 = Channels.Last();
  int NextGroupIdx = Channels.GetNextGroup(group->GetChIndex());
  
  dsyslog("AutoSort: Moving Group '%s' behind '%s'",channel1->Name(),group->Name());
  if (channel1->Index() < AutoGroups.GetFirstByOrder()->GetChIndex() ){
    dsyslog("AutoSort: -ERROR- ::MoveGroupBehindGroup() tries working before Auto-Sort-Mark  -ABORTED-");
    return;
    }	    
  
  cAutoGroup *dummyGroup = new cAutoGroup;
  dummyGroup->SetName("Dummy");
  InsGroup(dummyGroup,NextGroupIdx);
  ChIndex++;
  
  cAutoGroup *dummyGroup2 = new cAutoGroup;
  dummyGroup2->SetName("Dummy2");
  InsGroup(dummyGroup2,NextGroupIdx);
  ChIndex++;

  if ( ChIndex == Channels.Last()->Index() ) {
    //dsyslog(" group %s is last and empty",channel1->Name());
    MoveChannelToGroup(ChIndex, dummyGroup);    
    }
  else if ( ChIndex == channel2->Index() ) {
    //dsyslog(" group %s is inbetween and empty",channel1->Name());
    MoveChannelToGroup(ChIndex, dummyGroup);    
    }
  else {    
    //dsyslog(" group %s is inbetween and filled",channel1->Name());
    int i =  channel2->Index();
    // ToDo: Make a heavy speed-up here
    //while (strcmp(channel1->Name(),Channels.Get(i)->Name()) !=0 ) {
    while (Channels.Get(i)->GroupSep() == false) {
      //alarm(60);
      MoveChannelToGroup(i, dummyGroup);
      }
    MoveChannelToGroup(i, dummyGroup);
  } // if
      
  channel1 = Channels.Get(dummyGroup->GetChIndex());    
  Channels.Del(channel1);
  Channels.ReNumber();
  channel1 = Channels.Get(dummyGroup2->GetChIndex());    
  Channels.Del(channel1);
  Channels.ReNumber();
  Channels.SetModified();
}

void cNewAutoSort::InsGroup (cAutoGroup *group, int ChIndex)
{
  if (strcmp(group->Name(),"Dummy") && strcmp(group->Name(),"Dummy2")){
    dsyslog("AutoSort: Inserting Group '%s'", group->Name());
    }
  cChannel *NewGroup = new cChannel;
  cChannel *Before = Channels.Get(ChIndex);
  char *name;
  asprintf(&name,":%s",group->Name());
  NewGroup->Parse(name);
  Channels.Ins(NewGroup,Before);
  Channels.ReNumber();
  Channels.SetModified();
}

void cNewAutoSort::MoveGroupToEnd (int ChIndex)
{
  cChannel *channel1 = Channels.Get(ChIndex);
  cChannel *channel2 = Channels.Get(Channels.GetNextGroup(ChIndex)-1);
  dsyslog("AutoSort: Moving Group %s to end",channel1->Name());
  
  cAutoGroup *group = new cAutoGroup;
  group->SetName("Dummy");
  AddGroup(group);

  cAutoGroup *group2 = new cAutoGroup;
  group2->SetName("Dummy2");
  AddGroup(group2);
  
  int i =  channel2->Index();
  while (i != ChIndex -1)
    {
    MoveChannelToGroup(i, group);
    i--;
    }
    
  channel1 = Channels.Get(group->GetChIndex());    
  Channels.Del(channel1);
  Channels.ReNumber();
  channel1 = Channels.Get(group2->GetChIndex());    
  Channels.Del(channel1);
  Channels.ReNumber();
  Channels.SetModified();    
}

void cNewAutoSort::AddGroup (cAutoGroup *group)
{
  dsyslog("AutoSort: Creating Group '%s'", group->Name());
  cChannel *NewGroup = new cChannel;
  char *name;
  
  if (group->FixedPos() > 0) {
    asprintf(&name, ":@%d %s",group->FixedPos(),group->Name());
  } else {
    asprintf(&name,":%s",group->Name());
    }
  
  NewGroup->Parse(name);
  Channels.Add(NewGroup);
  Channels.ReNumber();
  Channels.SetModified(); 
}


void cNewAutoSort::Dump(int from, int to)
{
  cChannel *channel = Channels.Get(from);
  while ((channel) && (channel->Index() <= to)) {
    if (channel->GroupSep()) {
      dsyslog ("-Group- %d: --%s--", channel->Index(), channel->Name());
    } else {
      dsyslog ("Channel %d:   %s", channel->Index(), channel->Name());
      }
    channel = Channels.Next(channel);
    }
}


void cNewAutoSort::DeleteGroup (int ChIndex)
{
  //Delete Group means delete the delimiter and move channels to Unsorted
  cChannel *channel1 = Channels.Get(ChIndex);
  cChannel *channel2;
  dsyslog("AutoSort: Deleting Group '%s'",channel1->Name());
  
  if (Channels.GetNextGroup(ChIndex) > 0){
    channel2 = Channels.Get(Channels.GetNextGroup(ChIndex)-1);
    cAutoGroup *group = AutoGroups.GetByName("Unsorted");
    for (int i = channel2->Index(); i > ChIndex; i--)
      {
      MoveChannelToGroup(i, group);
      }
    } 
  Channels.Del(channel1);
  Channels.ReNumber();
  Channels.SetModified();  
}


void cNewAutoSort::SetTimeStamp (int ChIndex)
{
  // TimeStamp is in seconds
  // 1 hour   = 3600
  // 1 day    = 86400
  // 1 week   = 604800
  // 1 month  = 2592000
  // 3 months = 7776000
    cChannel *channel = Channels.Get(ChIndex);
    if (!strstr(channel->ShortName(),"ASTS")){
      char *buf = NULL;
      int stampTime = time(NULL);
      if (strlen(channel->ShortName()) < 1){
	asprintf(&buf,"%s ASTS-%d", KillSpecial(strdup(channel->Name())), stampTime);
      } else {
	asprintf(&buf,"%s ASTS-%d", KillSpecial(strdup(channel->ShortName())), stampTime);
	}	
      channel->SetName(channel->Name(), buf, channel->Provider());
    }
}

const char *cNewAutoSort::InGroup (int ChIndex)
{
  cChannel *channel = Channels.Get(Channels.GetPrevGroup(ChIndex));
  return channel->Name();
}

void cNewAutoSort::MoveChannelToGroup (int ChIndex, cAutoGroup *group)
{
  char *buf;
  cChannel *channel = Channels.Get(ChIndex);
  if (group->GetChIndex() == Channels.Last()->Index()){
    // Move channel behind last channel

    cAutoGroup *group2 = new cAutoGroup;
    group2->SetName("Dummy");
    AddGroup(group2);

    cChannel *ToChannel = Channels.Get(group->GetChIndex()+1);
    if (strcmp(group->Name(),"Dummy") != 0){
      dsyslog("AutoSort: move channel '%s' to group '%s'",channel->Name() ,group->Name());
      if (messageLevel >= 3) {
        asprintf(&buf, "AutoSort: Moving '%s' to '%s'", channel->Name(), group->Name());
        Skins.Message(mtStatus, buf, messageTime);    
        }
      }
    Channels.Move(channel, ToChannel);
    Channels.ReNumber();
    if (strcmp(InGroup(ToChannel->Index()), group->Name())!=0) {
      dsyslog ("ERROR: (MoveChannelToGroup1) Moved %s to %s instead of %s ", ToChannel->Name(), InGroup(ToChannel->Index()), group->Name());
      Dump(ToChannel->Index()-3,ToChannel->Index()+3);
      }

    cChannel *channel1 = Channels.Get(group2->GetChIndex());    
    //dsyslog("AutoSort: Deleting Group %s",channel1->Name());
    Channels.Del(channel1);
    Channels.ReNumber();
    Channels.SetModified();
  } else {
    // Move channel anywhere else
    cChannel *ToChannel;
    if (channel->Index() > group->GetChIndex()) {
      ToChannel = Channels.Get(group->GetChIndex()+1);
    } else {
      ToChannel = Channels.Get(group->GetChIndex());
      }
    if (strcmp(group->Name(),"Dummy") != 0){
      //dsyslog("move channel %d to %d (group %d)",channel->Index(), ToChannel->Index(), group->GetChIndex());
      dsyslog("AutoSort: move channel '%s' to group '%s'",channel->Name() ,group->Name());
      if (messageLevel >= 3) {
        asprintf(&buf, "AutoSort: Moving '%s' to '%s'", channel->Name(), group->Name());
        Skins.Message(mtStatus, buf, messageTime);    
        }
      }
    Channels.Move(channel, ToChannel);
    Channels.ReNumber();
    Channels.SetModified();
    //if (strcmp(InGroup(ToChannel->Index()), group->Name())!=0) {
      //dsyslog ("ERROR: (MoveChannelToGroup2) Moved %s to %s instead of %s ", ToChannel->Name(), InGroup(ToChannel->Index()), group->Name());
    if (strcmp(InGroup(channel->Index()), group->Name())!=0) { // XXX
      dsyslog ("ERROR: (MoveChannelToGroup2) Moved %s to %s instead of %s ", channel->Name(), InGroup(ToChannel->Index()), group->Name());
      Dump(ToChannel->Index()-3,ToChannel->Index()+3);
      }
  }
  // Sort here
  if (strcmp(group->Name(),"Dummy") != 0 ){
    Sort();
    }
}
