public class Timing implements FrameBufferInfo {
	private int pixfreq;
	private int hfreq;
	private int vfreq;
	private int hfporch;
	private int hsync;
	private int hbporch;
	private int hpixels;
	private int vfporch;
	private int vsync;
	private int vbporch;
	private int vpixels;
	private boolean colour;
	private boolean syncped;
	private boolean stereo;
	private String info;
	private String promval;
	private String monitor;

	private void setTiming( int pixfreq,
					int hfreq,
					int vfreq,
					int hfporch,
					int hsync,
					int hbporch,
					int hpixels,
					int vfporch,
					int vsync,
					int vbporch,
					int vpixels,
					boolean colour,
					boolean syncped,
					String info, 
					String promval) {
		this.pixfreq	= pixfreq;
		this.hfreq		= hfreq;
		this.vfreq		= vfreq;
		this.hfporch	= hfporch;
		this.hsync		= hsync;
		this.hbporch	= hbporch;
		this.hpixels	= hpixels;
		this.vfporch	= vfporch;
		this.vsync		= vsync;
		this.vbporch	= vbporch;
		this.vpixels	= vpixels;
		this.colour		= colour;
		this.syncped	= syncped;
		this.info		= info;

		if (promval.length() == 0) {
			this.promval= new String(hpixels+"x"+vpixels+"x"+vfreq);
			stereo = false;
		} else {
			this.promval= promval;
			stereo = (promval.indexOf("s") > 0);
		}

		if (stereo) {
			switch (vfreq) {
				case 112:
					monitor = new String("stereo_114"); 	//	Odd case
					break;
				default:
					monitor = new String("stereo_"+vfreq);
					break;
			}
		} else {
			switch (hpixels) {
				case 640:
					monitor = new String("ntsc");
					break;
				case 770:
					monitor = new String("pal"); 
					break;
				default:
					monitor = new String(hpixels+"_"+vfreq);
					break;
			}
		}
	}

	private void setTiming(int hpixels, int vpixels, int freq, String info, String promval) {
		setTiming(0,0,freq,0,0,0,hpixels,0,0,0,vpixels,false,false, info, promval);
	}

	public Timing (int timinfo) {
		switch (timinfo) {
			case DEFAULT_RES:
				setTiming(0, 0, 0, "Default", "Default");
				monitor = new String("ntsc");
				break;
			case _640x480x60i:
				setTiming(640,480,60, "Interlaced", "640x480x60i");
				monitor = new String("ntsc");
				break;
			case _768x575x50i:
				setTiming(768,575,50, "Interlaced", "768x575x50i");
				monitor = new String("pal");
				break;
			case _770x575x50i:
				setTiming(770,575,50, "Interlaced", "770x575x50i");
				monitor = new String("pal");
				break;
			case _960x680x108s:
				setTiming(960,680,108, "Stereo", "960x680x108s");
				break;
			case _960x680x112s:
				setTiming(960,680,112, "Stereo", "960x680x112s");
				break;
			case _1024x768x43:
				setTiming(1024,768,43, "", "1024x768x43");
				break;
			case _1024x768x60:
				setTiming(64125000,48286,60,16,128,160,1024,2,6,29,768,true,false,"","");
				break;
			case _1024x768x66:
				setTiming(1024,768,66, "", "1024x768x66");
				break;
			case _1024x768x70:
				setTiming(74250000,56593,70,16,136,136,1024,2,6,32,768,true,false,"","");
				break;
			case _1024x768x76:
				setTiming(1024,768,76, "", "1024x768x76");
				break;
			case _1024x768x77:
				setTiming(84375000,62040,77,32,128,176,1024,2,4,31,768,true,false,"","");
				break;
			case _1024x800x56:
				setTiming(1024,800,56, "", "1024x800x56");
				break;
			case _1024x800x60:
				setTiming(1024,800,60, "", "1024x800x60");
				break;
			case _1024x800x72:
				setTiming(1024,800,72, "", "1024x800x72");
				break;
			case _1024x800x74:
				setTiming(1024,800,74, "", "1024x800x74");
				break;
			case _1024x800x84:
				setTiming(1024,800,84, "", "1024x800x84");
				break;
			case _1024x800x85:
				setTiming(1024,800,85, "", "1024x800x85");
				break;
			case _1152x900x66:
				setTiming(94500000,61845,66,40,128,208,1152,2,4,31,900,true,false,"","");
				break;
			case _1152x900x76:
				setTiming(108000000,71808,76,32,128,192,1152,2,4,31,900,true,true,"","");
				break;
			case _1152x900x94:
				setTiming(1152, 900, 94, "", "1152x900x94");
				break;
			case _1280x1024x66:
				setTiming(1280,1024,66,"","1280x1024x66");
				break;
			case _1280x1024x67:
				setTiming(117000000,71691,67,16,112,224,1280,2,8,33,1024,true,true,"","");
				break;
			case _1280x1024x76:
				setTiming(135000000,81128,76,32,64,288,1280,2,8,32,1024,true,true,"","");
				break;
			case _1280x1024x76m:
				setTiming(135000000,81128,76,32,64,288,1280,2,8,32,1024,true,true,"*","1280x1024x76m");
				break;
			case _1600x1280x66:
				setTiming(1600,1280,66, "", "1600x1280x66");
				break;
			case _1600x1280x76:
				setTiming(216000000,101890,76,24,216,280,1600,2,8,50,1280,true,true,"","");
				break;
			case _1920x1080x72:
				setTiming(1920,1080,72, "", "1920x1080x72");
				break;
			default:
				System.err.println("Oh, bugger. Don't recognise resolution " + timinfo);
		}
	}

