/*
 * vdrcd.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id: vdrcd.c,v 1.3 2004/12/29 00:08:20 lordjaxom Exp $
 */

#include "vdrcd.h"
#include "i18n.h"
#include "setup.h"
#include "status.h"

#include <vdr/plugin.h>
#include <vdr/menu.h>

#include <sys/wait.h>
#include <getopt.h>
#include <stdlib.h>

extern const char *VideoDirectory;

static const char *VERSION        = "0.0.10.1-Image";
static const char *DESCRIPTION    = "Plays identified Media Discs (for autofs)";
static const char *MAINMENUENTRY  = "Play Disc";

enum eTypeMethod {
	State,
	Find
};

struct cTypelist {
  const char *Object;
  const char *PlugIn;
  bool NeedsDir;
  eTypeMethod Method;
	bool NeedsUmount;
};

#define VDRSUBDIR "2002-01-01.01.01.99.99.rec"
#define FINDCMD   "find %s -follow -type f -name '%s' 2> /dev/null"

#define trP(x,p) I18nTranslate(x, Typelist[p].PlugIn)

static struct cTypelist Typelist[] = {
       { "VIDEO_TS",   "dvd",     false, State, true  },
       { "video_ts",   "dvd",     false, State, true  },
       { "vcd",        "vcd",     false, State, true  },
       { "svcd",       "vcd",     false, State, true  },
       { "photo_cd",   "pcd",     false, State, true  },
       { "001.vdr",    NULL,      true,  State, false },
       { "001.vdr",    NULL,      false, Find,  false },
       { "*.mp3",      "mp3",     false, Find,  false },
       { "*.ogg",      "mp3",     false, Find,  false },
       { "*.wav",      "mp3",     false, Find,  false },
       { "*.jpg",      "image",   false, Find,  false },
       { "*.jpeg",     "image",   false, Find,  false },
       { "*.JPG",      "image",   false, Find,  false },
       { "*.JPEG",     "image",   false, Find,  false },
       { "*.GIF",      "image",   false, Find,  false },
       { "*.PNG",      "image",   false, Find,  false },
       { "*.gif",      "image",   false, Find,  false },
       { "*.png",      "image",   false, Find,  false },
       { "*.avi",      "mplayer", false, Find,  false },
       { "*.avi-????", "mplayer", false, Find,  false },
	   { "*.mpg",      "mplayer", false, Find,  false },
	   { "*.mpeg",     "mplayer", false, Find,  false },
	   { "*.MPG",	   "mplayer", false, Find,  false },
           { "*.MPEG",     "mplayer", false, Find,  false },
       { NULL }
     };


class cPluginVdrcd : public cPlugin {
private:
  char **Directories;
	uint DirCount;
	char *MountScript;
  const char *tmpcdrom;

	cVdrcdStatus *status;

protected:
  int TestType(const char *dir, const char *file);

public:
  cPluginVdrcd(void);
  virtual ~cPluginVdrcd();
  virtual const char *Version(void) { return VERSION; }
  virtual const char *Description(void) { return DESCRIPTION; }
  virtual const char *CommandLineHelp(void);
  virtual bool ProcessArgs(int argc, char *argv[]);
  virtual bool Start(void);
  virtual void Housekeeping(void);
  virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); }
  virtual cOsdObject *MainMenuAction(void);
  virtual cMenuSetupPage *SetupMenu(void);
  virtual bool SetupParse(const char *Name, const char *Value);
  };

cPluginVdrcd::cPluginVdrcd(void)
{
#ifdef XEATRE
	DirCount = 2;
	Directories = new char*[2];
	Directories[0] = "/mnt/cdfs";
	Directories[1] = "/mnt/dvd";
#else
	DirCount = 0;
	Directories = NULL;
#endif
	MountScript = NULL;
  tmpcdrom = NULL;
}

cPluginVdrcd::~cPluginVdrcd()
{
  char path[1024];
  if(tmpcdrom) {
    sprintf(path,"%s/cdrom/%s",tmpcdrom,VDRSUBDIR);
    unlink(path);
    sprintf(path,"%s/cdrom",tmpcdrom);
    rmdir(path);
    rmdir(tmpcdrom);
  }

	free(MountScript);
	delete[] Directories;

	delete status;
}

const char *cPluginVdrcd::CommandLineHelp(void)
{
	return "  -m MountScript, --mount=MountScript    mount.sh script\n"
		"     [mount.sh]\n"
		"  -c CdromDir,    --cdDir=CdromDir       First or additional CD-ROM [/cdrom]\n";
}

