// **********************************************
// File: DATABASE.CPP
// Database management module

#include "muzika.h"
#include <dir.h>
#include <string.h>

BOOL melodyExists = FALSE;        // Becomes TRUE as soon as a melody is loaded

// **********************************************
// Following are the various classes' member function definitions.

// **********************************************
// Melody::Melody is the default constructor of the Melody class.

Melody :: Melody() {}             // No default constructor for Melody

// **********************************************
// Melody::~Melody is the default destructor of the Melody class.

Melody :: ~Melody()
{
  // Destroy the melody parts one by one
  while (part.number())
    part.destroyAt(part.number()-1);
}

// **********************************************
// MelodyParameters::LoadFrom loads the melody parameters from a file,
// returning TRUE after success, otherwise calling the LoadError function.

BOOL MelodyParameters :: LoadFrom(istream &in, void (*LoadError)())
{
  // Read the staff width from the file
  in.read((char *) &staffWidth, sizeof(staffWidth));

  if (in.gcount() != sizeof(staffWidth)) {
    // An error occured: call the LoadError function
    (*LoadError)();
    return FALSE;
  }

  return TRUE;
}

// **********************************************
// MelodyParameters::printOn saves the melody parameters in a file.

void MelodyParameters :: printOn(ostream &out)
{
  // Write the staff width on the stream
  out.write((char *) &staffWidth, sizeof(staffWidth));
}

// **********************************************
// Melody::LoadFrom loads an entire melody from a file, using
// the Part class's LoadFrom function to load the parts one by one.

BOOL Melody :: LoadFrom(istream &in, void (*LoadError)())
{
  // Load the melody header
  if (in.get() != MELODY) {
    (*LoadError)();
    return FALSE;
  }

  // Load the melody parameters
  if (!MelodyParameters :: LoadFrom(in, LoadError))
    return FALSE;

  // Load the melody parts one by one
  int i = 0;
  while (!in.eof() && in.peek() > MELODY) {
    Part *p;
    part.insertAt(*(p = new Part), i++);
    if (!p->LoadFrom(in, LoadError))
      return FALSE;
  }

  return TRUE;
}

// **********************************************
// Melody::printOn saves an entire melody in a file, using
// the IndexedList class's printOn function which saves all parts.

void Melody :: printOn(ostream &out)
{
  // Write an ID mark
  out.put(MELODY);

  // Write the melody parameters
  MelodyParameters :: printOn(out);

  // Write the contents of the various parts
  part.printOn(out);
}

// **********************************************
// Part::Part is the default constructor of the Part class.

Part :: Part() {}                 // No default constructor for Part

// **********************************************
// Part::Part constructs an empty part
// given its name and staff multiplicity.

Part :: Part(const char *name, int multiplicity)
{
  SetPartY(0);
  strcpy(_name, name);
  _multiplicity = multiplicity;
}

// **********************************************
// Part::~Part is the default destructor of the Part class.

Part :: ~Part()
{
  // Destroy the part staves one by one
  while (staff.number())
    staff.destroyAt(staff.number()-1);
}

// **********************************************
// PartParameters::LoadFrom loads the part parameters from a file,
// returning TRUE after success, otherwise calling the LoadError function.

BOOL PartParameters :: LoadFrom(istream &in, void (*LoadError)())
{
  // Read the part name
  in.read(_name, MAXPARTNAME+1);
  if (in.gcount() != MAXPARTNAME+1) {
    (*LoadError)();
    return FALSE;
  }

  // Read the part staff multiplicity
  in.read((char *) &_multiplicity, sizeof(_multiplicity));
  if (in.gcount() != sizeof(_multiplicity)) {
    (*LoadError)();
    return FALSE;
  }

  // Set the part initial Y as 0 by default
  SetPartY(0);
  return TRUE;
}

// **********************************************
// PartParameters::printOn saves the part parameters in a file.

void PartParameters :: printOn(ostream &out)
{
  // Write the part parameters (name and multiplicity) to the stream
  out.write(_name, MAXPARTNAME+1);
  out.write((char *) &_multiplicity, sizeof(_multiplicity));
}