	public String identify() {
		String id;

		if (this.hpixels == 0)
			id = new String("Default for attached monitor");
		else
			id = new String(hpixels + "x" + vpixels + " @ " + vfreq + "Hz " + info);
		return(id);
	}

	private static void init_supportmask(int fb, int res) {
		SupportMask[fb] |= 1 << res;
	}

	private static final int [] SupportMask = new int[MAX_FB+1];

	static {
		for (int i = 0; i < MAX_FB; i++)
			SupportMask[i] = 0;

		init_supportmask(BW2, _1152x900x66);
		init_supportmask(BW2, _1152x900x76);

		init_supportmask(CG3, _1152x900x66);
		init_supportmask(CG3, _1152x900x76);

		init_supportmask(GX, _1152x900x66);
		init_supportmask(GX, _1152x900x76);

		SupportMask[GX_2MB] = SupportMask[GX];
		init_supportmask(GX_2MB, _1280x1024x67);

		SupportMask[GXPLUS] = SupportMask[GX];
		init_supportmask(GXPLUS, _1024x800x85);
		init_supportmask(GXPLUS, _1280x1024x67);

		SupportMask[TGX] = SupportMask[GX];
		init_supportmask(TGX, _1024x768x60);
		init_supportmask(TGX, _1024x768x70);
		init_supportmask(TGX, _1024x768x76);
		init_supportmask(TGX, _1024x800x74);
		init_supportmask(TGX, _1024x800x85);

		SupportMask[TGXPLUS] = SupportMask[TGX];
		init_supportmask(TGXPLUS, _1280x1024x67);
		init_supportmask(TGXPLUS, _1280x1024x76);
		init_supportmask(TGXPLUS, _1600x1280x76);

		init_supportmask(GS, _1152x900x76);

		init_supportmask(GT, _1280x1024x67);
		init_supportmask(GT, _1280x1024x76);

		init_supportmask(SX, _1024x768x60);
		init_supportmask(SX, _1024x768x66);
		init_supportmask(SX, _1024x768x70);
		init_supportmask(SX, _1024x800x84);
		init_supportmask(SX, _1152x900x66);
        init_supportmask(SX, _1152x900x76);
		init_supportmask(SX, _1280x1024x66);
        init_supportmask(SX, _1280x1024x76m);
        init_supportmask(SX, _1600x1280x66);
        init_supportmask(SX, _1920x1080x72);
		
		init_supportmask(ZX, _640x480x60i);
		init_supportmask(ZX, _770x575x50i);
		init_supportmask(ZX, _960x680x108s);
		init_supportmask(ZX, _960x680x112s);
		init_supportmask(ZX, _1024x768x60);
		init_supportmask(ZX, _1024x768x76);
		init_supportmask(ZX, _1152x900x66);
		init_supportmask(ZX, _1152x900x76);
		init_supportmask(ZX, _1280x1024x67);
		init_supportmask(ZX, _1280x1024x76);

		init_supportmask(S24, _1152x900x66);
		init_supportmask(S24, _1152x900x76);

		init_supportmask(TCX, _1152x900x66);
		init_supportmask(TCX, _1152x900x76);
		init_supportmask(TCX, _1152x900x94);

		SupportMask[TCX_2MB] = SupportMask[TCX];
		init_supportmask(TCX_2MB, _1280x1024x67);
		init_supportmask(TCX_2MB, _1280x1024x76);
		
		init_supportmask(FFB, _640x480x60i);
		init_supportmask(FFB, _768x575x50i);
		init_supportmask(FFB, _960x680x112s);
		init_supportmask(FFB, _960x680x108s);
		init_supportmask(FFB, _1024x768x43);
		init_supportmask(FFB, _1024x768x60);
		init_supportmask(FFB, _1024x768x70);
		init_supportmask(FFB, _1024x768x77);
		init_supportmask(FFB, _1024x800x56);
		init_supportmask(FFB, _1024x800x60);
		init_supportmask(FFB, _1024x800x72);
		init_supportmask(FFB, _1024x800x84);
		init_supportmask(FFB, _1152x900x66);
		init_supportmask(FFB, _1152x900x76);
		init_supportmask(FFB, _1280x1024x67);
		init_supportmask(FFB, _1280x1024x76);

		SupportMask[CREATOR3D] = SupportMask[FFB];
		SupportMask[FFB2_SV] = SupportMask[FFB];
		SupportMask[FFB2_DV] = SupportMask[FFB];
		SupportMask[FFB2_SH] = SupportMask[FFB];
		SupportMask[FFB2_DH] = SupportMask[FFB];
	}

	public static boolean supports(int fb, int res) {
		if (res == 0)
			return true;	//	EVERYTHING supports a default!

        return ((SupportMask[fb] & (1 << res)) != 0);
    }

	public String promval() {
		return(promval);
	}

	public boolean is_default() {
		return (hpixels == 0);
	}

	public String full_timing() {
		String ret = null;
		if (pixfreq > 0) {
			ret = new String( pixfreq+","+ hfreq+","+ vfreq+","+
				hfporch+","+ hsync+","+ hbporch+","+ hpixels+","+
				vfporch+","+ vsync+","+ vbporch+","+ vpixels+",");
			ret = ret + ((colour) ? "COLOR," : ",") + ((syncped) ? "0OFFSET" : "");
		}
		return(ret);
	}
}
