/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-  */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef _MDB_
#  include "mdb.h"
#endif

#ifndef _MORK_
#  include "mork.h"
#endif

#ifndef _MORKNODE_
#  include "morkNode.h"
#endif

#ifndef _MORKENV_
#  include "morkEnv.h"
#endif

#ifndef _MORKCURSOR_
#  include "morkCursor.h"
#endif

#ifndef _MORKPORTTABLECURSOR_
#  include "morkPortTableCursor.h"
#endif

#ifndef _MORKSTORE_
#  include "morkStore.h"
#endif

// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789

// ````` ````` ````` ````` `````
// { ===== begin morkNode interface =====

/*public virtual*/ void morkPortTableCursor::CloseMorkNode(
    morkEnv* ev)  // ClosePortTableCursor() only if open
{
  if (this->IsOpenNode()) {
    this->MarkClosing();
    this->ClosePortTableCursor(ev);
    this->MarkShut();
  }
}

/*public virtual*/
morkPortTableCursor::~morkPortTableCursor()  // ClosePortTableCursor() executed
                                             // earlier
{
  CloseMorkNode(mMorkEnv);
}

/*public non-poly*/
morkPortTableCursor::morkPortTableCursor(morkEnv* ev, const morkUsage& inUsage,
                                         nsIMdbHeap* ioHeap, morkStore* ioStore,
                                         mdb_scope inRowScope,
                                         mdb_kind inTableKind,
                                         nsIMdbHeap* ioSlotHeap)
    : morkCursor(ev, inUsage, ioHeap),
      mPortTableCursor_Store(0),
      mPortTableCursor_RowScope((mdb_scope)-1)  // we want != inRowScope
      ,
      mPortTableCursor_TableKind((mdb_kind)-1)  // we want != inTableKind
      ,
      mPortTableCursor_LastTable(0)  // not refcounted
      ,
      mPortTableCursor_RowSpace(0)  // strong ref to row space
      ,
      mPortTableCursor_TablesDidEnd(morkBool_kFalse),
      mPortTableCursor_SpacesDidEnd(morkBool_kFalse) {
  if (ev->Good()) {
    if (ioStore && ioSlotHeap) {
      mCursor_Pos = -1;
      mCursor_Seed = 0;  // let the iterator do its own seed handling
      morkStore::SlotWeakStore(ioStore, ev, &mPortTableCursor_Store);

      if (this->SetRowScope(ev, inRowScope))
        this->SetTableKind(ev, inTableKind);

      if (ev->Good()) mNode_Derived = morkDerived_kPortTableCursor;
    } else
      ev->NilPointerError();
  }
}

NS_IMPL_ISUPPORTS_INHERITED(morkPortTableCursor, morkCursor,
                            nsIMdbPortTableCursor)

morkEnv* morkPortTableCursor::CanUsePortTableCursor(nsIMdbEnv* mev,
                                                    mork_bool inMutable,
                                                    nsresult* outErr) const {
  morkEnv* outEnv = 0;
  morkEnv* ev = morkEnv::FromMdbEnv(mev);
  if (ev) {
    if (IsPortTableCursor())
      outEnv = ev;
    else
      NonPortTableCursorTypeError(ev);
    *outErr = ev->AsErr();
  }
  MORK_ASSERT(outEnv);
  return outEnv;
}

/*public non-poly*/ void morkPortTableCursor::ClosePortTableCursor(
    morkEnv* ev) {
  if (this->IsNode()) {
    mCursor_Pos = -1;
    mCursor_Seed = 0;
    mPortTableCursor_LastTable = 0;
    morkStore::SlotWeakStore((morkStore*)0, ev, &mPortTableCursor_Store);
    morkRowSpace::SlotStrongRowSpace((morkRowSpace*)0, ev,
                                     &mPortTableCursor_RowSpace);
    this->CloseCursor(ev);
    this->MarkShut();
  } else
    this->NonNodeError(ev);
}

// } ===== end morkNode methods =====
// ````` ````` ````` ````` `````

/*static*/ void morkPortTableCursor::NilCursorStoreError(morkEnv* ev) {
  ev->NewError("nil mPortTableCursor_Store");
}

/*static*/ void morkPortTableCursor::NonPortTableCursorTypeError(morkEnv* ev) {
  ev->NewError("non morkPortTableCursor");
}

