/*
 * videosystem.c: A plugin for the Video Disk Recorder
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include <vdr/plugin.h>
#include <vdr/device.h>
#include <vdr/thread.h>
#include <vdr/config.h>
#include <vdr/tools.h>
#include <sys/ioctl.h>
#include <linux/dvb/video.h>

#define DEV_DVB_VIDEO     "/dev/dvb/adapter%d/video%d"
#define UPDATE_INTERVALL  1000


static const char *VERSION        = "0.0.1";
static const char *DESCRIPTION    = "adopts the OSD-settings dependent on the video-system";
static const char *MAINMENUENTRY  = NULL; //"Videosystem";


struct tOsdSettings
{
	int top;
	int height;
};


class cUpdateOsd : public cThread
{
public:
	cUpdateOsd();
	~cUpdateOsd();
	
	void Exit(void);
	
protected:
	virtual void Action(void);
	
private:
	eVideoSystem GetVideoSystem(void);
	
	bool doexit;
	uint64 time;
	eVideoSystem curVidSys;
	bool initialized;
	tOsdSettings pal;
	tOsdSettings ntsc;
	
	// for the direct dvb-device-communication
	int dvbHandle;
	video_size_t videoSize;
};


class cPluginVideosystem : public cPlugin {
private:
	cUpdateOsd update;

public:
	cPluginVideosystem(void);
	virtual ~cPluginVideosystem();
	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 Initialize(void);
	virtual bool Start(void);
	virtual void Stop(void);
	virtual void Housekeeping(void);
	virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
	virtual cOsdObject *MainMenuAction(void);
	virtual cMenuSetupPage *SetupMenu(void);
	virtual bool SetupParse(const char *Name, const char *Value);
	virtual bool Service(const char *Id, void *Data = NULL);
	virtual const char **SVDRPHelpPages(void);
	virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
};


// =============================================================================


cUpdateOsd::cUpdateOsd() : cThread("Videosystem_UpdateOsd")
{
	doexit = 0;
	curVidSys = vsPAL;
	time = cTimeMs::Now();
}


cUpdateOsd::~cUpdateOsd()
{
	// Destructor
}


eVideoSystem cUpdateOsd::GetVideoSystem(void)
{
	eVideoSystem vs = vsPAL;
	cDevice *dev = cDevice::PrimaryDevice();
	if (dev)
	{
		char *dev = NULL;
		asprintf(&dev, DEV_DVB_VIDEO, cDevice::PrimaryDevice()->CardIndex(), 0);
		dvbHandle = open(dev, O_RDONLY | O_NONBLOCK);
		free(dev);
		
		if (ioctl(dvbHandle, VIDEO_GET_SIZE, &videoSize) == 0)
		{
			vs = videoSize.h == 240 || videoSize.h == 480 ? vsNTSC : vsPAL;
		}
		
		if (dvbHandle != -1)
		{
			close(dvbHandle);
			dvbHandle = -1;
		}
	}
	
	return vs;
}


void cUpdateOsd::Action(void)
{
	do
	{
		if (cTimeMs::Now() < time + UPDATE_INTERVALL)
		{
			cCondWait::SleepMs(UPDATE_INTERVALL);
		}
		else
		{
			time = cTimeMs::Now();
			eVideoSystem vs = GetVideoSystem();
			
			if (curVidSys == vsPAL && (pal.top != Setup.OSDTop || pal.height != Setup.OSDHeight))
			{
				// assume the default is PAL and get the default settings
				curVidSys = vsPAL;
				pal.top = Setup.OSDTop;
				pal.height = Setup.OSDHeight;
				
				// calculate the NTSC settings
				int ntscHeight = (int)((double)pal.height / 576. * 480.);
				// obey the VDR-limits
				if (ntscHeight < MINOSDHEIGHT) ntscHeight = MINOSDHEIGHT;
				ntsc.height = ntscHeight;
				ntsc.top = (480 - ntscHeight) / 2;
			}
			
			if (curVidSys != vs)
			{
				if (vs == vsPAL)
				{
					Setup.OSDTop = pal.top;
					Setup.OSDHeight = pal.height;
				}
				else
				{
					Setup.OSDTop = ntsc.top;
					Setup.OSDHeight = ntsc.height;
				}
				
				curVidSys = vs;
			}
		}
	}
	while (!doexit);
	
	usleep(1);
}


void cUpdateOsd::Exit(void)
{
	doexit = 1;
	
	Setup.OSDTop = pal.top;
	Setup.OSDHeight = pal.height;
	
	Cancel(3);
}


// =============================================================================


cPluginVideosystem::cPluginVideosystem(void)
{
  // Initialize any member variables here.
  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
}

cPluginVideosystem::~cPluginVideosystem()
{
  // Clean up after yourself!
}

const char *cPluginVideosystem::CommandLineHelp(void)
{
  // Return a string that describes all known command line options.
  return NULL;
}

bool cPluginVideosystem::ProcessArgs(int argc, char *argv[])
{
  // Implement command line argument processing here if applicable.
  return true;
}

bool cPluginVideosystem::Initialize(void)
{
	// Initialize any background activities the plugin shall perform.
	
	update.Start();
	
	return true;
}

bool cPluginVideosystem::Start(void)
{
  // Start any background activities the plugin shall perform.
  return true;
}

void cPluginVideosystem::Stop(void)
{
	// Stop any background activities the plugin shall perform.
	
	update.Exit();
}

void cPluginVideosystem::Housekeeping(void)
{
  // Perform any cleanup or other regular tasks.
}

cOsdObject *cPluginVideosystem::MainMenuAction(void)
{
	// Perform the action when selected from the main VDR menu.
	return NULL;
}

cMenuSetupPage *cPluginVideosystem::SetupMenu(void)
{
  // Return a setup menu in case the plugin supports one.
  return NULL;
}

bool cPluginVideosystem::SetupParse(const char *Name, const char *Value)
{
  // Parse your own setup parameters and store their values.
  return false;
}

bool cPluginVideosystem::Service(const char *Id, void *Data)
{
  // Handle custom service requests from other plugins
  return false;
}

const char **cPluginVideosystem::SVDRPHelpPages(void)
{
  // Return help text for SVDRP commands this plugin implements
  return NULL;
}

cString cPluginVideosystem::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
  // Process SVDRP commands this plugin implements
  return NULL;
}

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