/***********************************************************
Copyright (C) 2004 VeriSign, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

http://www.verisign.com/nds/naming/namestore/techdocs.html
***********************************************************/
package com.verisign.epp.framework;

import java.util.Hashtable;

import com.verisign.epp.codec.gen.EPPMsgQueue;
import com.verisign.epp.codec.gen.EPPResponse;
import com.verisign.epp.codec.gen.EPPResult;
import com.verisign.epp.serverstub.GenHandler;

/**
 * The {@code EPPPollQueueMgr} is used to handle the poll command, which allows
 * to discover and retrieve client service messages from a server. Once
 * {@link GenHandler} issues the get method, {@code EPPPollQueueMgr} receives
 * data from {@link EPPPollDataSource} and then returns concrete
 * {@link EPPResponse} back to {@link GenHandler}.
 */
public class EPPPollQueueMgr {
  /** Singleton instance */
  private static EPPPollQueueMgr theQueueMgr = new EPPPollQueueMgr();

  /** Poll data source */
  private EPPPollDataSource dataSource = null;

  /** Poll queue handlers */
  private Hashtable handlers = new Hashtable();

  /**
   * Private Constructor (Singleton pattern.)
   */
  private EPPPollQueueMgr() {
  }

  /**
   * Gets instance of {@code EPPPollQueueMgr}
   *
   * @return Gets the Singleton instance of {@code EPPPollQueueMgr}
   */
  public static EPPPollQueueMgr getInstance() {
    return theQueueMgr;
  }

  /**
   * Gets the poll queue data source
   *
   * @return Gets the registered {@link EPPPollDataSource} if
   *         defined;{@code null} otherwise.
   */
  public EPPPollDataSource getDataSource() {
    return this.dataSource;
  }

  /**
   * Registers a poll handler.
   *
   * @param aHandler
   *           Poll handler to register in the {@code EPPPollQueueMgr}.
   */
  public void register(EPPPollHandler aHandler) {
    this.handlers.put(aHandler.getKind(), aHandler);
  }

  /**
   * Registers the poll data source.
   *
   * @param aSource
   *           Poll data source to register in the {@code EPPPollQueueMgr}.
   */
  public void setDataSource(EPPPollDataSource aSource) {
    this.dataSource = aSource;
  }

  /**
   * Gets the {@link EPPResponse} poll message from the data source
   *
   * @param aRecp
   *           Server-specific recipient object that can be used to filter the
   *           messages by the recipient.
   * @param aContextData
   *           Server context passed into the {@link EPPPollDataSource}.
   *
   * @return EPPResponse Returns the concrete {@link EPPResponse}
   *
   * @exception EPPPollQueueException
   *               Unable to get the {@link EPPResponse} from data source
   */
  public EPPResponse get(Object aRecp, Object aContextData) throws EPPPollQueueException {
    EPPPollDataRecord theRecord = null;

    try {
      theRecord = this.dataSource.get(aRecp, aContextData);
    }
    catch (EPPPollQueueException ex) {
      if (ex.getType() == EPPPollQueueException.TYPE_QUEUE_EMPTY) {
        // No Record for recipient
        EPPResponse theResponse = new EPPResponse();
        theResponse.setResult(EPPResult.SUCCESS_POLL_NO_MSGS, "Command completed successfully; no messages");

        return theResponse;
      }

      // just re-throw the exception.
      throw ex;
    }

    // Needs to find appropriate handler
    EPPPollHandler theHandler = (EPPPollHandler) this.handlers.get(theRecord.getKind());

    // No associated handler?
    if (theHandler == null) {
      throw new EPPPollQueueException("Handler for kind " + theRecord.getKind() + " does not exist");
    }

    return theHandler.toResponse(theRecord);
  }

  /**
   * Puts poll message into the poll data source
   *
   * @param aRecp
   *           Recipient of the poll message, which represents the logical poll
   *           queue.
   * @param aKind
   *           Kind of poll message
   * @param aData
   *           Poll message
   * @param aContextData
   *           Server context data
   *
   * @exception EPPPollQueueException
   *               Error putting message in queue
   */
  public void put(Object aRecp, String aKind, Object aData, Object aContextData) throws EPPPollQueueException {
    this.dataSource.put(aRecp, aKind, aData, aContextData);
  }

  /**
   * Delete object from data source
   *
   * @param aRecp
   *           Recipient of the poll message, which represents the logical poll
   *           queue.
   * @param aMsgId
   *           Message identifier to delete
   * @param aContextData
   *           Server context data
   *
   * @return EPPResponse {@link EPPResponse} containing result of deleting the
   *         poll message
   *
   * @exception EPPPollQueueException
   *               Error deleting the message
   */
  public EPPResponse delete(Object aRecp, String aMsgId, Object aContextData) throws EPPPollQueueException {
    int queueSize = 0;

    try {
      queueSize = this.dataSource.delete(aRecp, aMsgId, aContextData);
    }
    catch (EPPPollQueueException ex) {
      if (ex.getType() == EPPPollQueueException.TYPE_MSGID_NOT_FOUND) {
        EPPResult theResult = new EPPResult(EPPResult.OBJECT_DOES_NOT_EXIST);
        theResult.addValue("<poll msgID=\"" + aMsgId + "\" op=\"ack\"/>");

        EPPResponse theResponse = new EPPResponse();
        theResponse.setResult(theResult);

        return theResponse;
      }

      // just re-throw the exception.
      throw ex;
    }

    EPPResult theResult = new EPPResult(EPPResult.SUCCESS);
    EPPResponse theResponse = new EPPResponse();
    theResponse.setResult(theResult);
    theResponse.setMsgQueue(new EPPMsgQueue(Long.valueOf(queueSize), aMsgId));

    return theResponse;
  }
}
