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

class Line : public Object {

  // structure representing a connection between lines
  struct Connection {

    // number of low lines in this group
    int iCountLow;

    // linked list of lines
    Line* pFirstLine;

    // linked lists of lines which have OnHigh/OnLow functions
    Line* pFirstLineHigh;
    Line* pFirstLineLow;

    // linked lists of lines which have OnEveryChange function
    Line* pFirstLineEveryChange;

    // clear variables
    void Clear() {
      iCountLow = 0;
      pFirstLine = NULL;
      pFirstLineHigh = NULL;
      pFirstLineLow = NULL;
      pFirstLineEveryChange = NULL;
    }

    // constructor
    Connection() {
      Clear();
    }

  };

  // current output state, either high or low
  flag fOutputLow;

  // parent function pointers
  pfn pfnOnHigh;
  pfn pfnOnLow;
  pfn pfnOnEveryChange;

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

  // next line in the group
  Line* pNextLine;

  // next lines in the group which have OnHigh/OnLow functions
  Line* pNextLineHigh;
  Line* pNextLineLow;

  // next lines in the group which have OnEveryChange functions
  Line* pNextLineEveryChange;

  // dummy for unconnected lines
  Connection NoConnection;

public:

  // constructor
  global Line() {
    fOutputLow = false;
    pfnOnHigh = NULL;
    pfnOnLow = NULL;
    pfnOnEveryChange = NULL;
    pConnection = &NoConnection;
    pNextLine = NULL;
    pNextLineHigh = NULL;
    pNextLineLow = NULL;
    pNextLineEveryChange = NULL;
    NoConnection.pFirstLine = this;
  }

  // destuctor
  virtual ~Line() {
    if (IsConnected()) {
      if (IsInputLow() && !IsOutputLow()) {
        SetOutputLow();
      }
      Disconnect();
    }
  }

  // OnHigh/OnLow functions
  global void SetOnHigh(pfn pfnNewOnHigh);
  inline pfn GetOnHigh()                                                                            { return pfnOnHigh; }
  global void SetOnLow(pfn pfnNewOnLow);
  inline pfn GetOnLow()                                                                             { return pfnOnLow; }
  global void SetOnEveryChange(pfn pfnNewOnEveryChange);
  inline pfn GetOnEveryChange()                                                                     { return pfnOnEveryChange; }

  // line access
  inline void SetOutput(flag fNewOutput)                                                            { if (fNewOutput) { if (!IsOutputHigh()) { SetOutputHigh(); } } else { if (!IsOutputLow()) { SetOutputLow(); } } }
  global void SetOutputHigh();
  global void SetOutputLow();
  inline flag IsOutputHigh()                                                                        { return !fOutputLow; }
  inline flag IsOutputLow()                                                                         { return fOutputLow; }
  inline flag IsInputHigh()                                                                         { return pConnection->iCountLow == 0; }
  inline flag IsInputLow()                                                                          { return pConnection->iCountLow != 0; }
  inline flag IsInputWithoutThisHigh()                                                              { assert(fOutputLow == 0 || fOutputLow == 1); return pConnection->iCountLow - fOutputLow == 0; }
  inline flag IsInputWithoutThisLow()                                                               { assert(fOutputLow == 0 || fOutputLow == 1); return pConnection->iCountLow - fOutputLow != 0; }

  // connect two lines or two groups of lines
  global void ConnectTo(Line& OtherLine);
  global void Disconnect();
  inline flag IsConnected()                                                                         { return pConnection->pFirstLine->pNextLine != NULL; }
  inline flag IsConnectedTo(Line& OtherLine)                                                        { return pConnection == OtherLine.pConnection; }

  // check the line and its connections for errors
  global void AssertValid();

};
