#ifndef __COMMANDS_DVDSWITCH_H
#define __COMMANDS_DVDSWITCH_H

#include <vdr/plugin.h>
#include "menu.h"
#include "imagelist.h"
#include "setup.h"
#include <stdlib.h>
#include <string>
#include <fstream>
#include <iostream>

enum eCMDs
{
  cmdNone,
  cmdDirManage,
  cmdDVDopen,
  cmdDVDclose,
  cmdImgPlay,
  cmdImgRename,
  cmdImgMove,
  cmdImgDelete,
  cmdImgBurn,
  cmdImgRead,
  cmdCommands,
  cmdDeleteCache
};

enum eCMDState
{
  csNone,
  csDirNew,
  csDirEdit,
  csDirMove,
  csDirDel
};


class cCMD
{
  public:
    static eOSState Play(cMainMenuItem *item = NULL);
    static eOSState Eject(bool close = false);
};


class cCMDMenu : public cOsdMenu
{
  private:
    int ImgType;
    char Dir[MaxFileName];
    char File[MaxFileName];
    cMainMenuItem *iItem;
    cMainMenu *OsdObject;
  public:
    cCMDMenu(cMainMenuItem *item, cMainMenu *osdobject);
    void DvdRead();
    virtual eOSState ProcessKey(eKeys Key);
};

class cCMDDir : public cOsdMenu, private cDirHandlingOpt
{
  private:
    eCMDState State;
    char Dir[MaxFileName];
    cMainMenu *OsdObject;
    bool Select;
    char *Buffer;

    void Build(char *dir = NULL);
    void SetDir(char *value = NULL)
    {
      DEBUG (" cCMDDir %s ", value); 
      if(value)
        strn0cpy(Dir, value, MaxFileName);
      else
        strcpy(Dir, "\0");
    }

    eOSState New(void);
    eOSState New(eKeys Key);
    eOSState Edit(cMainMenuItem *mItem);
    eOSState Edit(eKeys Key);
  public:
    cCMDDir(cMainMenu *osdobject = NULL, bool select = false, char *buffer = NULL);

    virtual eOSState ProcessKey(eKeys Key);
    void SetHelp(void);
};

class cCMDMove : public cOsdMenu, private cDirHandlingOpt
{
  private:
    char *File;
    cMainMenu *OsdObject;
    bool Dir;
    bool Direct;

    void Build(char *dir = NULL);
  public:
    cCMDMove(char *file, cMainMenu *osdobject, bool dir = true, bool direct = false);
    ~cCMDMove(void) { free(File); }

    virtual eOSState ProcessKey(eKeys Key);
    void SetHelp(void);
};

class cCMDImage
{
  private:
    char *File;
    cMainMenu *OsdObject;
  public:
    char NewFile[MaxFileName];
    int tmpHideTypeCol;
    //int tmpHideImgSize

    cCMDImage(cMainMenu *osdobject = NULL);
    ~cCMDImage(void);

    char* Rename(char *file = NULL);
    eOSState Delete(char *file = NULL);
    eOSState Burn(char *file);
};

class cCMDImageRead : public cOsdMenu
{
  private:
    char File[MaxFileName];
    char Dir[MaxFileName];
    char ImgTypeTxt[MaxFileName];
    int ImgType;
  public:
    cCMDImageRead(void);
    ~cCMDImageRead(void);

    //void SetHelp(void);
    //virtual eOSState ProcessKey(eKeys Key);
};

/* Thread to read the output of vobcopy and growisofs 
 * and sends the percent completed to bgprocess
 * */
enum Mode{ ReadMode, BurnMode};
using namespace std;
class cStatusThread : public cThread
{
 private:
	 int mode; // ReadMode / BurnMode
	 string logFileName; // file to monitor
	 string thread_desc; // what the thread is doing
	 time_t startTime;
         string PluginName; 
 public:
	  cStatusThread(int m, string filename, string desc)
	  {
	   mode = (m==BurnMode?BurnMode:ReadMode);
	   logFileName = filename;
	   thread_desc = desc;
	   PluginName = tr("Rip a DVD");
	  }
	  