bool cPluginVdrcd::ProcessArgs(int argc, char *argv[])
{
  static struct option long_options[] = {
       { "cdDir",       required_argument, NULL, 'c' },
			 { "mount",       required_argument, NULL, 'm' },
       { NULL }
     };

  int c;
	char **p;
  while ((c = getopt_long(argc, argv, "m:c:", long_options, NULL)) != -1) {
		switch (c) {
		case 'c': 
			p = new char*[DirCount + 1];
			memcpy(p, Directories, sizeof(char*) * DirCount);
			p[DirCount] = optarg;
			++DirCount;
			delete[] Directories;
			Directories = p;
			break;

		case 'm': 
			MountScript = strdup(optarg);
			break;

		default:  
			return false;
		}
	}
  return true;
}

bool cPluginVdrcd::Start(void) {
	RegisterI18n(Phrases);

	if (MountScript == NULL) 
		MountScript = "mount.sh";

	if (DirCount == 0) {
		Directories = new char*[++DirCount];
		Directories[0] = "/cdrom";
	}

	status = new cVdrcdStatus;

  return true;
}

void cPluginVdrcd::Housekeeping(void) {
}

int cPluginVdrcd::TestType(const char *dir, const char *file) {
  int cnt=0;
  char *cmd = NULL;
  asprintf(&cmd, FINDCMD, dir, file);
  FILE *p = popen(cmd, "r");
  if (p) {
    char *s;
#if VDRVERSNUM >= 10318
	cReadLine r;
    while ((s = r.Read(p)) != NULL) {
#else
    while ((s = readline(p)) != NULL) {
#endif
    	if(strcmp(s,"")!=0) cnt++;
    }
  }
	fclose(p);
  free(cmd);
  return cnt;
}

cOsdObject *cPluginVdrcd::MainMenuAction(void) {
  struct stat buf;
	uint dir, type = 0;
	bool found = false;
	char *cmd, *path;

#if VDRVERSNUM < 10307
  Interface->Status(tr("Identifying Disc"));
  Interface->Flush();
#else
	Skins.Message(mtStatus, tr("Identifying Disc"));
	Skins.Flush();
#endif

	for (dir = 0; dir < DirCount; ++dir) {
		int ret;

		asprintf(&cmd, "%s status %s", MountScript, Directories[dir]);
		ret = system(cmd);

		if (WEXITSTATUS(ret) == 1) {
			asprintf(&cmd, "%s %s %s", MountScript, "mount", Directories[dir]);
			ret = system(cmd);
		}

		if (WEXITSTATUS(ret) == 0) {
			for (type = 0; Typelist[type].Object != NULL; ++type) {
				switch (Typelist[type].Method) {
				case State:
					asprintf(&path, "%s/%s", Directories[dir], Typelist[type].Object);
					if (stat(path, &buf) == 0)
						found = true;
					free(path);
					break;

				case Find:
					if (TestType(Directories[dir], Typelist[type].Object))
						found = true;
					break;
				}

				if (found)
					break;
			}
		}
		free(cmd);
	
		if (!found || Typelist[type].NeedsUmount) {
			asprintf(&cmd, "%s %s %s", MountScript, "unmount", Directories[dir]);
			system(cmd);
			free(cmd);
		}

		if (found)
			break;
	}

#if VDRVERSNUM >= 10307
	Skins.Message(mtStatus, NULL);
	Skins.Flush();
#endif

	if (!found) {
#if VDRVERSNUM < 10307
		Interface->Error(tr("Couldn't identify disc!"));
#else
		Skins.Message(mtError, tr("Couldn't identify disc!"));
#endif
		return NULL;
	}

	if (Typelist[type].PlugIn == NULL) {
		const char *oldvideodir = VideoDirectory;

		if (Typelist[type].NeedsDir) {
			if (tmpcdrom == NULL) {
				tmpcdrom = tmpnam(NULL);
				mkdir(tmpcdrom,0777);
				sprintf(path, "%s/cdrom", tmpcdrom);
				mkdir(path, 0777);
				asprintf(&path, "%s/cdrom/%s", tmpcdrom, VDRSUBDIR);
				symlink(Directories[dir], path);
				free(path);
			}
			VideoDirectory = tmpcdrom;
		} else
			VideoDirectory = Directories[dir];

		DPRINT("open recmenu in %s\n", VideoDirectory);

#if VDRVERSNUM >= 10311
		Recordings.Load();
#endif
		cOsdMenu *menu = new cMenuRecordings();
		VideoDirectory = oldvideodir;
		status->NumClears() = 3;

		return menu;
	}

	printf("gettingplugin%s\n", Typelist[type].PlugIn);
	cPlugin *p = cPluginManager::GetPlugin(Typelist[type].PlugIn);
	cOsdObject *osd = NULL;
	if (p) {
		osd = p->MainMenuAction();

		if (strcmp(Typelist[type].PlugIn, "vcd") == 0 && VdrcdSetup.AutoStartVCD) {
			const char *last = status->Current();

			osd->ProcessKey(kDown);
			if (status->Current() != last) {
				// VCD has more than one track, so leave the menu open
				osd->ProcessKey(kUp);
			} else {
				// Autostart CD and destroy menu
				osd->ProcessKey(kOk);

				delete osd;
				osd = NULL;
			}
		} 

			 else if (strcmp(Typelist[type].PlugIn, "image") == 0) {
                        // Switch to "source" mode
                        osd->ProcessKey(kYellow);
                        // Move to first entry
                        const char *actual = status->Current();
                        osd->ProcessKey(kUp);
                        while (actual != status->Current()) {
                                osd->ProcessKey(kUp);
                                actual = status->Current();
                        }
                        // Search for /cdrom-entry
                        bool imagesource = true;
                        while (strstr(actual,Directories[dir]) == NULL) {
                                osd->ProcessKey(kDown);
                                if (actual == status->Current()) {
                                        imagesource = false;
                                        break;
                                } else {
                                        actual = status->Current();
                                }
                        }

			
			 if (imagesource) {
                                osd->ProcessKey(kOk);

                                const char *last = status->Current();
                                osd->ProcessKey(kDown);
                                if (status->Current() != last) {
                                        // CD has more than one image-file, so leave the menu open
                                        osd->ProcessKey(kRed);
					delete osd;
					osd = NULL;
                                } else {
                                        // Autostart CD and destroy menu
                                        osd->ProcessKey(kOk);

                                        delete osd;
                                        osd = NULL;
                                }
                        } else {
                                delete osd;
                                osd = NULL;
#if VDRVERSNUM < 10307
                                Interface->Error(tr("Drive not present in imagesources.conf!"));
#else
                                Skins.Message(mtError, tr("Drive not present in imagesources.conf!"));
#endif
                        }

			

		}

			else if (strcmp(Typelist[type].PlugIn, "mplayer") == 0) {
			// Switch to "source" mode
			osd->ProcessKey(kYellow);
			// Move to first entry
			const char *actual = status->Current();
			osd->ProcessKey(kUp);
			while (actual != status->Current()) {
				osd->ProcessKey(kUp);
				actual = status->Current();
			}
			// Search for /cdrom-entry
			bool mplayersource = true;
			while (strstr(actual,Directories[dir]) == NULL) {
				osd->ProcessKey(kDown);
				if (actual == status->Current()) {
					mplayersource = false;
					break;
				} else {
					actual = status->Current();
				}
			}
			if (mplayersource) {
				osd->ProcessKey(kOk);

				const char *last = status->Current();
				osd->ProcessKey(kDown);
				if (status->Current() != last) {
					// CD has more than one avi-file, so leave the menu open
					osd->ProcessKey(kUp);
				} else {
					// Autostart CD and destroy menu
					osd->ProcessKey(kOk);

					delete osd;
					osd = NULL;
				}
			} else {
				delete osd;
				osd = NULL;
#if VDRVERSNUM < 10307
				Interface->Error(tr("Drive not present in mplayersources.conf!"));
#else
				Skins.Message(mtError, tr("Drive not present in mplayersources.conf!"));
#endif
			}
		} else if (strcmp(Typelist[type].PlugIn, "mp3") == 0) {
			const char *wanted = trP("MP3", type);

			// Switch to Browse Mode if not already done...
			if (strncmp(status->Title(), wanted, strlen(wanted)) != 0)
				osd->ProcessKey(kBack);

			// Now switch into "source" mode
			osd->ProcessKey(kGreen);

			const char *last = NULL;
			while (last != status->Current()) {
				if (strcmp(status->Current() + strlen(status->Current()) 
						- strlen(Directories[dir]), Directories[dir]) == 0) {

					osd->ProcessKey(kRed);    // Select it
					osd->ProcessKey(kYellow); // Change to browse mode again

					if (VdrcdSetup.AutoStartMP3)
						osd->ProcessKey(kYellow); // Instant Playback
					
					break;
				}

				last = status->Current();
				osd->ProcessKey(kDown);
			}

			delete osd;
			osd = NULL;

			if (last == status->Current())
#if VDRVERSNUM < 10307
				Interface->Error(tr("Drive not present in mp3sources.conf!"));
#else
				Skins.Message(mtError, tr("Drive not present in mp3sources.conf!"));
#endif

		}
	} else {
#if VDRVERSNUM < 10307
		Interface->Error(tr("Missing appropriate PlugIn!"));
#else
		Skins.Message(mtError, tr("Missing appropriate PlugIn!"));
#endif

		if (!Typelist[type].NeedsUmount) { // Unmount again if nothing found
			asprintf(&cmd, "%s %s %s", MountScript, "unmount", Directories[dir]);
			system(cmd);
			free(cmd);
		}
	}
		
	return osd;
}

cMenuSetupPage *cPluginVdrcd::SetupMenu(void) {
  return new cVdrcdMenuSetupPage;
}

bool cPluginVdrcd::SetupParse(const char *Name, const char *Value) {
  return VdrcdSetup.SetupParse(Name, Value);
}

VDRPLUGINCREATOR(cPluginVdrcd); // Don't touch this!
