//
//----------------------------------------------------------------------------
// ObjectComponents
// (C) Copyright 1994 by Borland International, All Rights Reserved
//
//   Implementation of TOcDocument Class
//----------------------------------------------------------------------------
#include <ocf/ocfpch.h>
#include <ocf/ocstorag.h>
#include <ocf/ocdoc.h>
#include <ocf/ocpart.h>
#include <ocf/ocapp.h>
#include <ocf/ocremvie.h>

DIAG_DECLARE_GROUP(OcRefCount);

const char  DocStreamName[] = "OcDocument";

TOcDocument::TOcDocument(TOcApp& app, const char far* fileName)
:
  OcApp(app),
#if defined(BI_DATA_NEAR)
  Name(*new string(fileName)),
  PartCollection(*new TOcPartCollection),
  ViewCollection(*new TOcViewCollection)
#else
  Name(fileName),
  PartCollection(),
  ViewCollection()
#endif
{
  // client support...
  //
  ActiveView = 0;
  NumViews   = 0;
  PartID     = 0;
  Storage    = 0;

  bool createNew = ToBool(!fileName || !fileName[0]);
  Storage = new TOcStorage(fileName, createNew);
  TRACEX(OcRefCount, 1, "TOcDocument() @" << (void*)this);
}

TOcDocument::TOcDocument(TOcApp& app, const char far* fileName, IStorage far* storageI)
:
  OcApp(app),
#if defined(BI_DATA_NEAR)
  Name(*new string(fileName)),
  PartCollection(*new TOcPartCollection),
  ViewCollection(*new TOcViewCollection)
#else
  Name(fileName),
  PartCollection(),
  ViewCollection()
#endif
{
  // client support...
  //
  ActiveView = 0;
  NumViews   = 0;
  PartID     = 0;
  Storage    = 0;
  OrgStorage = 0;

  if (storageI) // storage already created by application
    Storage = new TOcStorage(storageI);
  else
    Storage = 0; // no storage yet
  TRACEX(OcRefCount, 1, "TOcDocument() @" << (void*)this);
}

TOcDocument::~TOcDocument()
{
  delete Storage;
#if defined(BI_DATA_NEAR)
  delete &PartCollection;
  delete &ViewCollection;
  delete &Name;
#endif
}

//----------------------------------------------------------------------------

//
// Set the storage for this document
//
void
TOcDocument::SetStorage(IStorage* storage, bool remember)
{
  if (Storage && (storage == Storage->GetIStorage()))
    return;

  if (remember) {
    delete Storage;
    OrgStorage = 0;
  }
  else
    OrgStorage = Storage;

  if (storage)
    Storage = new TOcStorage(storage);
  else
    Storage = 0;
}

//
// Set the storage for this document
//
void
TOcDocument::SetStorage(const char far* path)
{
  delete Storage;
  Storage = new TOcStorage(path, false);
  SetName(path);    // new Storage now has path as a name
}

//
// Restore the original root IStorage before the save operation
//
bool
TOcDocument::RestoreStorage()
{
  if (OrgStorage) {
    delete Storage;

    Storage    = OrgStorage;
    OrgStorage = 0;
  }

  return true;
}

//
// Perform saveas operation
//
bool
TOcDocument::SaveToFile(const char far* newName)
{
  PRECONDITION(newName);

  TOcStorage newStorage(newName, true);
  Name = newName;
  return SaveParts(newStorage.GetIStorage(), false);
}

