package gmt.protocol.ais;

class ChecksumFailedException extends Exception
{
	public ChecksumFailedException() {}
	public ChecksumFailedException( String str )
	{
		super(str);
	}
}


class VDMSentenceException extends Exception
{
	public VDMSentenceException() {}
	public VDMSentenceException( String str )
	{
		super(str);
	}
}


public class RawAISParser {
	
	int    msgid;               //!< Message ID 0-31
    int    sequence;            //!< VDM message sequence number
    int    total;               //!< Total # of parts for the message
    int    num;                 //!< Number of the last part stored
    char   channel;             //!< AIS Channel character
    Sixbit six_state;           //!< sixbit parser state
    
    int    dac;
    int    fi;
    int    asc;
    
    
    String[] fields;
	Nmea nmea_message = new Nmea();
	
	/**
	 *  Assemble AIVDM/VDO sentences
	 *
     * This function handles re-assembly and extraction of the 6-bit data
     * from AIVDM/AIVDO sentences.
     *
     * Because the NMEA standard limits the length of a line to 80 characters
     * some AIS messages, such as message 5, are output as a multipart VDM 
     * messages. 
     * This routine collects the 6-bit encoded data from these parts and 
     * returns a 1 when all pieces have been reassembled.
     * 
     * It expects the sentences to:
     * - Be in order, part 1, part 2, etc.
     * - Be from a single sequence
     * 
     * It will return an error if it receives a piece out of order or from
     * a new sequence before the previous one is finished.
     * 
     * Returns
     *   - 0 Complete packet
     *   - 1 Incomplete packet
     *   - 2 NMEA 0183 checksum failed
     *   - 3 Not an AIS message
     *   - 4 Error with nmea_next_field
     *   - 5 Out of sequence packet
     *
     */	
	public int assembleVDM( String str ) throws ChecksumFailedException, StartNotFoundException, VDMSentenceException {		
		int ptr;
		int	total;
		int num;
		int sequence;
		
		nmea_message.init(str);
		
		if (nmea_message.checkChecksum() != 0)
			throw new ChecksumFailedException();
		ptr = nmea_message.find_start();

		// Allow any sender type for VDM and VDO messages
		if (	!str.regionMatches(ptr+3, "VDM", 0, 3) &&
				!str.regionMatches(ptr+3, "VDO", 0, 3)		)
	    {
	        /* Not an AIS message */
	        throw new VDMSentenceException("Not a VDM or VDO message");
	    }

		fields = str.split(",|\\*");
		if (fields.length != 8)
			throw new VDMSentenceException("Does not have 8 fields");
		
		// Get the message info for multipart messages
		try {
			total = Integer.parseInt(fields[1]);
			num   = Integer.parseInt(fields[2]);
		} catch (NumberFormatException e) {
			throw new VDMSentenceException("total or num field is not an integer");
		}
		try {
		    sequence = Integer.parseInt(fields[3]);
		} catch (NumberFormatException e) {
			// null sequence is not fatal
			sequence = 0;
		}

		// Are we looking for more message parts?
		if (this.total > 0)
		{
			if( (this.sequence != sequence) || (this.num != num-1) )
	        {
	            this.total = 0;
	            this.sequence =0;
	            this.num = 0;
	            throw new VDMSentenceException("Out of sequence sentence");
	        }
	        this.num++;
		} else {
			this.total = total;
	        this.num = num;
	        this.sequence = sequence;
	        this.six_state = new Sixbit();
	        this.six_state.init("");
		}
		this.channel = fields[4].charAt(0);
		
		this.six_state.add(fields[5]);
		
		if ((total==0) || (this.total == num))
		{
		    this.total    = 0;
		    this.num      = 0;
		    this.sequence = 0;

		    // Get the message id
		    try {
		    	this.msgid = (int) six_state.get(6);		    	
		    	getDACFIASC(this.six_state);
		    } catch (SixbitsExhaustedException e) {
		    	throw new VDMSentenceException("Not enough bits for msgid");
		    }
		    
		    // Adjust bit count
		   this.six_state.padBits( Integer.parseInt(fields[6]) );
		    
		    /* Found a complete packet */
		    return 0;
		}
		
		// No complete message yet
		return 1;
	}
		
	
	private void getDACFIASC(Sixbit sixbit) {
		this.dac = 0;
		this.fi  = 0;
		this.asc = 0;				
		
		long garbage;
		
		try {
			switch(this.msgid) {
			case(6) :{
				Sixbit tempbit = new Sixbit();
				if (sixbit.getClone(tempbit)) {
					garbage = tempbit.get(2);
					garbage = tempbit.get(30);
					garbage = tempbit.get(2);
					garbage = tempbit.get(30);
					garbage = tempbit.get(2);
					
					this.dac = (int) tempbit.get(10);
					this.fi  = (int) tempbit.get(6);
					
					if ((dac == 440 || dac == 441 || dac == 0) && fi == 63) {
						this.asc = (int) tempbit.get(6);
					}					
				}
				break;}
			case(8) :{
				Sixbit tempbit = new Sixbit();
				if (sixbit.getClone(tempbit)) {
					garbage = tempbit.get(2);
					garbage = tempbit.get(30);
					garbage = tempbit.get(2);
					
					this.dac = (int) tempbit.get(10);
					this.fi  = (int) tempbit.get(6);
					this.asc = (int) tempbit.get(6);
				}
				break;}
			}
		} catch (SixbitsExhaustedException e) {
			this.dac = 0;
	        this.fi  = 0;
			e.printStackTrace();
		}
	}
		
	
	
	
}
