package org.rubycoder.gsm;

// $Id: GSMDecoderStream.java,v 1.1 2008/09/18 10:51:48 kane77 Exp $

import org.rubycoder.gsm.GSMDecoder;
import java.io.InputStream;
import java.io.IOException;

// will extend InputStream eventually
class GSMDecoderStream extends InputStream{

	// make the 8k lookup table
	private static int[] lookup_table;

	private static byte lin2mu(int lin){
	        int s,e,f; // sign, exp, mant
        	int topp;
		lin>>=2;
	        if (lin < 0) {
        	        lin = -lin;
                	s=1;
	        } else
        	        s=0;
	        for(int n=0;n<8;n++) {
        	        topp=(0x20<<(n+1)) - 0x20 ;
        		if (lin < topp ) {
	                        e = n;
        	                f = ( lin - (0x20<<e) + 0x20 ) >>> (e+1);
                	        //System.out.print(" s=" + s + " e=" + e + " f=" + f );
                        	return (byte)( ( (s << 7) | (e << 4) | (f & 0x0F) ) ^ 0xFF);
	                }
	        }
	    	return (byte)(s<<8) ;
	}


	private static int[] create_lookup_table() {
		int i;
		int pcm;
		int[] res = new int[8192];

		for(i=0;i<8192;i++) {
			pcm = i-4096;
			// pcm in the range [-4096,4095]
			pcm<<=3;
			// pcm in the range [-32768,32760]
			// (this is high as the gsm signal can go as the
			// last 3 bits set are fixed at zero)
			//res[i] = lin2mu(pcm)+128;
			//res[i] = (int)(lin2mu(pcm))&0xff;
			res[i] = (lin2mu(pcm))&0xff;
//System.out.println("res["+i+":"+pcm+"]=="+res[i]);
		}

		return res;
	}

	// the InputStream methods

	private InputStream GSMStream;


	private int gsm_index = 0;
	private final byte[] gsm_frame;
	private final int[] buffer;
	private int buffer_index = 0;
	private int buffer_size = 0;

	private final GSMDecoder theDecoder;

	public GSMDecoderStream(InputStream is) {
		if (lookup_table==null) 
			lookup_table = create_lookup_table();
		gsm_index = 0;
		GSMStream = is;
		buffer = new int[160];
		buffer_index = 0;
		buffer_size = 0;
		gsm_frame = new byte[33];
		theDecoder = new GSMDecoder();
	}

	public int available() throws IOException {
		int avail;

		// how many frames are still available?
		try {
			avail = GSMStream.available()/33;
		} catch (IOException ioe) {
			throw new IOException("Recieved IO Exception from source stream.");
		} catch (NullPointerException npe) {
			throw new IOException("Source stream not open.");
		}
		// each frame is 160 bytes long
		avail *= 160;
		// how much is in the buffer?
		avail += buffer_size-buffer_index;

		return avail;
	}

	public void close() {
		GSMStream = null;
	}

	public synchronized void mark(int size) {
		return;
	}

	public boolean markSupported() {
		return false;
	}

	public final int read() {
		int res;
		//System.out.println("read()");
		int read_count = 0;
		if (buffer_index>=buffer_size) {
			// can't handle any less than 33 bytes
			//System.out.println("reading a frame");
			try {
				read_count = GSMStream.read(gsm_frame);
			} catch (IOException ioe) {
				System.out.println("got io exception");
				close();
				return -1;
			} catch (NullPointerException npe) {
				return -1;
			}
			if (read_count<33) {
				//System.out.println("got less than 33 bytes");
				close();
				return -1;
			}
			//System.out.println("got "+read_count+" bytes from source");
			try {
				theDecoder.decode(gsm_frame,buffer);
			} catch (InvalidGSMFrameException igfe) {
				System.out.println("invalid frame");
				close();
				return -1;
			}
			buffer_index = 0;
			buffer_size = 160;
		}

		res = lookup_table[(buffer[buffer_index++]>>3)+4096];
		return res;
	}
	
	public final int read(byte output[], int start, int length) {
		//System.out.println("read(..,"+start+","+length+");");
		int val,i;
		if (GSMStream==null) return -1;
		for(i=start;i<length;i++) {


			read: {
				//System.out.println("read()");
				int read_count = 0;
				if (buffer_index>=buffer_size) {
					// can't handle any less than 33 bytes
					//System.out.println("reading a frame");
					gsm_index = 0;
					while (gsm_index<33) {
						try {	
							read_count = GSMStream.read(gsm_frame,gsm_index,33-gsm_index);
						} catch (IOException ioe) {
							System.out.println("got io exception");
							close();
							val = -1;
							break read;
						} catch (NullPointerException npe) {
							val = -1;
							break read;
						}
						//System.out.println("got "+read_count+" bytes from source");
						//if (read_count<33) {
						//	System.out.println("got less than 33 bytes");
						//	close();
						//	val = -1;
						//	break read;
						//}
						if (read_count<0) {
							System.out.println("got eof");
							close();
							val = -1;
							break read;
						}
						gsm_index += read_count;
					}
					try {
						theDecoder.decode(gsm_frame,buffer);
					} catch (InvalidGSMFrameException igfe) {
						System.out.println("invalid frame");
						close();
						val = -1;
						break read;
					}
					buffer_index = 0;
					buffer_size = 160;
				}

				val = lookup_table[(buffer[buffer_index++]>>3)+4096];
			}
			//return res;
			//val = read();


			if (val<0) {
				//System.out.println("got negative on read");
				break;
			}
			output[i]=(byte)(val);
			//System.out.println("output["+i+"]="+output[i]);
		}
		//System.out.println("read "+i+" bytes.");
		return i;
	}

	public final int read(byte output[]) {
		return read(output,0,output.length);
	}

	public void reset() {
		return;
	}

	public void skip(int n) {
		// not implemented yet
		return;
	}

}