	  inline void NotifyBgProcess(std::string desc, int percent_done)
	   {
	      struct {std::string name; std::string desc; time_t t; float percent; } AProcess;
	
	      AProcess.name = PluginName;
	      AProcess.t = startTime;
	      AProcess.desc = desc;
	      AProcess.percent = percent_done;
	
	      cPlugin *Plugin = cPluginManager::GetPlugin("bgprocess");
	      if (Plugin)
	          Plugin->Service("bgprocess-data", &AProcess);
	  }
 
	  void StopStatusThread(int waitTime)
	  { 
	   NotifyBgProcess(thread_desc, 110); // thread done!
	   Cancel(waitTime);
	  }
 protected:
	 virtual void Action()
	 {
	   startTime = time(NULL);
           int percentage_done=0;
	   while(Running())
	    {
	      switch(mode)
	        {
	          case ReadMode: percentage_done = StatusRead(); break;
	          case BurnMode: percentage_done = StatusBurn(); break;
	        }
	    DEBUG(" percent %d mode%d\n",percentage_done, mode);
	    if (percentage_done > 100) StopStatusThread(0);
	    else
	    NotifyBgProcess(thread_desc,percentage_done);

	    sleep(2);
	    }
	 }


         int StatusBurn() 
	 { 
	     float percentage_done = 0;
	     string line;
	     ifstream ifile;

	     ifile.open(logFileName.c_str(), ios::in);
             while(ifile.good())
	       {
	         getline(ifile,line);

	         if (line == "DONE") return 1000;  // over

	         if (line.find("done, estimate finish") != string::npos)
	              sscanf(line.c_str()," %f", &percentage_done);
	       }
	       return (int) percentage_done;
	 } 

	 int StatusRead() // reads the logFile till the end and returns the status
	 {
          long long diskSize;
	  ifstream ifile;
	  string line, part_str;
	  int count = 0;
	  long long total_size_written = 0;
	  long long last_written_size = 0;
	  int last_pos = 0;
	  int curr_pos = 0;
	  unsigned int pos_tmp;
	  count = 0;
	
	  ifile.open (logFileName.c_str(), ios::in);
	  while (ifile.good ())
	  {
	    getline (ifile, line);

	     if (line == "DONE") return 1000;  // over
	
	    pos_tmp = line.find ("Vobs size: ");
	    if (pos_tmp != string::npos)
	    {
	      sscanf (line.substr (pos_tmp + 11).c_str (), " %lld", &diskSize);
	      diskSize *= 1024*1024; // Vobcopy reports disk size in MB
	    } // END if "Vobs size"
	
	
	    if (line.find ("written") != string::npos)
	    {
	      long long total_size, done_size;
	      char size_str[5], tmp_str[32];
	      while (curr_pos != string::npos)
	      {
	        curr_pos = line.find (0x0d, last_pos + 1);      // '^M'
	        if (curr_pos == string::npos)
	          break;
	        part_str = line.substr (last_pos, curr_pos - 1);
	        if (!sscanf (part_str.c_str (), " %lld %s %s %lld %s %s", &done_size, size_str, tmp_str, &total_size, size_str, tmp_str)) break;                // error or EOF
	        {
	          if (size_str[0] == 'k' || size_str[0] == 'K') // asdiskSizee KB
	            done_size *= 1024;
	          if (size_str[0] == 'm' || size_str[0] == 'M') // asdiskSizee MB
	            done_size *= 1024 * 1024;
	          if (size_str[0] == 'g' || size_str[0] == 'G') // asdiskSizee GB
	            done_size *= 1024 * 1024 * 1024;
	
	          last_written_size = done_size;
	        }
	        last_pos = curr_pos;
	      }
	      last_pos = curr_pos = 0;
	    } // END if written
	
	  total_size_written += last_written_size;
	  last_written_size = 0;
	  } // END while !eof
	  ifile.close();
	  //cout <<  total_size_written << " bytes written  " << total_size_written * 100.0 / diskSize << "% done" << endl;
         
          return  (int)(total_size_written * 100.0 / diskSize);

	 }
};

