using System;
namespace hcalc {
	/// <summary>
	/// Simple parser for text input. 
	/// input = string
	/// parse()
	/// peek() determines what the line is about
	///			Convex, Hid, Grid, 
	/// advance()	(peek does not advance)
	/// rewind()
	/// 
	/// </summary>
	public class CalcParser {
		public enum Type {
			Empty,
			Convex,
			Hid,
			Grid,
			Unknown
		}
		public enum Error {
			Ok,
			errOnlyOne,
			errIllegalNumber,
			errFormat,
			errEol,
			errUnkown
		}
		private Int64 _i1, _i2;
		private string _spec;
		private char[] _delims;
		private string[] _tokens;
		private int _ct;
		private Error _error;
		private void init(){
			_delims = " \t\n\r,".ToCharArray();
		}
		private string __prematureeol = "Not enough arguments";
		private string __onlyOne = "Only one number given";
		public string errmsg(){
			switch(_error){
				case Error.Ok: return "Ok" ;
				case Error.errOnlyOne: return __onlyOne;
				case Error.errIllegalNumber: return "Illegal number format"; 
				case Error.errFormat: return "Format must be XXX or YYYY"; 
				case Error.errUnkown: return "Unkown error(1). Call George";
				case Error.errEol: return this.__prematureeol;
			}
			return "Unknown error (3). Call George";
		}
		public CalcParser() {
			//
			// TODO: Add constructor logic here
			//
			init();
			input = null;
		}
		/* *******************process tokens******************************/
		private void advance(){
			_ct++;
		}
		public  bool isok {
			get {
				return _error == Error.Ok;
			}
		}
		/// <summary>
		/// True, if there are any tokens left to parse. ismore() skips over white spaces
		/// </summary>
		protected bool ismore {
			get {
				while( _ct < _tokens.Length && _tokens[_ct].Length == 0){
					_ct++;
				}
				return _ct < _tokens.Length;
			}
		}
		private int getint(int skip){
			int res;
			if (!ismore){
				_error = Error.errEol;
				throw new Exception(__prematureeol);
			}
			res = int.Parse(_tokens[_ct].Substring(skip));
			advance();
			return res;
		}
		private char getchar() {
			char res;
			if (!ismore){
				_error = Error.errEol;
				throw new Exception(__prematureeol);
			}
			res = char.Parse(_tokens[_ct]);
			advance();
			return res;
		}
		private Int64 getInt64() {
			Int64 res;
			if (!ismore){
				_error = Error.errEol;
				throw new Exception(__prematureeol);
			}
			res = long.Parse(_tokens[_ct]);
			advance();
			return res;
		}
		private string getstring(){
			string res;
			if (!ismore){
				_error = Error.errEol;
				throw new Exception(__prematureeol);
			}
			res = _tokens[_ct];
			advance();
			return res;

		}
		private int getint() {
			int res;
			if (!ismore){
				_error = Error.errEol;
				throw new Exception(__prematureeol);
			}
			res = int.Parse(_tokens[_ct]);
			advance();
			return res;
		}
		private double getdouble() {
			double res;
			if (!ismore){
				_error = Error.errEol;
				throw new Exception(__prematureeol);
			}
			res = double.Parse(_tokens[_ct]);
			advance();
			return res;
		}
		private bool match_current(string pattern, bool casesensitive){
			if (!ismore){
				return false;
			}
			if (casesensitive){
				return (_tokens[_ct].Equals(pattern));
			} else {
				return (_tokens[_ct].ToLower().Equals(pattern));
			}
		}
		private bool isprefix(string prefix){
			if (!ismore)
				return false;
			return _tokens[_ct].Substring(0, prefix.Length).Equals(prefix);
		}
		public Error err {
			get {
				return _error;
			}
		}
		public string input {
			get {
				return _spec;
			}
			set {
				_error = Error.Ok;
				_ct = 0;
				_spec = value;
				if (_spec == null){
					_tokens = null;
				} else if (_spec.Length > 0){
					_tokens = _spec.Split(_delims);

				} else {
					_tokens = null;
				}		
			}
		}
		private void rewind(){
			_ct = 0;
		}
		public CalcParser(char[] in_spec) {
			//
			// Break spec into words (tokens)
			// First word must be recognized as one of the keys
			// Case insensitive
			init();
			input = new string(in_spec);
		}
		public CalcParser(string in_spec){
			init();
			input = in_spec;
		}
		/// <summary>
		/// Determine the type of token to follow
		/// </summary>
		/// <returns></returns>
		public Type peek(){
			Type res = Type.Unknown;
			// Peek into the string, and return the type 
			if (_tokens == null){
				return(Type.Empty);
			}
			if (match_current("c", false)){
				res = Type.Convex;
			} else if (match_current("h", false)){
				res = Type.Hid;
			}
			return res;
		}
		private bool parse_pairs(){
			//
			//
			while(ismore){
				//
				//
				try {
					_i1 = this.getInt64();
					if (ismore){
						_i2 = this.getInt64();
					} else {
						_i2 = _i1;
						_error = Error.errOnlyOne;
						return isok;
					}
				} catch {
					if (_error != Error.errEol)
						_error = Error.errIllegalNumber;
					return isok;
				}
			}
			return true;
		}
		public string parseStep(out Int64 hid, out bool full) {
			string result = null;
			full = false;
			hid = 0;
			if (ismore) {
				if (!this.match_current("Saving", true)) {
					return null;
				}
			} else {
				return null;
			}
			this.advance();
			if (ismore) {
				hid = this.getInt64();
				result = _spec;
				if (ismore) {
					full = (this.getchar() == 'F');
				}
			} else {
				return null;
			}
			return result;
		}
		public bool parseHid(ref Int64 hid, ref bool filled, ref bool arch, out string color){
			this.getchar();
			color = null;
			if(ismore) hid = this.getInt64(); else return false;
			if(ismore) filled = (this.getchar() == 'c');else return false;
			if(ismore) arch = (this.getchar() == 'a');else return false;
			if(ismore) color = this.getstring();else return false;
			return true;
		}
		public bool parseHid(out Int64 hid, out char type) {
			hid = -1;
			type = 'U';
			if (ismore) {
				hid = this.getInt64();
				if (ismore) {
					type = this.getchar();
				}
			} else {
				return false;
			}
			return true;
		}
		public bool parseHid(out Int64 hid) {
			hid = -1;
			if (ismore) hid = this.getInt64(); else return false;
			return true;
		}

