/* ``The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved via the world wide web at http://www.erlang.org/.
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
 * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
 * AB. All Rights Reserved.''
 * 
 *     $Id$
 */
package com.ericsson.otp.erlang;

import java.net.InetAddress;
import java.net.UnknownHostException;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.BufferedReader;
  
/**
 * <p> Represents an OTP node. </p>
 * 
 * <p> About nodenames: Erlang nodenames consist of two components, an
 * alivename and a hostname separated by '@'. Additionally, there are
 * two nodename formats: short and long. Short names are of the form
 * "alive@hostname", while long names are of the form
 * "alive@host.fully.qualified.domainname". Erlang has special
 * requirements regarding the use of the short and long formats, in
 * particular they cannot be mixed freely in a network of
 * communicating nodes, however Jinterface makes no distinction. See
 * the Erlang documentation for more information about nodenames. </p>
 * 
 * <p> The constructors for the AbstractNode classes will create names
 * exactly as you provide them as long as the name contains '@'. If
 * the string you provide contains no '@', it will be treated as an
 * alivename and the name of the local host will be appended,
 * resulting in a shortname. Nodenames longer than 255 characters will
 * be truncated without warning. </p>
 *
 * <p> Upon initialization, this class attempts to read the file
 * .erlang.cookie in the user's home directory, and uses the trimmed
 * first line of the file as the default cookie by those constructors
 * lacking a cookie argument. If for any reason the file cannot be
 * found or read, the default cookie will be set to the empty string
 * (""). The location of a user's home directory is determined using
 * the system property "user.home", which may not be automatically set
 * on all platforms. </p>
 *
 * <p> Instances of this class cannot be created directly, use one of
 * the subclasses instead. </p>
 **/
public class AbstractNode {
  static String localHost = null;
  String node;
  String host;
  String alive;
  String cookie;
  static String defaultCookie = null;

  // Node types
  static final int NTYPE_R6 =        110; // 'n' post-r5, all nodes
  static final int NTYPE_R4_ERLANG = 109; // 'm' Only for source compatibility
  static final int NTYPE_R4_HIDDEN = 104; // 'h' Only for source compatibility
  
  // Node capability flags
  static final int dFlagPublished = 1;
  static final int dFlagAtomCache = 2;
  static final int dFlagExtendedReferences = 4;
  static final int dFlagDistMonitor = 8;
  static final int dFlagFunTags = 16;
  static final int dFlagExtendedPidsPorts = 256;

  int ntype = NTYPE_R6;
  int proto = 0; // tcp/ip
  int distHigh = 5; // Cannot talk to nodes before R6
  int distLow = 5;  // Cannot talk to nodes before R6
  int creation = 0;
  int flags = dFlagExtendedReferences|dFlagExtendedPidsPorts;

  /* initialize hostname and default cookie */
  static {
    try {
      localHost = InetAddress.getLocalHost().getHostName();
      /* Make sure it's a short name, i.e. strip of everything after first '.' */
      int dot = localHost.indexOf(".");
      if (dot != -1) localHost = localHost.substring(0,dot);
    }
    catch (UnknownHostException e) {
      localHost = "localhost";
    }

    String dotCookieFilename = System.getProperty("user.home") + File.separator + ".erlang.cookie";
    BufferedReader br = null;

    try {
      File dotCookieFile = new File(dotCookieFilename);

      br = new BufferedReader(new FileReader(dotCookieFile));
      defaultCookie = br.readLine().trim();
    }
    catch (IOException e) {
      defaultCookie = "";
    }
    finally {
      try {
	if (br != null) br.close();
      } catch (IOException e) {}
    }
  }

  protected AbstractNode() {
  }
  
  /**
   * Create a node with the given name and the default cookie.
   **/
  protected AbstractNode(String node) {
    this(node,defaultCookie);
  }
  
  /**
   * Create a node with the given name and cookie.
   **/
  protected AbstractNode(String name, String cookie) {
    this.cookie = cookie;

    int i = name.indexOf('@',0);
    if (i<0) {
      alive = name;
      host = localHost;
    }
    else {
      alive = name.substring(0,i);
      host = name.substring(i+1,name.length());
    }

    if (alive.length() > 0xff) {
      alive = alive.substring(0,0xff);
    }

    node = alive + "@" + host;
  }

  /**
   * Get the name of this node.
   *
   * @return the name of the node represented by this object.
   **/
  public String node() {
    return node;
  }

  /**
   * Get the hostname part of the nodename. Nodenames are composed of
   * two parts, an alivename and a hostname, separated by '@'. This
   * method returns the part of the nodename following the '@'.
   *
   * @return the hostname component of the nodename.
   **/
  public String host() {
    return host;
  }

  /**
   * Get the alivename part of the hostname. Nodenames are composed of
   * two parts, an alivename and a hostname, separated by '@'. This
   * method returns the part of the nodename preceding the '@'.
   *
   * @return the alivename component of the nodename.
   **/
  public String alive() {
    return alive;
  }

  /**
   * Get the authorization cookie used by this node.
   *
   * @return the authorization cookie used by this node.
   **/
  public String cookie() {
    return cookie;
  }

  // package scope
  int type() {
    return ntype;
  }
  
  // package scope
  int distHigh() {
    return distHigh;
  }

  // package scope
  int distLow() {
    return distLow;
  }

  // package scope: useless information?
  int proto() {
    return proto;
  }

  // package scope
  int creation() {
    return creation;
  }

  /**
   * Set the authorization cookie used by this node.
   *
   * @return the previous authorization cookie used by this node.
   **/
  public String setCookie(String cookie) {
    String prev = this.cookie;
    this.cookie = cookie;
    return prev;
  }

  public String toString() {
    return node();
  }
}