class cCMDImageReadThread : public cThread
{
  private:
    char *File;
    char *Dir;
    eFileInfo FileType;
  protected:
    virtual void Action(void)
    {
      DEBUG("ReadThread gestartet");
      if(File && Dir && FileType != tNone)
      {
        DEBUG("Aktion wird ausgef�hrt");
        char *cmd = NULL;
        char *output = NULL;
        char *mountpoint = NULL;
        int step = 0;
        cTokenizer *token = NULL;
        char buffer[MaxFileName];
        if (realpath(DVDSwitchSetup.DVDLinkOrg, buffer)== NULL) buffer[0] = '\0';

        while(!mountpoint && step < 2)
        {
          step++;
          asprintf(&cmd,
                   "cat /etc/fstab | grep -e \"%s\" | grep -e \"^[^#]\"",
                   step == 1 ? DVDSwitchSetup.DVDLinkOrg : buffer);
          DEBUG("Commando: %s", cmd);

          FILE *p = popen(cmd, "r");
          if(p)
          {
#if VDRVERSNUM >= 10318
            cReadLine rl;
            output = rl.Read(p);
#else
            output = readline(p);
#endif
            pclose(p);
          }
          token = new cTokenizer(output, " ");
          if(token->Count() > 1)
            mountpoint = strdup(stripspace(token->GetToken(2)));
          DELETENULL(token);
        }
        FREENULL(cmd);
        asprintf(&cmd,
                  "'%s' '%s' '%s' '%s' '%s' '%s'",
                  DVDSwitchSetup.DVDReadScript,
                  Dir, // /media/hd/movies 
                  File, // leer 
                  buffer,
                  (mountpoint==NULL) ? " ":mountpoint,
                  (FileType == tFile) ? "IMAGE" : "DIR");
        printf("\nAufruf: %s\n", cmd);
	// Start thread here
        int rc = SystemExec(cmd);
	cStatusThread* read_status = new cStatusThread(ReadMode,"/tmp/vobcopy.bla",tr("copying DVD"));
	read_status->Start();
        printf("\nAufruf: %s DONE with %d\n", cmd, rc);
        DEBUG("Rueckgabe Aufruf: %i", rc);
        FREENULL(cmd);
        FREENULL(mountpoint);
      }
      delete(this);
    };
  public:
    cCMDImageReadThread(char *file, char *dir, int imgtype)
    {
      DEBUG("ReadThread created");
      File = NULL;
      Dir = NULL;
      FileType = tNone;

      DEBUG ( "  File: %s dir : %s  imgType %d " , file, dir ,imgtype);
      cImageListItem *item = ImageList.Get(imgtype);
      if(imgtype >= 0 && file && dir)
      {
        if(item->GetFType() == tFile)
        {
          asprintf(&File, "%s%s", file, item->GetValue());
          DEBUG ( "  File: %s itemValue %s  ", file, item->GetValue());
        }
        else
        {
          File = strdup(file);
          DEBUG ( "  File: %s itemValue %s  ", file);
        }
        Dir = strdup(dir);
        FileType = item->GetFType();
      }
    }
    ~cCMDImageReadThread(void)
    {
      free(File);
      free(Dir);
    }
};

class cCMDImageBurnThread : public cThread
{
  private:
    char *File;
    eFileInfo FileType;
  protected:
    virtual void Action(void)
    {
      DEBUG("BurnThread gestartet");
      if(File && FileType != tNone)
      {
        DEBUG("Aktion wird ausgef�hrt");
        char *cmd;
        asprintf(&cmd,
                  "'%s' '%s' '%s'",
                  DVDSwitchSetup.DVDWriteScript,
                  File,
                  FileType == tFile ? "IMAGE" : "DIR");
        DEBUG("Aufruf: %s", &cmd);
        int rc = SystemExec(cmd);
	cStatusThread* burn_status = new cStatusThread(BurnMode,"/var/log/dvdswitch_burn.log","Writing DVD");
	burn_status->Start();
        DEBUG("R�ckgabe Aufruf: %i", rc);
        FREENULL(cmd);
      }
        delete(this);
    };
  public:
    cCMDImageBurnThread(char *file, eFileInfo type)
    {
      DEBUG("BurnThread created");
      File = NULL;
      FileType = tNone;

      if(file && type != tNone)
      {
        File = strdup(file);
        FileType = type;
      }
    }
    ~cCMDImageBurnThread(void) { free(File); }
};

#endif // __COMMANDS_DVDSWITCH_H