mork_bool morkPortTableCursor::SetRowScope(morkEnv* ev, mork_scope inRowScope) {
  mPortTableCursor_RowScope = inRowScope;
  mPortTableCursor_LastTable = 0;  // restart iteration of space

  mPortTableCursor_TableIter.CloseMapIter(ev);
  mPortTableCursor_TablesDidEnd = morkBool_kTrue;
  mPortTableCursor_SpacesDidEnd = morkBool_kTrue;

  morkStore* store = mPortTableCursor_Store;
  if (store) {
    morkRowSpace* space = mPortTableCursor_RowSpace;

    if (inRowScope)  // intend to cover a specific scope only?
    {
      space = store->LazyGetRowSpace(ev, inRowScope);
      morkRowSpace::SlotStrongRowSpace(space, ev, &mPortTableCursor_RowSpace);

      // We want mPortTableCursor_SpacesDidEnd == morkBool_kTrue
      // to show this is the only space to be covered.
    } else  // prepare space map iter to cover all space scopes
    {
      morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter;
      rsi->InitRowSpaceMapIter(ev, &store->mStore_RowSpaces);

      space = 0;
      (void)rsi->FirstRowSpace(ev, (mork_scope*)0, &space);
      morkRowSpace::SlotStrongRowSpace(space, ev, &mPortTableCursor_RowSpace);

      if (space)  // found first space in store
        mPortTableCursor_SpacesDidEnd = morkBool_kFalse;
    }

    this->init_space_tables_map(ev);
  } else
    this->NilCursorStoreError(ev);

  return ev->Good();
}

void morkPortTableCursor::init_space_tables_map(morkEnv* ev) {
  morkRowSpace* space = mPortTableCursor_RowSpace;
  if (space && ev->Good()) {
    morkTableMapIter* ti = &mPortTableCursor_TableIter;
    ti->InitTableMapIter(ev, &space->mRowSpace_Tables);
    if (ev->Good()) mPortTableCursor_TablesDidEnd = morkBool_kFalse;
  }
}

mork_bool morkPortTableCursor::SetTableKind(morkEnv* ev,
                                            mork_kind inTableKind) {
  mPortTableCursor_TableKind = inTableKind;
  mPortTableCursor_LastTable = 0;  // restart iteration of space

  mPortTableCursor_TablesDidEnd = morkBool_kTrue;

  morkRowSpace* space = mPortTableCursor_RowSpace;
  if (!space && mPortTableCursor_RowScope == 0) {
    this->SetRowScope(ev, 0);
  }
  this->init_space_tables_map(ev);

  return ev->Good();
}

morkRowSpace* morkPortTableCursor::NextSpace(morkEnv* ev) {
  morkRowSpace* outSpace = 0;
  mPortTableCursor_LastTable = 0;
  mPortTableCursor_SpacesDidEnd = morkBool_kTrue;
  mPortTableCursor_TablesDidEnd = morkBool_kTrue;

  if (!mPortTableCursor_RowScope)  // not just one scope?
  {
    morkStore* store = mPortTableCursor_Store;
    if (store) {
      morkRowSpaceMapIter* rsi = &mPortTableCursor_SpaceIter;

      (void)rsi->NextRowSpace(ev, (mork_scope*)0, &outSpace);
      morkRowSpace::SlotStrongRowSpace(outSpace, ev,
                                       &mPortTableCursor_RowSpace);

      if (outSpace)  // found next space in store
      {
        mPortTableCursor_SpacesDidEnd = morkBool_kFalse;

        this->init_space_tables_map(ev);

        if (ev->Bad()) outSpace = 0;
      }
    } else
      this->NilCursorStoreError(ev);
  }

  return outSpace;
}