// **********************************************
// Part::LoadFrom loads an entire part from a file, using the Staff class's
// LoadFrom function to load the staves one by one.

BOOL Part :: LoadFrom(istream &in, void (*LoadError)())
{
  // Load the part header
  if (in.get() != PART) {
    (*LoadError)();
    return FALSE;
  }
  if (!PartParameters :: LoadFrom(in, LoadError))
    return FALSE;

  // Load the part staves one by one
  int i = 0;
  while (!in.eof() && in.peek() > PART) {
    Staff *s;
    staff.insertAt(*(s = new Staff), i++);
    if (!s->LoadFrom(in, LoadError))
      return FALSE;
  }

  return TRUE;
}

// **********************************************
// Part::printOn saves an entire part on a file, using the
// IndexedList class's printOn function to save all the staves.

void Part :: printOn(ostream &out) const
{
  out.put(PART);
  PartParameters :: printOn(out);
  staff.printOn(out);
}

// **********************************************
// Staff::Staff is the default constructor of the Staff class.

Staff :: Staff() {}               // No default constructor for Staff

// **********************************************
// Staff:Staff constructs an empty staff given its Y location.

Staff :: Staff(int Y)
{
  _X = 32;
  _Y = Y;
  _width = melody.GetStaffWidth();
}

// **********************************************
// Staff::~Staff is the default destructor of the Staff class.

Staff :: ~Staff()
{
  // Destroy the list of the point objects
  while (pointObject.number())
    pointObject.destroyAt(pointObject.number()-1);

  // Destroy the list of the continuous objects
  while (continuousObject.number())
    continuousObject.destroyAt(continuousObject.number()-1);
}

// **********************************************
// StaffParameters::LoadFrom loads the staff parameters from a file.

BOOL StaffParameters :: LoadFrom(istream &in, void (*LoadError)())
{
  // Load the staff X location
  in.read((char *) &_X, sizeof(_X));
  if (in.gcount() != sizeof(_X)) {
    (*LoadError)();
    return FALSE;
  }

  // Load the staff Y location
  in.read((char *) &_Y, sizeof(_Y));
  if (in.gcount() != sizeof(_Y)) {
    (*LoadError)();
    return FALSE;
  }

  // Load the staff width
  in.read((char *) &_width, sizeof(_width));
  if (in.gcount() != sizeof(_width)) {
    (*LoadError)();
    return FALSE;
  }

  return TRUE;
}

// **********************************************
// StaffParameters::printOn saves the staff parameters in a file.

void StaffParameters :: printOn(ostream &out)
{
  // Write the staff parameters to the stream
  out.write((char *) &_X, sizeof(_X));
  out.write((char *) &_Y, sizeof(_Y));
  out.write((char *) &_width, sizeof(_width));
}

// **********************************************
// Staff::LoadFrom loads an entire staff from a file, using the
// PointObject and ContinuousObject classes' LoadFrom virtual functions.

BOOL Staff :: LoadFrom(istream &in, void (*LoadError)())
{
  // Load the staff header
  if (in.get() != STAFF) {
    (*LoadError)();
    return FALSE;
  }

  // Load the staff parameters
  if (!StaffParameters :: LoadFrom(in, LoadError))
    return FALSE;

  // Load the staff objects themselves
  int p = 0, c = 0;
  int type;
  MusicalObject *m;

  // For every object loaded, insert it in the appropriate list
  while (!in.eof() && in.peek() > STAFF)
    if ((m = LoadObject(in, LoadError, &type)) != NULL)
      switch(type) {
        case POINTOBJECT:
          pointObject.insertAt(*m, p++);
          break;

        case CONTINUOUSOBJECT:
          continuousObject.insertAt(*m, c++);
          break;
      }
    else
      return FALSE;

  return TRUE;
}

// **********************************************
// Staff::printOn saves an entire staff in a file, using the
// IndexedList class's printOn function to save all the objects
// contained in the staff.

void Staff :: printOn(ostream &out) const
{
  out.put(STAFF);
  StaffParameters :: printOn(out);
  pointObject.printOn(out);
  continuousObject.printOn(out);
}

Melody melody;                    // The actual melody object