		public bool parseHalfspace(out double x, out double y, out double z, out double D, out char color) {
			x = y = z = D = 0.0;
			color = 'R';
			if (ismore) x = this.getdouble(); else return false;
			if (ismore) y = this.getdouble(); else return false;
			if (ismore) z = this.getdouble(); else return false;
			if (ismore) D = this.getdouble(); else return false;
			if (ismore) color = this.getchar(); else return false;
			return true;
		}
		public bool parseConvex(
			ref double x, ref double y, ref double z, ref double D,
			ref bool filled, out string color){
			this.getchar();
			color = null;

			if(ismore) x = this.getdouble(); else return false;
			if(ismore) y = this.getdouble();else return false;
			if(ismore) z = this.getdouble();else return false;
			if(ismore) D = this.getdouble();else return false;
			if(ismore) filled = (this.getchar() == 'f');else return false;
			if(ismore) color = this.getstring();else return false;
			return true;
		}
		public bool parse(ref Int64 i1, ref Int64 i2){
			bool res;
			res = parse_pairs();
			i1 = this._i1;
			i2 = this._i2;
			return res;
		}
		public bool parse(){
			if (_tokens == null)
				return isok;
			if (ismore){
				return parse_pairs();
			}
			return true;
		}
		protected static int tokenvalue(char c){
			switch (c){
				case'0': return 0;
				case '1': return 1;
				case '2': return 2;
				case '3': return 3;
				case '4': return 4;
				case '5': return 5;
				case '6': return 6;
				case '7': return 7;
				case '8': return 8;
				case '9': return 9;
				case 'A': return 10;
				case 'B': return 11;
				case 'C': return 12;
				case 'D': return 13;
				case 'E': return 14;
				case 'F': return 15;

				default: return 1;
			}
		}
		public static void getrgb(char[] name, out int r, out int g, out int b){
			r = 16 * tokenvalue(name[1]) + tokenvalue(name[2]);
			g = 16 * tokenvalue(name[3]) + tokenvalue(name[4]);
			b = 16 * tokenvalue(name[5]) + tokenvalue(name[6]);
			return;
		}
	}
}
