/*
 * @author : Umesh Kulkarni
 * @version 1.0
 *
 * Development Environment : Oracle9i JDeveloper
 *
 * Name of the File : UserAccountBean.java
 *
 * Creation / Modification History
 *    Umesh           26-Apr-2002        Created
 *
 */
package oracle.otnsamples.ibfbs.usermanagement.ejb;

import javax.ejb.EntityContext;

import java.util.Collection;

/**
 * This Class acts as Bean Implementation for UserAccountBean which is a local
 * EJB Object. Every User Account can have multiple Alerts, Preferences,
 * Portfolio, Trade Details and this  1:N relationship is managed by the
 * OC4J EJB Container.
 *
 * This new feature called CMR (Container Managed RelationShips) is a new
 * feature added in EJB 2.0 Specification.
 *
 * Note that A) This class is defined as abstract as Container actually
 * implements most of the logic for managing relationships (CMRs) and
 * Persisting Fields(CMPs).
 * This feature of CMR is added in EJB 2.0 Specification.
 *
 * B) This Class also does not declare the instance variables such as
 * AccountNumber, Symbol etc. In earlier approach EJB 1.1, the entity bean
 * used to declare the instance variables mapping to variables in the
 * Entity object. This was removed in EJB 2.0 Specification.

 * @version 1.0

 * @since   1.0
 */