morkTable* morkPortTableCursor::NextTable(morkEnv* ev) {
  mork_kind kind = mPortTableCursor_TableKind;

  do  // until spaces end, or until we find a table in a space
  {
    morkRowSpace* space = mPortTableCursor_RowSpace;
    if (mPortTableCursor_TablesDidEnd)  // current space exhausted?
      space = this->NextSpace(ev);      // go on to the next space

    if (space)  // have a space remaining that might hold tables?
    {
#ifdef MORK_BEAD_OVER_NODE_MAPS
      morkTableMapIter* ti = &mPortTableCursor_TableIter;
      morkTable* table =
          (mPortTableCursor_LastTable) ? ti->NextTable(ev) : ti->FirstTable(ev);

      for (; table && ev->Good(); table = ti->NextTable(ev))
#else  /*MORK_BEAD_OVER_NODE_MAPS*/
      mork_tid* key = 0;     // ignore keys in table map
      morkTable* table = 0;  // old value table in the map
      morkTableMapIter* ti = &mPortTableCursor_TableIter;
      mork_change* c = (mPortTableCursor_LastTable)
                           ? ti->NextTable(ev, key, &table)
                           : ti->FirstTable(ev, key, &table);

      for (; c && ev->Good(); c = ti->NextTable(ev, key, &table))
#endif /*MORK_BEAD_OVER_NODE_MAPS*/
      {
        if (table && table->IsTable()) {
          if (!kind || kind == table->mTable_Kind) {
            mPortTableCursor_LastTable = table;  // ti->NextTable() hence
            return table;
          }
        } else
          table->NonTableTypeWarning(ev);
      }
      mPortTableCursor_TablesDidEnd = morkBool_kTrue;  // space is done
      mPortTableCursor_LastTable = 0;  // make sure next space starts fresh
    }

  } while (ev->Good() && !mPortTableCursor_SpacesDidEnd);

  return (morkTable*)0;
}

// { ----- begin table iteration methods -----

// { ===== begin nsIMdbPortTableCursor methods =====

// { ----- begin attribute methods -----
NS_IMETHODIMP
morkPortTableCursor::SetPort(nsIMdbEnv* mev, nsIMdbPort* ioPort) {
  NS_ASSERTION(false, "not implemented");
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
morkPortTableCursor::GetPort(nsIMdbEnv* mev, nsIMdbPort** acqPort) {
  nsresult outErr = NS_OK;
  nsIMdbPort* outPort = 0;
  morkEnv* ev =
      this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if (ev) {
    if (mPortTableCursor_Store)
      outPort = mPortTableCursor_Store->AcquireStoreHandle(ev);
    outErr = ev->AsErr();
  }
  if (acqPort) *acqPort = outPort;
  return outErr;
}

NS_IMETHODIMP
morkPortTableCursor::SetRowScope(nsIMdbEnv* mev,  // sets pos to -1
                                 mdb_scope inRowScope) {
  nsresult outErr = NS_OK;
  morkEnv* ev =
      this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if (ev) {
    mCursor_Pos = -1;

    SetRowScope(ev, inRowScope);
    outErr = ev->AsErr();
  }
  return outErr;
}

NS_IMETHODIMP
morkPortTableCursor::GetRowScope(nsIMdbEnv* mev, mdb_scope* outRowScope) {
  nsresult outErr = NS_OK;
  mdb_scope rowScope = 0;
  morkEnv* ev =
      this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if (ev) {
    rowScope = mPortTableCursor_RowScope;
    outErr = ev->AsErr();
  }
  *outRowScope = rowScope;
  return outErr;
}
// setting row scope to zero iterates over all row scopes in port

NS_IMETHODIMP
morkPortTableCursor::SetTableKind(nsIMdbEnv* mev,  // sets pos to -1
                                  mdb_kind inTableKind) {
  nsresult outErr = NS_OK;
  morkEnv* ev =
      this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if (ev) {
    mCursor_Pos = -1;

    SetTableKind(ev, inTableKind);
    outErr = ev->AsErr();
  }
  return outErr;
}

NS_IMETHODIMP
morkPortTableCursor::GetTableKind(nsIMdbEnv* mev, mdb_kind* outTableKind)
// setting table kind to zero iterates over all table kinds in row scope
{
  nsresult outErr = NS_OK;
  mdb_kind tableKind = 0;
  morkEnv* ev =
      this->CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if (ev) {
    tableKind = mPortTableCursor_TableKind;
    outErr = ev->AsErr();
  }
  *outTableKind = tableKind;
  return outErr;
}
// } ----- end attribute methods -----

// { ----- begin table iteration methods -----
NS_IMETHODIMP
morkPortTableCursor::NextTable(  // get table at next position in the db
    nsIMdbEnv* mev,              // context
    nsIMdbTable** acqTable) {
  nsresult outErr = NS_OK;
  nsIMdbTable* outTable = 0;
  morkEnv* ev =
      CanUsePortTableCursor(mev, /*inMutable*/ morkBool_kFalse, &outErr);
  if (ev) {
    morkTable* table = NextTable(ev);
    if (table && ev->Good()) outTable = table->AcquireTableHandle(ev);

    outErr = ev->AsErr();
  }
  if (acqTable) *acqTable = outTable;
  return outErr;
}

// 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
