/**
 * AIS Parser manages the multiple states associated with AIS message parsing.
 *  An AISParser manages multi-part AISMessages.  An AISMessage is one single
 *  line of NMEA/AIS, not a complete, multi-part message.
 *
 * @file     AISParser.js
 * @date     2008-10-23 19:31 HST
 * @author   Paul Reuter
 * @version  1.1
 *
 * @include Event
 * @include Sixbit
 */


/**
 * Provides an API for decoding AIS messages and returning objects.
 *
 * @constructor
 * @return {object} A new AISParser object.
 */
function AISParser(evt) {
  this.m_event = evt || new Event();
  this.m_state = null;
  
  this.initialize();
  return this;
}; // END constructor AISParser()


/**
 * Decodes the message at hand.
 *
 * @param {string} message An AIS message to decode.
 * @return {int} Status code returned from parsing message.
 *   1: incomplete
 *   0: complete
 *  -1: invalid
 *  -2: unrecognized
 *  -3: decode error
 */
AISParser.prototype.receive = function(message) { 
  // new message
  var nmsg = new AISMessage(message);
  if( !nmsg.isAIS() ) { 
    this.m_event.trigger(this,"invalid_message",message);
    return -2;
  }

  if( !nmsg.hasValidChecksum() ) { 
    this.m_event.trigger(this,"invalid_checksum",nmsg);
    return -1;
  }

  // old / previous message pointer
  var omsg = this.m_state["aismsg"];

  // Test: is this a fresh, new message  
  if( nmsg.isFirst() ) { 
    // Yes (fresh): Reset message parser state.

    // Test: was the previous message incomplete?
    if( !( omsg == null || omsg.isLast() ) ) { 
      // yes (was incomplete)
      this.m_event.trigger(this,"decode_error",omsg);
    }
    // Ok, we start fresh with this new message.
    this.reset(nmsg);

    // update pointer
    omsg = this.m_state["aismsg"];

  } else { 
    // No (not fresh).
    // test: was there a message waiting?
    if( !omsg ) { 
      // no: decode error.
      this.m_event.trigger(this,"decode_error",nmsg);
      return -3;
    }
    // test: is current message the one we were waiting for?
    if( omsg.isSequential(nmsg) ) {
      // yes (expected): so append message.
      if( !omsg.append(nmsg) ) { 
        // decode error
        this.m_event.trigger(this,"decode_error",nmsg);
        return -3;
      }

    } else {
      // no (unexpected): this message is junk.
      this.m_event.trigger(this,"decode_unexpected",nmsg);
      return -1;
    }
  }
  
  // Test: after possible append, is the message complete now?
  if( omsg.isComplete() ) {
    var hash = omsg.decode();
    if( !hash ) { 
      // return decode error
      this.m_event.trigger(this,"decode_error",omsg);
      return -3;
    }
    this.m_event.trigger(this,"complete",omsg.msgid,hash,omsg);
    // return complete
    return 0;
  }
  // return incomplete
  return 1;  
}; // END: function receive(message)
/*
AISParser.prototype.receive = function(message) { 
  var aism = new AISMessage(message);
  if( !aism.isAIS() ) { 
    this.m_event.trigger(this,"invalid_message",message);
    return -2;
  }
  if( !aism.hasValidChecksum() ) { 
    this.m_event.trigger(this,"invalid_checksum",aism);
    return -1;
  }

  // Test: is this a fresh, new message  
  if( aism.isFirst() ) { 
    // Yes (fresh): Reset message parser state.

    // Test: was the previous message incomplete?
    if( !(  this.m_state["aismsg"] == null || this.m_state["aismsg"].isLast() ) ) { 
      // yes (was incomplete)
      this.m_event.trigger(this,"decode_error",this.m_state["aismsg"]);
    }
    // Ok, we start fresh with this new message.
    this.reset(aism);

  } else { 
    // No (not fresh).
    // test: was there a message waiting?
    if( !this.m_state["aismsg"] ) { 
      // no: decode error.
      this.m_event.trigger(this,"decode_error",aism);
      return -3;
    }
    // test: is current message the one we were waiting for?
    if( this.m_state["aismsg"].isSequential(aism) ) {
      // yes (expected): so append message.
      if( !this.m_state["aismsg"].append(aism) ) { 
        // decode error
        this.m_event.trigger(this,"decode_error",aism);
        return -3;
      }

    } else {
      // no (unexpected): this message is junk.
      this.m_event.trigger(this,"decode_unexpected",aism);
      return -1;
    }
  }
  
  // Test: after possible append, is the message complete now?
  if( this.m_state["aismsg"].isComplete() ) {
    var hash = this.m_state["aismsg"].decode();
    if( !hash ) { 
      // return decode error
      this.m_event.trigger(this,"decode_error",this.m_state["aismsg"]);
      return -3;
    }
    this.m_event.trigger(this,"complete",
      this.m_state["aismsg"].msgid,hash,this.m_state["aismsg"]
    );
    // return complete
    return 0;
  }
  // return incomplete
  return 1;  
}; // END: function receive(message)
*/


/**
 * Initializes the AISParser object.
 *
 * @private
 * @return {boolean} true
 */
AISParser.prototype.initialize = function() {
  this.m_state = {
    'sixbit':new Sixbit(),
    'aismsg':null
  };
  return true;
}; // END: function initialize()


/**
 * re-initialize the parser state to match that of the current message.
 *
 * @private
 * @param {AISMessage} aism An AISMessage object.
 * @return {boolean} true always.
 */
AISParser.prototype.reset = function(aism) {
  this.m_state["sixbit"].set(aism.getMessage());
  this.m_state["aismsg"] = aism; // current working AIS message
  return true;
}; // END: function reset(aism)