public abstract class UserAccountBean
                implements javax.ejb.EntityBean {
//                implements javax.ejb.EntityBean, TimedObject {

  private EntityContext  ectx;
//  private InitialContext ictx;

  /**
   * Method to create UserAccountBean Local EJB Object.
   *
   * @param accountNumber  Account Number of the User Account
   * @param password       Password associated with the User Account
   * @param firstName      First Name associated with the User Account
   * @param lastName       Last Name associated with the User Account
   * @param organization   Organization associated with the User Account
   * @param address        Address associated with the User Account
   * @param city           City associated with the User Account
   * @param country        Country associated with the User Account
   * @param phone          Phone Number associated with the User Account
   * @param accountBalance Account Balance associated with User Account
   * @param email          Email Value associated with the User Account
   * @param userType       User Type associated with the User Account
   * @param linesPerPage   Lines Per Page associated with User Account
   * @param alertMode      Alert Mode associated with the User Account
   * @param mobileEmail    Mobile Email associated with the User Account
   * @return Local EJB Object implementing UserAccountLocal interface
   * @since   1.0

   */
  public Integer ejbCreate(Integer accountNumber, String password,
                           String firstName, String lastName,
                           String organization, String address,
                           String city, String state, String country,
                           String phone, float accountBalance, String email,
                           String userType, Integer linesPerPage,
                           String alertMode, String mobileEmail) {
    // Invoke Appropriate Setter Methods to initialize the values.
    // Note that the implementation of these setter methods are provided
    // by EJB Container as these fields are CMP
    setAccountNumber(accountNumber);
    setPassword(password);
    setFirstName(firstName);
    setLastName(lastName);
    setOrganization(organization);
    setAddress(address);
    setCity(city);
    setState(state);
    setCountry(country);
    setPhone(phone);
    setAccountBalance(accountBalance);
    setEmail(email);
    setUserType(userType);
    setLinesPerPage(linesPerPage);
    setAlertMode(alertMode);
    setMobileEmail(mobileEmail);
    return null;
  }

  /**
   * Method which is called by the Container after invoking of ejbCreate().
   * This method has the same signature as ejbCreate() method.
   * @param accountNumber  Account Number of the User Account
   * @param password       Password associated with the User Account
   * @param firstName      First Name associated with the User Account
   * @param lastName       Last Name associated with the User Account
   * @param organization   Organization associated with the User Account
   * @param address        Address associated with the User Account
   * @param city           City associated with the User Account
   * @param country        Country associated with the User Account
   * @param phone          Phone Number associated with the User Account
   * @param accountBalance Account Balance associated with User Account
   * @param email          Email Value associated with the User Account
   * @param userType       User Type associated with the User Account
   * @param linesPerPage   Lines Per Page associated with User Account
   * @param alertMode      Alert Mode associated with the User Account
   * @param mobileEmail    Mobile Email associated with the User Account
   * @since   1.0

   */
  public void ejbPostCreate(Integer accountNumber, String password,
                            String firstname, String lastname,
                            String organization, String address,
                            String city, String state, String country,
                            String phone, float accountBalance, String email,
                            String userType, Integer linesPerPage,
                            String alertMode, String mobileEmail) {}

  /**
   * Method Declarations for implementing Persistent CMR relationships
   * Method implementations are provided by the EJB Container.
   */

  /**
   * Method to get all the Alerts for this User Account.
   * Note that there are multiple Alerts for a User Account and return value
   * is a collection of 'AlertsLocal'
   *
   * @return All Alerts Associated with the User Account.
   * @since   1.0

   */
  public abstract Collection getAlerts();

  /**
   * Method to set all the Alerts for this User Account.
   * Note that there are multiple Alerts for a User Account and input value
   * is a collection of 'AlertsLocal'
   *
   * @param alerts All Alerts Associated with the User Account.
   * @since   1.0

   */
  public abstract void setAlerts(Collection alerts);

  /**
   * Method to get all the preferences for this User Account.
   * Note that there are multiple Preferences for a User Account and return
   * value is a collection of 'PreferencesLocal'
   *
   * @return All Preferences Associated with the User Account.
   * @since   1.0

   */
  public abstract Collection getPreferences();

  /**
   * Method to set all the preferences for this User Account.
   * Note that there are multiple Preferences for a User Account and input
   * value is a collection of 'PreferencesLocal'
   *
   * @param preferences All Preferences Associated with the User Account.
   * @since   1.0

   */
  public abstract void setPreferences(Collection preferences);

  /**
   * Method to get all the Portfolio Records for this User Account.
   * Note that there are multiple portfolio records for a User Account and
   * return value is a collection of 'PortfolioLocal'
   *
   * @return All Portfolio records Associated with the User Account.
   * @since   1.0

   */
  public abstract Collection getPortfolio();

  /**
   * Method to set all the portfolio for this User Account.
   * Note that there are multiple portfolio records for a User Account and
   * input value is a collection of 'PortfolioLocal'
   *
   * @param portfolio All Portfolio Associated with the User Account.
   * @since   1.0

   */
  public abstract void setPortfolio(Collection portfolio);

  /**
   * Method to get all the Trade Details records for this User Account.
   * Note that there are multiple Trade Details Records for a User Account
   * and return value is a collection of 'TradeDetailsLocal'
   *
   * @return All Trade Detail Records Associated with the User Account.
   * @since   1.0

   */
  public abstract Collection getTradeDetails();

  /**
   * Method to set all the Trade Details Records for this User Account.
   * Note that there are multiple Trade Details Records for a User Account
   * and input value is a collection of 'PortfolioLocal'
   *
   * @param tradeDetails All Trade Details Records Associated with
   *                        the User Account.
   * @since   1.0

   */
  public abstract void setTradeDetails(Collection tradeDetails);

  /**
   * Abstract Methods for CMP Fields.
   * Method implementations are provided by the EJB Container.
   */

  /**
   * Method to retrieve the value of AccountNumber associated
   * with the User Account
   *
   * @return  The Desired Account Number Value
   * @since   1.0

   */
  public abstract Integer getAccountNumber();

  /**
   * Method to assign the new AccountNumber value in the User Account.
   *
   * @param accountNumber New Account Number Value
   * @since   1.0

   */
  public abstract void setAccountNumber(Integer accountNumber);

  /**
   * Method to retrieve the value of Password associated with the User Account
   *
   * @return  The Desired Password Value
   * @since   1.0

   */
  public abstract String getPassword();

  /**
   * Method to assign the new Password value in the User Account.
   *
   * @param password New Password Value
   * @since   1.0

   */
  public abstract void setPassword(String password);

  /**
   * Method to retrieve the value of First Name associated with the User Account
   *
   * @return  The Desired First Name Value
   * @since   1.0

   */
  public abstract String getFirstName();

  /**
   * Method to assign the new First Name value in the User Account.
   *
   * @param firstName New First Name Value
   * @since   1.0

   */
  public abstract void setFirstName(String firstName);

  /**
   * Method to retrieve the value of Last Name associated with the User Account
   *
   * @return  The Desired Last Name Value
   * @since   1.0

   */
  public abstract String getLastName();

  /**
   * Method to assign the new Last Name value in the User Account.
   *
   * @param lastName New Last Name Value
   * @since   1.0

   */
  public abstract void setLastName(String lastName);

  /**
   * Method to assign the new Organization value in the User Account.
   * @since   1.0

   */
  public abstract String getOrganization();

  /**
   * Method to retrieve the value of Address associated with the User Account
   *
   * @param organization New Organization Value
   * @since   1.0

   */
  public abstract void setOrganization(String organization);

  /**
   * Method to retrieve the value of Address associated with the User Account
   *
   * @return  The Desired Address Value
   * @since   1.0

   */
  public abstract String getAddress();

  /**
   * Method to assign the new Address value in the User Account.
   *
   * @param address New Address Value
   * @since   1.0

   */
  public abstract void setAddress(String address);

  /**
   * Method to retrieve the value of City associated with the User Account
   *
   * @return  The Desired City Value
   * @since   1.0

   */
  public abstract String getCity();

  /**
   * Method to assign the new City value in the User Account.
   *
   * @param city New City Value
   * @since   1.0

   */
  public abstract void setCity(String city);

  /**
   * Method to retrieve the value of State associated with the User Account
   *
   * @return  The Desired State Value
   * @since   1.0

   */
  public abstract String getState();

  /**
   * Method to assign the new State value in the User Account.
   *
   * @param state New State Value
   * @since   1.0

   */
  public abstract void setState(String state);

  /**
   * Method to retrieve the value of Country associated with the User Account
   *
   * @return  The Desired Country Value
   * @since   1.0

   */
  public abstract String getCountry();

  /**
   * Method to assign the new country value in the User Account.
   *
   * @param country New Country Value
   * @since   1.0

   */
  public abstract void setCountry(String country);

  /**
   * Method to retrieve the value of Phone associated with the User Account
   *
   * @return  The Desired Phone Value
   * @since   1.0

   */
  public abstract String getPhone();

  /**
   * Method to assign the new Phone value in the User Account.
   *
   * @param phone New Phone Value
   * @since   1.0

   */
  public abstract void setPhone(String phone);

  /**
   * Method to retrieve the value of AccountBalance associated with the User Account
   *
   * @return  The Desired AccountBalance Value
   * @since   1.0

   */
  public abstract float getAccountBalance();

  /**
   * Method to assign the new AccountBalance value in the User Account.
   *
   * @param accountBalance New Account Balance Value
   * @since   1.0

   */
  public abstract void setAccountBalance(float accountBalance);

  /**
   * Method to retrieve the value of Email associated with the User Account
   *
   * @return  The Desired Email Value
   * @since   1.0

   */
  public abstract String getEmail();

  /**
   * Method to assign the new Email value in the User Account.
   *
   * @param email New Email Value
   * @since   1.0

   */
  public abstract void setEmail(String email);

  /**
   * Method to retrieve the value of UserType associated with the User Account
   *
   * @return  The Desired User Type Value
   * @since   1.0

   */
  public abstract String getUserType();

  /**
   * Method to assign the new UserType value in the User Account.
   *
   * @param userType New User Type Value
   * @since   1.0

   */
  public abstract void setUserType(String userType);

  /**
   * Method to retrieve the value of Lines Per Page associated with the User Account
   *
   * @return  The Desired Lines Per Page Value
   * @since   1.0

   */
  public abstract Integer getLinesPerPage();

  /**
   * Method to assign the new Lines Per Page value in the User Account.
   *
   * @param linePerPage New Lines Per Page Value
   * @since   1.0

   */
  public abstract void setLinesPerPage(Integer linePerPage);

  /**
   * Method to retrieve the value of Alert Mode associated with the User Account
   *
   * @return  The Desired Alert Mode Value
   * @since   1.0

   */
  public abstract String getAlertMode();

  /**
   * Method to assign the new Alert Mode value in the User Account.
   *
   * @param alertMode New Alert Mode Value
   * @since   1.0

   */
  public abstract void setAlertMode(String alertMode);

  /**
   * Method to retrieve the value of Mobile Email associated with the User Account
   *
   * @return  The Desired Mobile Email Value
   * @since   1.0
   */
  public abstract String getMobileEmail();

  /**
   * Method to assign the new Mobile Email value in the User Account.
   *
   * @param mobileEmail New Mobile Email Value
   * @since   1.0
   */
  public abstract void setMobileEmail(String mobileEmail);

  /**
   * standard call back methods
   */
  public void setEntityContext(EntityContext ectx) {
    this.ectx = ectx;
  }

  public void unsetEntityContext() { }

  public void ejbLoad() { }

  public void ejbStore() { }

  public void ejbActivate() {
/**
    if (this.ictx == null) {
      try {
        this.ictx = new InitialContext();
      } catch (NamingException ne)  {
        System.out.println("Naming Exception in ejbActivate()");
      }
    }
**/
  }

  public void ejbPassivate() { }

  public void ejbRemove() { }

/**  Following code depends on Bug Fix for Timer with Local Entity Beans

  public void initializeTimer(long timeout, String info, InitialContext ic) {

    try {

      this.ictx = ic;

      TimerService ts = ectx.getTimerService();

      // If there are existing Timer associated with this Bean,
      // Cancel it before initializing another one
      checkAndCancelTimers(ts.getTimers());

      long initialDuration  = timeout;
      long intervalDuration = timeout;

      ts.createTimer(initialDuration, intervalDuration,
                     info + " : " + this.getAccountNumber() );

      System.out.println("Timer created at " +
                         new Date(System.currentTimeMillis()) +
                         " with a timeout: " + intervalDuration +
                         " and with info: " + info);

    } catch (Exception ex) {
      ex.printStackTrace();
    }

    return;

  }

  private void checkAndCancelTimers(Collection allTimers) {


    if ( allTimers == null ) {
System.out.println("No timers associated");
      return;
    }

System.out.println("get iterator");
    Iterator timerIter = allTimers.iterator();
    while ( timerIter.hasNext() ) {
System.out.println("in loop...");
      Object obj = timerIter.next();

System.out.println("Obj type : " + obj.getClass().toString());

//**** To be removed twice next() is not required
      Timer timer = (Timer) timerIter.next();

      String info = (String) timer.getInfo();
System.out.println("INfo : " + info);

      if ( info.endsWith(this.getAccountNumber().toString()) ||
           info.endsWith("Portfolio Updates ") ) {
        timer.cancel();
System.out.println("Existing Timer cancelled");
      }
    }

  }

  public void ejbTimeout(Timer timer)
  {
    String timerInfo = (String) timer.getInfo();
    System.out.println("ejbTimeout() called at: " +
                       new Date(System.currentTimeMillis()) +
                           " with info: " + timerInfo);
    sendMail(timerInfo);

    return;
  }

  private void sendMail(String timerInfo) {

    Collection pfColl  = getPortfolioValuation();   // Get Portfolio Valuation
    String     message = formatHTMLMessage(pfColl, timerInfo); // Format Message

    MailService mailService = null;
    try {

      // Initialize the MailSender
      MailServiceHome mailServiceHome =
        (MailServiceHome) ictx.lookup("MailService");

      mailService = mailServiceHome.create();

      mailService.sendMail(getEmail(),
                           "portfolioupdates@fbs.com",
                           " Portfolio Updates ",
                           message);

System.out.println("Email : " + getEmail());
    } catch (Exception ex) {
      System.out.println(
             " Couldn't initialize mail service to send portfolio updates :"+
             ex.toString());
    }

  }

  private String formatHTMLMessage(Collection pfColl, String timerInfo) {

    StringBuffer sb = new StringBuffer();

    sb.append("<html><body>");

    sb.append("<table width=\"90%\"><tr><td>");
    sb.append("Hello, <br>  Portfolio information for Account Number : " );
    sb.append(this.getAccountNumber());
    sb.append(" is as follows : ");
    sb.append(" <br>  Timer Info : " + timerInfo);
    sb.append("</td></tr></table>");

    sb.append("<table width=\"90%\" border=\"1\" >");

    // Set the iterator for the Collection
    Iterator pvIter  = pfColl.iterator();

    int rowCount = 0; // initialize row counter

    int totalRecords = pfColl.size(); // Total records

    // Iterate through both the collections parallely
    // since they contain values for the same row
    while (pvIter.hasNext()) {
      if ( totalRecords == 1 )  {
        // Only one record in the Collection
        break;
      }

      PortfolioValue pfv = (PortfolioValue)pvIter.next();

      rowCount += 1;

      // Display the Table header only once when the row count is 1
      if (rowCount == 1) {

        String tabHeader[] = new String[] {
                                            "DATE",
                                            "NAME",
                                            "PRICE ($)",
                                            "CURRENT<br>PRICE ($)",
                                            "QUANTITY",
                                            "GAIN/<br>LOSS ($)",
                                            "GAIN (%)",
                                            "CURRENT<br>VALUE ($)"
                                          };

        sb.append("<tr> ");
        for (int count=0; count < 8 ; count++) {
          sb.append("   <th> ");
          sb.append("      <div align=\"center\">  ");
          sb.append(tabHeader[count]);
          sb.append("      </div>  ");
          sb.append("   </th>  ");
        }
        sb.append("</tr> ");

      }

      // Display the values in the Collection
      sb.append("<tr> ");
      sb.append("<td height=\"30\"> ");
      sb.append("   <div align=\"center\">");
      sb.append(getStringFromDate(pfv.getPurchaseDate(), "dd-MMM-yyyy"));
      sb.append("   </div>");
      sb.append("</td>");
      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"left\"> ");
      sb.append(pfv.getCompanyName());
      sb.append("  </div>");
      sb.append("</td>");
      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"right\"> ");
      sb.append(pfv.getPrice());
      sb.append("  </div>");
      sb.append("</td>");
      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"right\"> ");
      sb.append(pfv.getCurrPrice());
      sb.append("  </div>");
      sb.append("</td>");
      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"right\"> ");
      sb.append(pfv.getQuantity());
      sb.append("  </div>");
      sb.append("</td>");
      sb.append("<td height=\"30\"> ");

      String lossOrGain = formatFloatToString(pfv.getGainorloss());

      sb.append("  <table width=\"100%\" ");
      sb.append("         cellpadding=\"0\" cellspacing=\"0\"> ");
      sb.append("    <tr>");
      sb.append("      <td align=\"right\"> ");

      if (lossOrGain.indexOf("-") == 0) {  // Indicates Loss
        sb.append("<font color=\"#FF0000\" > "); // In red
      } else {
        sb.append("<font color=\"#008000\" > "); // In green
      }

      sb.append(lossOrGain);
      sb.append("</font></td>");

      sb.append("    </tr>");
      sb.append("  </table>");
      sb.append("</td>");
      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"right\"> ");
      sb.append(formatFloatToString(pfv.getPercent()) );
      sb.append("  </div>");
      sb.append("</td>");
      sb.append("<td height=\"30\"> ");
      sb.append("  <div align=\"right\"> ");
      sb.append(formatFloatToString(pfv.getCurrValue()) );
      sb.append("  </div>");
      sb.append("</td>");
      sb.append("</tr>");

      if ( rowCount == (totalRecords - 1) )  {
        // Last record has the Total Asset Value
        break;
      }
    }

    sb.append("</table>");

    sb.append("<table width=\"90%\">");

    float totalAssetValue = ((Float)pvIter.next()).floatValue();

    sb.append("<tr><td align=\"left\" >");
    sb.append(" Total Asset Value : ");
    sb.append(formatFloatToString(totalAssetValue));
    sb.append("</td></tr>");

    sb.append("<br>");

    sb.append("<tr><td align=\"left\" >");
    sb.append(" Account Balance : ");
    sb.append(formatFloatToString(getAccountBalance()));
    sb.append("</td></tr>");

    sb.append("</table>");

    sb.append("<table width=\"90%\"><tr><td>");
    sb.append("<br><br>");
    sb.append("Thank you for trading with us.");
    sb.append("<br><br>Happy Trading !! <br> FBS Team.");
    sb.append("</td></tr></table>");

    sb.append("</body></html>");

    return sb.toString();

  }

  private Collection getPortfolioValuation() {

    Collection pfValue    = new ArrayList();
    Collection pfColl     = null;

    float totalAssetValue = 0; // The Total Asset Value

    // Interface to hold TradeManagement impl class
    TradeManagementDAO tradeManagementDAO;

    try {

      // Get the stockrate implementation
      tradeManagementDAO = TradeManagementDAOFactory.getDAO(this.ictx);

      // Get the Collection of Portfolio corresponding to the user account
      pfColl = (Collection)getPortfolio();

      // Create an iterator for the above Collection
      Iterator portfolioLocalIter = pfColl.iterator();

      while(portfolioLocalIter.hasNext()) {

        // If the iterator has more values, get the next portfolio information
        PortfolioLocal pf = (PortfolioLocal)portfolioLocalIter.next();

        if(pf.getQuantity().intValue() > 0) {

          // If the Portfolio Quantity is greater than 0,
          // Add the Portfolio information to a new collection
          float currPrice  = tradeManagementDAO.getStockRate(pf.getSymbol());
          float buyValue   = pf.getQuantity().floatValue() * pf.getPrice();
          float currValue  = pf.getQuantity().floatValue() * currPrice;
          float gainorloss = currValue - buyValue;
          float percent    = (gainorloss / buyValue) * 100;

          pfValue.add(new PortfolioValue(pf.getLineNo(), pf.getQuantity(),
                                         pf.getPrice(), pf.getSymbol(),
                        tradeManagementDAO.getCompanyName(pf.getSymbol()),
                                         pf.getPurchaseDate(),
                                         pf.getPurchaseMode(),
                                         pf.getTradeId(), currPrice,
                                         gainorloss, percent, currValue));

          // Compute the Total Asset Value
          totalAssetValue = totalAssetValue + currValue;
        }
      }
    } catch(Exception ex) {
      System.out.println(" Exception in get Portfolio : " + ex.toString());
    }

    pfValue.add(new Float(totalAssetValue));

    //  return the collection created
    return pfValue;
  }


  /**
   * This method formats the input Date value according to the input format
   * and returns the formatted string.
   *
   * @param inputDate Input Date value
   * @param format    Format to which the date value has to be converted
   * @return The formatted Date string
   * @since 1.0
   *
  private String getStringFromDate(Date inputDate, String format) {
    String dateFormat = format; // Date Format
    SimpleDateFormat formatDate = new SimpleDateFormat(dateFormat);

    return formatDate.format(inputDate);
  }

  /**
   * This method takes an input float value and returns a string
   * with two decimal places.
   *
   * @param value float Value
   * @return The String representation of the input float value
   * @since 1.0
   *
  private String formatFloatToString(float value) {

    String stringValue = "";

    // Convert the input value
    float interim = (float) ((int) (value * 100)) / 100;
    stringValue   = (new Float(interim)).toString();

    StringTokenizer st = new StringTokenizer(stringValue, ".");
    if (st.countTokens() == 2) {   // If the string has a decimal
      String str = st.nextToken(); //   Ignore first portion of string
      str = st.nextToken();        //   Use the Decimal portion of the string

      /* If the decimal portion has only one digit *
      if (str.length() < 2)  {
        stringValue += "0";        //   Append one zero in the decimal place
      }
    }
    else {                         // Else, the string has no decimal places
      stringValue += ".00";        //   Append two zeroes in the decimal place
    }

    return stringValue;
  }

****/

}