text: #include <cvd/videosource.h>
  #include <cvd/byte.h>
  #include <cvd/colourspaces.h>
  
  namespace CVD {
  	using namespace std;
  		
  	void tolower(std::string& s)
  	{
  		for (size_t i=0; i<s.length(); ++i)
  			s[i] = ::tolower(s[i]);
  	}
  
  		void tolower(char* s)
  	{
  		for (size_t i=0; s[i]; ++i)
  			s[i] = ::tolower(s[i]);
  	}
  
  	using std::tolower;
  	
  	//If the flag is specified at all, but no parameter is
  	//given, this implies true.
  	bool parseBoolFlag(std::string s)
  	{
  		tolower(s);
  		if(s == "" || s == "yes" || s == "true" || s=="1")
  			return 1;
  		else if(s == "no" || s == "false" || s=="0")
  			return 0;
  		else
  			throw ParseException("invalid interlaced/fields setting '"+s+"' (must be true/false or yes/no or 1/0)");
  	}
  
  	ImageRef parseImageRef(string s, bool is_size)
  	{
  		ImageRef size;
  		tolower(s);
  		if (is_size && s == "vga")
  			size = ImageRef(640,480);
  		else if (is_size && s == "qvga")
  			size = ImageRef(320,240);
  		else if (is_size && s == "pal") 
  			size = ImageRef(720,576);
  		else if (is_size && s == "ntsc")
  			size = ImageRef(720,480);
  		else if (is_size && s == "xga")
  			size = ImageRef(1024,768);
  		else {
  			std::istringstream size_in(s);
  			char x;
  			if (!(size_in >> size.x >> x >> size.y))
  			{
  				if(is_size)
  					throw ParseException("invalid image size specification: '"+ s +"'\n\t valid specs: qvga, vga, pal, ntsc, xga, <width>x<height>");
  				else
  					throw ParseException("invalid image position specification: '"+ s + "'\n\t valid specs: <width>x<height>");
  			}
  		}
  		return size;
  	}
  		
  	std::string escape(char c)
  	{
  		static std::string escaped[256];
  		static bool init = false;
  		if (!init) {
  			for (int i=0; i<256; ++i)
  				escaped[i] = std::string(1,static_cast<char>(i));
  			escaped[(unsigned char)'\a'] = "\\a";
  			escaped[(unsigned char)'\b'] = "\\b";
  			escaped[(unsigned char)'\f'] = "\\f";
  			escaped[(unsigned char)'\n'] = "\\n";
  			escaped[(unsigned char)'\r'] = "\\r";
  			escaped[(unsigned char)'\t'] = "\\t";
  			escaped[(unsigned char)'\v'] = "\\v";
  			escaped[(unsigned char)'\\'] = "\\\\";
  			escaped[(unsigned char)'\''] = "\\'";
  			escaped[(unsigned char)'\"'] = "\\\"";
  			init = true;
  		}
  		return escaped[(unsigned char)c];
  	}
  
  	std::string unescape(const std::string& s)
  	{
  		static const char mapping[][2] = {{'a','\a'},
  										  {'b','\b'},
  										  {'f','\f'},
  										  {'n','\n'},
  										  {'r','\r'},
  										  {'t','\t'},
  										  {'v','\v'},
  										  {'\\','\\'},
  										  {'\'','\''},
  										  {'"','"'},
  										  {0,0}};
  		std::string ret;
  		ret.reserve(s.length());
  		for (size_t i=0; i<s.length(); ++i) {
  			if (s[i] == '\\') {
  				if (i + 1 == s.length())
  					throw ParseException("illegal '\\' terminating literal");
  				char escape = s[i+1];
  				if (isdigit(escape)) {
  					if (i + 3 >= s.length() || (s[i+2] < '0' || s[i+2] > '7') || (s[i+3] < '0' || s[i+3] > '7'))
  						throw ParseException("partial octal character code; need three digits");
  					int code = (escape-'0')*64 + (s[i+2] - '0')*8 + s[i+3] - '0';
  					if (code > 255)
  						throw ParseException("invalid octal character code; must be in [000,377]");
  					ret += static_cast<char>(code);
  					i += 3;
  				} else if (escape == 'h') {
  					if (i+3 >= s.length() || !isxdigit(s[i+2]) || !isxdigit(s[i+3]))
  						throw ParseException("partial hex character code; need two hex digits");
  					int code = (isdigit(s[i+2]) ? (s[i+2]-'0') : (tolower(s[i+2])-'a' + 10))*16 +
  						(isdigit(s[i+3]) ? (s[i+3]-'0') : (tolower(s[i+3])-'a' + 10));
  					ret += static_cast<char>(code);
  					i += 3;
  				} else {
  					int j=0;
  					while (mapping[j][0] && mapping[j][0] != s[i+1])
  						++j;
  					if (!mapping[j][0])
  						throw ParseException("unknown escape sequence");
  					ret += mapping[j][1];
  					i += 1;
  				}
  			} else
  				ret += s[i];
  		}
  		return ret;
  	}
  
  	std::ostream& operator<<(std::ostream& out, const VideoSource& vs)
  	{
  		out << vs.protocol << ":";
  		if (!vs.options.empty()) {
  			out << "[ ";
  			for (VideoSource::option_list::const_iterator it = vs.options.begin(); it != vs.options.end(); ++it) {
  				out << it->first << "=" << it->second << ", ";
  			}
  			out << "]";
  		}
  		out << "//" << vs.identifier;
  		return out;
  	}
  
  	void match(std::istream& in, char c) {
  		if (in.peek() != c) {
  			std::ostringstream oss;
  			oss << "expected '" << c <<"', got '" << escape(in.peek()) << "'";
  			throw ParseException(oss.str());
  		}
  		in.ignore();
  	}
  
  	void skip_ws(std::istream& in) {
  		while (isspace(in.peek())) 
  			in.ignore();
  	}
  
  	std::string read_quoted_literal(std::istream& in)
  	{
  		std::string value;
  		match(in, '"');
  		while (in.peek() != '"') {
  			char c = in.get();
  			value += c;
  			if (c == '\\')
  				value += in.get();
  		}
  		match(in,'"');
  		return value;
  	}
  
  	std::string read_word(std::istream& in)
  	{
  		std::string word;
  		while (isalnum(in.peek()) || in.peek()=='_')
  			word += in.get();			
  		return word;
  	}
  
  	std::string read_path(std::istream& in)
  	{
  		std::string word;
  		while (isgraph(in.peek()))
  			word += in.get();
  		return word;
  	}
  
  
  
  	void parse(std::istream& in, VideoSource& vs)
  	{
  		std::string prot;
  		skip_ws(in);
  		while (isalnum(in.peek()))
  			prot += in.get();
  		if (prot.length() == 0) 
  			throw ParseException("protocol must not be empty");
  		vs.protocol = prot;
  		//std::cerr << "prot" << std::endl;
  		if (in.peek() != ':')
  			throw ParseException("expected ':' after protocol");
  		in.ignore();
  		if (in.peek() == '[') {
  			in.ignore();
  			while (true) {
  				skip_ws(in);
  				std::string name = read_word(in);
  				tolower(name);
  				//std::cerr << "opt: " << name << std::endl;
  				skip_ws(in);	
  			
  				if (in.peek() != '=')
  					vs.options.push_back(std::make_pair(name,""));
  				else {
  					in.ignore();
  					skip_ws(in);
  					if (in.peek() == '"') {
  						vs.options.push_back(std::make_pair(name,unescape(read_quoted_literal(in))));
  					} else {
  						vs.options.push_back(std::make_pair(name,read_word(in)));
  					}
  				}
  				skip_ws(in);
  				if (in.peek() != ',')
  					break;
  				in.ignore();
  			}
  			skip_ws(in);
  			match(in, ']');
  			//std::cerr << "options" << std::endl;
  		}
  		match(in, '/');
  		match(in, '/');
  		//std::cerr << "slashes" << std::endl;
  		if (in.peek() == '"') {
  			std::string id = read_quoted_literal(in);
  			vs.identifier = unescape(id);
  			//std::cerr << "quoted id" << std::endl;
  		} else {
  			std::string id = read_path(in);
  			vs.identifier = id;
  			//std::cerr << "id" << std::endl;
  		}
  	
  	}
  
  	void get_jpegstream_options(const VideoSource& vs, int& ra_frames)
  	{
  		ra_frames = 0;
  
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it) {		
  			if (it->first == "read_ahead") {
  				ra_frames = 50;
  				if (it->second.length())
  					ra_frames = atoi(it->second.c_str());
  			}
  			else
  				throw VideoSourceException("invalid option for files protocol: "+it->first +
  										   "\n\t valid options: read_ahead");
  		}
  	}
  
  	void get_deinterlace_options(const VideoSource& vs, DeinterlaceBufferFields::Fields& fields, bool& line_double)
  	{
  		
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it)
  		{		
  			if(it->first == "oddonly" && parseBoolFlag(it->second) == true)
  				fields = DeinterlaceBufferFields::OddOnly;
  			else if(it->first == "evenonly" && parseBoolFlag(it->second) == true)
  				fields = DeinterlaceBufferFields::EvenOnly;
  			else if(it->first == "oddeven" && parseBoolFlag(it->second) == true)
  				fields = DeinterlaceBufferFields::OddEven;
  			else if(it->first == "evenodd" && parseBoolFlag(it->second) == true)
  				fields = DeinterlaceBufferFields::EvenOdd;
  			else if(it->first == "double" && parseBoolFlag(it->second) == true)
  				line_double = true;
  			else
  				throw VideoSourceException("invalid option for files protocol: "+it->first +
  										   "\n\t valid options: oddonly, evenonly, oddeven, evenodd, double");
  		}
  	}
  
  	void get_colourspace_options(const VideoSource& vs, string& colourspace)
  	{
  		colourspace = "mono";
  
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it) {		
  			if (it->first == "from") {
  				colourspace = it->second;
  			}
  			else
  				throw VideoSourceException("invalid option for files protocol: "+it->first +
  										   "\n\t valid options: from");
  		}
  	}
  
  	void get_files_options(const VideoSource& vs, int& fps, int& ra_frames, VideoBufferFlags::OnEndOfBuffer& eob)
  	{
  		fps = 30;
  		ra_frames = 0;
  		eob = VideoBufferFlags::RepeatLastFrame;
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it) {		
  			if (it->first == "fps") {
  				fps = atoi(it->second.c_str());
  			} else if (it->first == "read_ahead") {
  				ra_frames = 50;
  				if (it->second.length())
  					ra_frames = atoi(it->second.c_str());
  			} else if (it->first == "on_end") {			
  				if (it->second == "loop") 
  					eob = VideoBufferFlags::Loop;
  				else if (it->second == "unset_pending")
  					eob = VideoBufferFlags::UnsetPending;
  				else if (it->second == "repeat_last")
  					eob = VideoBufferFlags::RepeatLastFrame;
  				else
  					throw VideoSourceException("invalid end-of-buffer behaviour: "+it->second+"\n\t valid options are repeat_last, unset_pending, loop");
  			} else
  				throw VideoSourceException("invalid option for files protocol: "+it->first +
  										   "\n\t valid options: read_ahead, on_end, fps");
  		}
  	}
  
  
  	void get_v4l1_options(const VideoSource& vs, ImageRef& size)
  	{
  		size = ImageRef(0,0);
  
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it) {
  			if (it->first == "size")
  				size = parseImageRef(it->second, true);
  			else
  				throw VideoSourceException("invalid option for 'v4l2' protocol: "+it->first+"\n\t valid options: size, input, interlaced, fields");
  		}
  	}
  
  
  	void get_v4l2_options(const VideoSource& vs, ImageRef& size, int& input, bool& interlaced, bool& verbose)
  	{
  		size = ImageRef(640,480);
  		input = -1;
  		interlaced = false;
  		verbose=0;
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it) {
  			if (it->first == "size")
  				size = parseImageRef(it->second, true);
  			else if (it->first == "input") {
  				input = atoi(it->second.c_str());
  			} else if (it->first == "verbose") {
  						verbose = parseBoolFlag(it->second);
  			} else if (it->first == "interlaced" || it->first == "fields") {
  						interlaced = parseBoolFlag(it->second);
  			} else
  				throw VideoSourceException("invalid option for 'v4l2' protocol: "+it->first+"\n\t valid options: size, input, interlaced, fields");
  		}
  	}
  
  
  
  	void get_file_options(const VideoSource& vs, int& ra_frames, VideoBufferFlags::OnEndOfBuffer& eob)
  	{
  		eob = VideoBufferFlags::RepeatLastFrame;
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it) {
  			if (it->first == "read_ahead") {
  				ra_frames = 50;
  				if (it->second.length())
  					ra_frames = atoi(it->second.c_str());
  			} else if (it->first == "on_end") {			
  				if (it->second == "loop") 
  					eob = VideoBufferFlags::Loop;
  				else if (it->second == "unset_pending")
  					eob = VideoBufferFlags::UnsetPending;
  				else if (it->second == "repeat_last")
  					eob = VideoBufferFlags::RepeatLastFrame;
  				else
  					throw VideoSourceException("invalid end-of-buffer behaviour: "+it->second+"\n\t valid options are repeat_last, unset_pending, loop");
  			} else
  				throw VideoSourceException("invalid option for 'file' protocol: "+it->first+"\n\t valid options: read_ahead, on_end, fps");
  		}
  	}
  
  
  	void get_dc1394_options(const VideoSource& vs, ImageRef& size, float& fps, ImageRef& offset, bool& verbose, bool& bus_reset, int& format7_mode)
  	{ 
  		size = offset = ImageRef(-1, -1);
  		fps = -1;
  		verbose = 0;
  		bus_reset = 0;
  		format7_mode = -1;
  
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it) {
  			if (it->first == "fps")
  				fps = atoi(it->second.c_str());
  			else if (it->first == "size")
  				size = parseImageRef(it->second, true);
  			else if (it->first == "offset")
  				offset = parseImageRef(it->second, false);
  			else if (it->first == "verbose")
  				verbose = parseBoolFlag(it->second);
  			else if (it->first == "reset")
  				bus_reset = parseBoolFlag(it->second);
  			else if(it->first == "mode" || it->first == "format7_mode" || it->first == "format7")
  				format7_mode = atoi(it->second.c_str());
  			else
  				throw VideoSourceException("invalid option for dc1394 protocol: "+it->first+"\n\t valid options: fps, size, offset, verbose");
  		}
     }
  	
  	void get_qt_options(const VideoSource & vs, ImageRef & size, bool & showsettings, bool & verbose){
  		size = ImageRef(640, 480);
  		showsettings = false;
  		verbose = false;
  		for (VideoSource::option_list::const_iterator it=vs.options.begin(); it != vs.options.end(); ++it) {
  			if (it->first == "size")
  				size = parseImageRef(it->second, true);
  			else if(it->first == "showsettings")
  				showsettings = parseBoolFlag(it->second);
  			else if(it->first == "verbose")
  				verbose = parseBoolFlag(it->second);
  			else
  				throw VideoSourceException("invalid option for 'qt' protocol: "+it->first+"\n\t valid options: size, showsettings");
  		}
  	}
  }
  