////////////////////////////////////////////////////////////////////////////////
// Port.h -- this file is part of the Emulator Developers Kit
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm

class Port : public Object {
  // structure representing a connection between ports
  struct Connection {

    // common state of all connected ports
    byte bState;
    byte abFill[3];

    // output states of connected lines, default = 0xFF
    byte abOutput[16];

    // a set bit x indicates that abOutput[x] is used
    int iOutputsUsed;

    // linked list of ports
    Port* pFirstPort; 

    // linked list of ports which have OnChange function
    Port* pFirstPortChange;

    // linked list of ports which have OnEveryChange function
    Port* pFirstPortEveryChange;

    // clear variables
    void Clear() {
      bState = 0xFF;
      memset(abOutput, 0xFF, sizeof abOutput);
      iOutputsUsed = 0;
      pFirstPort = NULL;
      pFirstPortChange = NULL;
      pFirstPortEveryChange = NULL;
    }

    // constructor
    Connection() {
      memset(abFill, 0, sizeof abFill);
      Clear();
    }

  };

  // pointer to pConnection->abOutput[x]
  byte* pbOutput;

  // parent function pointers
  pfnbb pfnbbOnChange;
  pfn pfnOnEveryChange;

  // all ports in a group point to the same connection structure
  Connection* pConnection;

  // next port in the group
  Port* pNextPort;

  // next port in the group which has an OnChange function
  Port* pNextPortChange;

  // next port in the group which has an OnEveryChange function
  Port* pNextPortEveryChange;

  // dummy for unconnected ports
  Connection NoConnection;

public:

  // constructor
  global Port() {
    pbOutput = &NoConnection.abOutput[0];
    pfnbbOnChange = NULL;
    pfnOnEveryChange = NULL;
    pConnection = &NoConnection;
    pNextPort = NULL;
    pNextPortChange = NULL;
    pNextPortEveryChange = NULL;
    NoConnection.iOutputsUsed = 1;
    NoConnection.pFirstPort = this;
  }

  // destuctor
  virtual ~Port() {
    if (IsConnected()) {
      if (GetOutput() != GetInput()) {
        SetOutput(GetInput());
      }
      Disconnect();
    }
  }

  // OnChange function
  global void SetOnChange(pfnbb pfnbbNewOnChange);
  inline pfnbb GetOnChange()                                                                        { return pfnbbOnChange; }
  global void SetOnEveryChange(pfn pfnNewOnEveryChange);
  inline pfn GetOnEveryChange()                                                                     { return pfnOnEveryChange; }

  // port access
  global void SetOutput(byte bNewOutput);
  inline byte GetOutput()                                                                           { return *pbOutput; }
  inline byte GetInput()                                                                            { return pConnection->bState; }
  global byte GetInputWithoutThis();

  // connect two ports or two groups of ports
  global void ConnectTo(Port& OtherPort);
  global void Disconnect();
  inline flag IsConnected()                                                                         { return pConnection->pFirstPort->pNextPort != NULL; }
  inline flag IsConnectedTo(Port& OtherPort)                                                        { return pConnection == OtherPort.pConnection; }

  // check the port and its connection for errors
  global void AssertValid();

};