//
// Save the embedded parts to the provided storage. 'remember' flag is not
// really used here, as our save/restore is done outside here.
//
bool
TOcDocument::SaveParts(IStorage* storage, bool sameAsLoaded, bool remember)
{
  if (!Storage)
    return true;

  TOcStorage* oldStorage = 0;

  // If the storage passed is not the same as the one we loaded, wrap the new
  // one & use it in here.
  //
  if (!sameAsLoaded) {
    CHECK(storage);

    // If we are switching storages, make sure parts all all pulled in now
    //
    for (TOcPartCollectionIter i(PartCollection); i; i++) {
      TOcPart& part = *i.Current();
      part.FinishLoading();
    }
    oldStorage = Storage;
    Storage = new TOcStorage(storage);
  }

  // Create a stream for part information
  //
  STATSTG statstg;
  if (!HRSucceeded(Storage->Stat(&statstg, STATFLAG_NONAME)))
    return false;

  TOcStream  stream(*Storage, ::DocStreamName, true, statstg.grfMode);

  // Write TOcDocument data into stream
  //
  ulong count;
  bool  ok;
  ulong value = PartCollection.Count();
  ok = ToBool(HRSucceeded(stream.Write(&PartID, sizeof PartID, &count)) &&
              HRSucceeded(stream.Write(&value, sizeof value, &count)));

  if (ok) {
    for (TOcPartCollectionIter i(PartCollection); i; i++) {
      TOcPart& part = *i.Current();
      int16 len = int16(part.GetName().Length());

      // Write the part name string, pascal style [len]+chars, no 0
      //
      ok = ToBool(HRSucceeded(stream.Write(&len, sizeof len, &count)) &&
                  HRSucceeded(stream.Write((char far*)part.GetName(), len, &count)) &&
                  part.Save(sameAsLoaded, remember));

      if (!ok)
        break;
    }
  }

  // Deal with the alloc'd storage if there was one. Either put things back on
  // failure, or keep the new storage around for a while
  //
  if (!sameAsLoaded) {
    if (!ok) {
      delete Storage;
      Storage = oldStorage;
    }
    else
      delete oldStorage;
  }

  return ok;
}

//
// Loads the parts from the current storage into the PartCollection. Return
// false if a serious error occurs. Having no part stream at all is OK.
//
bool
TOcDocument::LoadParts()
{
  if (!Storage)
    return true;

  // Clear the part collection
  //
  PartCollection.Clear();

  // Open a stream with part information
  //
  STATSTG statstg;
  if (!HRSucceeded(Storage->Stat(&statstg, STATFLAG_NONAME)))
    return false;

  try {
    TOcStream  stream(*Storage, ::DocStreamName, false, statstg.grfMode);

    // Read TOcDocument data from stream. Return false if any of the data
    // is missing--something must have been corrupted.
    //
    if (!HRSucceeded(stream.Read(&PartID, sizeof PartID)))
      return false;

    ulong value;
    if (!HRSucceeded(stream.Read(&value, sizeof value)))
      return false;

    // Rebuild the part collection
    //
    for (int i = (int)value; i; i--) {
      char name[33];
      int16 len;

      // Read the part name string, pascal style [len]+chars, no 0
      //
      if (!HRSucceeded(stream.Read(&len, sizeof len)))
        return false;

      if (len >= sizeof name) // too big for our buffer, how could that happen?
        return false;

      if (!HRSucceeded(stream.Read(name, len)))
        return false;
      name[len] = 0;  // 0 was not written

      new TOcPart(*this, name);
    }
  }
  catch (TXObjComp&) {
    // There is no part related info stream. Thats OK, we then have a document
    // with no parts
  }
  return true;
}

void
TOcDocument::RenameParts(IBRootLinkable far* bLDocumentI)
{
  if (Name.is_null())
    return; // Temporary file does not have moniker

  for (TOcPartCollectionIter i(PartCollection); i; i++) {
    TOcPart& part = *i.Current();

    IBLinkable far* linkI;
    if (HRSucceeded(part.QueryInterface(IID_IBLinkable, &(LPVOID)linkI))) {
      linkI->OnRename(bLDocumentI, part.GetName());
      linkI->Release();
    }
  }
}

void
TOcDocument::Close()
{
  for (TOcPartCollectionIter i(PartCollection); i; i++) {
    TOcPart& part = *i.Current();
    part.Close();
  }

  for (TOcViewCollectionIter j(ViewCollection); j; j++) {
    TOcLinkView& view = *j.Current();
    view.Disconnect();
  }

  ViewCollection.Clear(); // remove the link views now
}

void
TOcDocument::SetActiveView(TOcView* view)
{
  ActiveView = view;
}

//
// Notify container that doc pathname has changed
//
void
TOcDocument::SetName(const string& newName)
{
  Name = newName;

  // create a moniker for this document
  //
  if (!Name.is_null() && ActiveView)
    ActiveView->Rename();
}



