/***********************************************************************

  Copyright by Telelogic AB 1998

  This Program is owned by Telelogic and is protected by national
  copyright laws and international copyright treaties. Telelogic grants
  you the right to use this Program on one computer or in one local
  computer network at any one time.

  Under this License you may only modify the source code for the purpose
  of adapting it to your environment. You must reproduce and include any
  copyright and trademark notices on all copies of the source code.  You
  may not use, copy, merge, modify or transfer the Program except as
  provided in this License.

  Telelogic does not warrant that the Program will meet your
  requirements or that the operation of the Program will be
  uninterrupted and error free. You are solely responsible that the
  selection of the Program and the modification of the source code will
  achieve your intended results and that the results are actually
  obtained.

************************************************************************/

#include "omaccess.h"


////////////////////////////////////////////////////////////
// Support methods

void GetSuperClassList (const OMModule &omModule,
                        const string &className,
                        list<string> *superClasses)
{
  for (list<Generalization>::const_iterator i = omModule.generalizationList.begin();
       i != omModule.generalizationList.end();
       i++)
  {
    const Generalization &omGeneralization = *i;
    if (omGeneralization.subtype == className) 
      superClasses->push_back(omGeneralization.supertype);
  }
}

void GetSubClassList (const OMModule &omModule,
                      const string &className,
                      list<string> *subClasses)
{
  for (list<Generalization>::const_iterator i = omModule.generalizationList.begin();
       i != omModule.generalizationList.end();
       i++)
  {
    const Generalization &omGeneralization = *i;
    if (omGeneralization.supertype == className) 
      subClasses->push_back(omGeneralization.subtype);
  }
}

string SourceAggregationEnd(const AssociationClass &association)
{
  return association.fromEnd.type;
}

string TargetAggregationEnd(const AssociationClass &association)
{
  return association.toEnd.type;
}

void GetEndPoints(const AssociationClass &association, list<string> *classes)
{
  classes->push_back(association.fromEnd.type);
  classes->push_back(association.toEnd.type);
}

bool hasEndPoint(const AssociationClass &association, const string &name)
{
  if (association.fromEnd.type==name || association.toEnd.type==name)
    return true;
  else
    return false;
}

void GetAggregations(const OMModule &omModule,
                     list<AssociationClass> *aggregations)
{
  for (list<AssociationClass>::const_iterator ai = omModule.associationList.begin();
       ai != omModule.associationList.end();
       ai++)
  {
    const AssociationClass &association = *ai;
    if (association.isAggregation)
      aggregations->push_back(association);
  }
}

void GetAggregations(const OMModule &omModule,
                     list<AssociationClass> *aggregations,
                     const string &owner)
{
  for (list<AssociationClass>::const_iterator ai = omModule.associationList.begin();
       ai != omModule.associationList.end();
       ai++)
  {
    const AssociationClass &association = *ai;
    if (association.isAggregation &&
        SourceAggregationEnd(association) == owner)
      aggregations->push_back(association);
  }
}

void GetAssociations(const OMModule &omModule,
                     list<AssociationClass> *associations)
{
  for (list<AssociationClass>::const_iterator ai = omModule.associationList.begin();
       ai != omModule.associationList.end();
       ai++)
  {
    const AssociationClass &association = *ai;
    if (!association.isAggregation)
      associations->push_back(association);
  }
}

void GetAssociations(const OMModule &omModule,
                     list<AssociationClass> *associations,
                     const string &firstEnd)
{
  list<AssociationClass> tmplist;
  GetAssociations(omModule, associations);
    
  for (list<AssociationClass>::iterator ai = tmplist.begin();
       ai != tmplist.end();
       ai++)
  {
    const AssociationClass &association = *ai;
    if (!association.isAggregation && hasEndPoint(association, firstEnd))
      associations->push_back(association);
  }
}

void GetAssociations(const OMModule &omModule, list<AssociationClass> *associations,
                     const string &firstEnd, const string &secondEnd)
{
  list<AssociationClass> tmplist;
  GetAssociations(omModule, associations);
    
  for (list<AssociationClass>::iterator ai = tmplist.begin();
       ai != tmplist.end();
       ai++)
  {
    const AssociationClass &association = *ai;
    if (! association.isAggregation &&
        hasEndPoint(association, firstEnd) && hasEndPoint(association, secondEnd))
      associations->push_back(association);
  }
}

////////////////////////////////////////////////////////////
// Misc methods

bool _getBufID(const char* bufferId, OMModule *omModule, string *status)
{
  OMAccessTool omTool;
  if (! omTool.RPCStart ()) {
    if(status)
      *status=omTool.GetErrorText();
    return false;
  }

  if (! omTool.RPCGetObjects (bufferId, omModule)) {
    if(status)
      *status=omTool.GetErrorText();
    return false;
  }
  return true;
}

  
bool GetBufID(const char* bufferId, OMModule *omModule, string* status)
{
  PMConnection pmConnection;
  if (! pmConnection.Open(status)) {
    return false;
  }
   
  OMEditorTool omeTool;
  if (! omeTool.RPCStart ()) {
    if (status)
      *status=omeTool.GetErrorText();
    
    return false;
  }

  return _getBufID(bufferId, omModule, status);
} 

bool GetFile(const string &filename, OMModule *omModule, string *status)
{
  PMConnection pmConnection;
  if (! pmConnection.Open(status)) {
    return false;
  }
   
  OMEditorTool omeTool;
  if (! omeTool.RPCStart ()) {
    if(status)
      *status=omeTool.GetErrorText();
    return false;
  }

  int omBufferId=0;

  if (! omeTool.RPCLoad (filename, &omBufferId)) {
    if(status)
      *status=omeTool.GetErrorText();
    return false;
  }

  // Calculate OM diagram bufferId as string
  ostrstream s;
  s << omBufferId << ends;

  char *bufid=s.str();
  bool success=_getBufID(bufid, omModule, status);
  delete[] bufid;
  
  return success;
}

////////////////////////////////////////////////////////////
//  Code Generators

void trClass(const Class *omClass, ostream &os, string prefix="")
{
  char *tab="  ";
  os << prefix << tab << "Class: " << omClass->name << "\n";

  os << prefix << tab << tab << "Stereotype: " << omClass->stereotype << "\n";
  os << prefix << tab << tab << "Properties: " << omClass->property << "\n";

  // List attributes
  for (list<Attribute>::const_iterator ai = omClass->attributeList.begin();
       ai != omClass->attributeList.end();
       ai++)
  {
    const Attribute &omAttribute = *ai;
    os << prefix << tab << tab << "Attribute: " << omAttribute.name 
       << " Visibility: " << omAttribute.visibility
       << " Type: " << omAttribute.type
       << " Initial value: " << omAttribute.initialValue << "\n";
  }

  // List operations
  for (list<Operation>::const_iterator oi = omClass->operationList.begin();
       oi != omClass->operationList.end();
       oi++)
  {
    const Operation &omOperation = *oi;
    os << prefix << tab << tab << "Operation: " << omOperation.name 
       << " Visibility: " << omOperation.visibility
       << " Returns: " << omOperation.returnType <<  "\n";
    for (list<Parameter>::const_iterator pi = omOperation.parameterList.begin();
         pi != omOperation.parameterList.end();
         pi++)
    {
      const Parameter &omParameter = *pi;
      os << prefix << tab << tab << tab << "Parameter: " <<
        omParameter.name << " " << "Type: " << omParameter.type << "\n";
    }
  }
}

void TraceModule (const OMModule &omModule, ostream &os) {
  const char *tab = "  ";
  
  os << "Classes:\n";
  for (list<Class>::const_iterator ci = omModule.classList.begin();
       ci != omModule.classList.end();
       ci++)
  {
    const Class &omClass = *ci;
    trClass(&omClass, os);
  }

  os << "Generalizations:\n";
  for (list<Generalization>::const_iterator i =
         omModule.generalizationList.begin();
       i != omModule.generalizationList.end();
       i++)
  {
    const Generalization &omGeneralization = *i;
    os << tab << omGeneralization.subtype << " inherits from "
       << omGeneralization.supertype << " Discriminator: "
       << omGeneralization.discriminator << "\n";
  }

  os << "Aggregations:\n";
  list<AssociationClass> aglist;
  GetAggregations(omModule, &aglist);
  for (list<AssociationClass>::iterator ai = aglist.begin();
       ai != aglist.end();
       ai++)
  {
    const AssociationClass &aggregation = *ai;
    os << tab <<  SourceAggregationEnd(aggregation) << " contains/owns " 
       << TargetAggregationEnd(aggregation) << "\n";

    os << tab << tab << "Endpoint: " << aggregation.fromEnd.type << endl;
    os << tab << tab << tab
           << "Name: " << aggregation.fromEnd.name << endl;
    os << tab << tab << tab 
       << "Rolename: " << aggregation.fromEnd.roleName << endl;
    os << tab << tab << tab
       << "Multiplicity: " << aggregation.fromEnd.multiplicity << endl;
    os << tab << tab << tab
       << "isOrdered: " << aggregation.fromEnd.isOrdered << endl;
    os << tab << tab << tab
       << "isSorted: " << aggregation.fromEnd.isSorted << endl;
    os << tab << tab << tab
       << "aggregation: " << aggregation.fromEnd.aggregation << endl;

    os << tab << tab << "Endpoint: " << aggregation.toEnd.type << endl;
    os << tab << tab << tab
       << "Name: " << aggregation.toEnd.name << endl;;
    os << tab << tab << tab
       << "Rolename: " << aggregation.toEnd.roleName << endl;
    os << tab << tab << tab
       << "Multiplicity: " << aggregation.toEnd.multiplicity << endl;
    os << tab << tab << tab
       << "isOrdered: " << aggregation.toEnd.isOrdered << endl;
    os << tab << tab << tab
       << "isSorted: " << aggregation.toEnd.isSorted << endl;
    os << tab << tab << tab
       << "aggregation: " << aggregation.toEnd.aggregation << endl;

    if (aggregation.hasAssociationClass)
    {
      string prefix(tab);
      prefix+=tab;
      
      os << tab << tab << "hasAssociationClass: " << 
        aggregation.hasAssociationClass << endl;
      trClass(&aggregation, os, prefix);
    }

  }

  os << "Associations:\n";
  list<AssociationClass> aslist;
  GetAssociations(omModule, &aslist);
  for (list<AssociationClass>::iterator asi = aslist.begin();
       asi != aslist.end();
       asi++)
  {
    const AssociationClass &association = *asi;
    os << tab << association.fromEnd.type << " is associated with "
       << association.toEnd.type << "\n";

    os << tab << tab << "Endpoint: " << association.fromEnd.type << endl;
    os << tab << tab << tab << "Rolename: " 
       << association.fromEnd.roleName << endl;
    os << tab << tab << tab
       << "Multiplicity: " << association.fromEnd.multiplicity << endl;
    os << tab << tab << tab
           << "Name: " << association.fromEnd.name << endl;
    os << tab << tab << tab
       << "isOrdered: " << association.fromEnd.isOrdered << endl;
    os << tab << tab << tab
       << "isSorted: " << association.fromEnd.isSorted << endl;
    os << tab << tab << tab
       << "aggregation: " << association.fromEnd.aggregation << endl;

    os << tab << tab << "Endpoint: " << association.toEnd.type  << endl;
    os << tab << tab << tab
       << "Rolename: " << association.toEnd.roleName << endl;
    os << tab << tab << tab
       << "Multiplicity: " << association.toEnd.multiplicity << endl;
    os << tab << tab << tab
       << "Name: " << association.toEnd.name << endl;;
    os << tab << tab << tab
       << "isOrdered: " << association.toEnd.isOrdered << endl;
    os << tab << tab << tab
       << "isSorted: " << association.toEnd.isSorted << endl;
    os << tab << tab << tab
       << "aggregation: " << association.toEnd.aggregation << endl;

    if (association.hasAssociationClass)
    {
      string prefix(tab);
      prefix+=tab;
      
      os << tab << tab << "hasAssociationClass: " << 
        association.hasAssociationClass << endl;
      trClass(&association, os, prefix);
    }
  }
}


bool OMAccessTool::RPCGetObjects (const string &module, OMModule *omModule){
  MessagePacker packer;
  packer.PackString (module);
  packer.PackInteger (OMINFOVERSION);

  string result;
  if (! RPCSendMessage (SEGETOBJECTS, packer.GetMessage(), &result))
    return false;

  MessageUnpacker unpacker(result);
  unpacker.UnpackReplyStatus ();
  
  int version=0;
  unpacker.UnpackInteger(&version);
  if (version != OMINFOVERSION) {
    cout << "Wrong version: " << version
         << " expected: " << OMINFOVERSION << "\n";
    return false;
  }

  int classCount = 0;
  unpacker.UnpackInteger (&classCount);

  int i;
  for (i = 0; i < classCount; i++)
  {
    string className;
    unpacker.UnpackString (&className);
    Class &omClass = omModule->AddClass (className);

    unpacker.UnpackString (&(omClass.stereotype));
    unpacker.UnpackString (&(omClass.property));
    
    // Attributes
    int attributeCount = 0;
    unpacker.UnpackInteger (&attributeCount);
    for (int ai = 0; ai < attributeCount; ai++)
    {
      Attribute omAttribute;
      unpacker.UnpackString (&(omAttribute.name));
      int tmpInt;
      unpacker.UnpackInteger (&(tmpInt));
      omAttribute.visibility = (VisibilityKind)tmpInt;
      unpacker.UnpackString (&(omAttribute.type));
      unpacker.UnpackString (&(omAttribute.initialValue));
      unpacker.UnpackString (&(omAttribute.note));

      omClass.attributeList.push_back(omAttribute);
    }
    
    int operationCount = 0;
    unpacker.UnpackInteger (&operationCount);
    for (int oi = 0; oi < operationCount; oi++)
    {
      Operation omOperation;

      unpacker.UnpackString (&(omOperation.name));
      int tmpInt;
      unpacker.UnpackInteger (&(tmpInt));
      omOperation.visibility = (VisibilityKind)tmpInt;
      unpacker.UnpackString (&(omOperation.returnType));
      unpacker.UnpackString (&(omOperation.note));

      // Not supported
      // unpacker.UnpackInteger (&(omOperation.suffix));

      int parameterCount = 0;
      unpacker.UnpackInteger (&parameterCount);
      for (int pi = 0; pi < parameterCount; pi++) {
        Parameter omParameter;
        unpacker.UnpackString (&(omParameter.name));
        unpacker.UnpackString (&(omParameter.type));
        omOperation.parameterList.push_back (omParameter);
      }

      omClass.operationList.push_back(omOperation);
    }
  }

  int generalizationCount = 0;
  unpacker.UnpackInteger (&generalizationCount);
  for (i = 0; i < generalizationCount; i++) {
    Generalization generalization;
    unpacker.UnpackString (&(generalization.discriminator));
    unpacker.UnpackString (&(generalization.supertype));
    unpacker.UnpackString (&(generalization.subtype));
    omModule->generalizationList.push_back (generalization);
  }

  int associationsCount=0;
  unpacker.UnpackInteger (&associationsCount);
  for (i = 0; i < associationsCount; i++) {
    AssociationClass association;
    int tmpInt;

    // unpacker.UnpackString (&(association.Association::name));
    
    unpacker.UnpackBool (&(association.isAggregation));
    unpacker.UnpackBool (&(association.hasAssociationClass));
    
    // fromEnd
    unpacker.UnpackString (&(association.fromEnd.name));
    unpacker.UnpackString (&(association.fromEnd.type));
    unpacker.UnpackString (&(association.fromEnd.roleName));
    unpacker.UnpackString (&(association.fromEnd.multiplicity));
    unpacker.UnpackString (&(association.fromEnd.qualifier));
    unpacker.UnpackString (&(association.fromEnd.constraint));
    unpacker.UnpackBool (&(association.fromEnd.isOrdered));
    unpacker.UnpackBool (&(association.fromEnd.isSorted));
    unpacker.UnpackInteger (&(tmpInt));
    association.fromEnd.aggregation = (AggregationKind)tmpInt;

    // toEnd
    unpacker.UnpackString (&(association.toEnd.name));
    unpacker.UnpackString (&(association.toEnd.type));
    unpacker.UnpackString (&(association.toEnd.roleName));
    unpacker.UnpackString (&(association.toEnd.multiplicity));
    unpacker.UnpackString (&(association.toEnd.qualifier));
    unpacker.UnpackString (&(association.toEnd.constraint));
    unpacker.UnpackBool (&(association.toEnd.isOrdered));
    unpacker.UnpackBool (&(association.toEnd.isSorted));
    unpacker.UnpackInteger (&(tmpInt));
    association.toEnd.aggregation = (AggregationKind)tmpInt;


    // The Association_Class_
    unpacker.UnpackString (&(association.Class::name));

    // Attributes
    int attributeCount = 0;
    unpacker.UnpackInteger (&attributeCount);
    for (int ai = 0; ai < attributeCount; ai++)
    {
      Attribute omAttribute;
      unpacker.UnpackString (&(omAttribute.name));
      int tmpInt;
      unpacker.UnpackInteger (&(tmpInt));
      omAttribute.visibility = (VisibilityKind)tmpInt;
      unpacker.UnpackString (&(omAttribute.type));
      unpacker.UnpackString (&(omAttribute.initialValue));
      unpacker.UnpackString (&(omAttribute.note));

      association.attributeList.push_back(omAttribute);
    }

    // The operations
    int operationCount = 0;
    unpacker.UnpackInteger (&operationCount);
    for (int oi = 0; oi < operationCount; oi++)
    {
      Operation omOperation;

      unpacker.UnpackString (&(omOperation.name));
      int tmpInt;
      unpacker.UnpackInteger (&(tmpInt));
      omOperation.visibility = (VisibilityKind)tmpInt;
      unpacker.UnpackString (&(omOperation.returnType));
      unpacker.UnpackString (&(omOperation.note));
      
      // Not supported
      // unpacker.UnpackInteger (&(omOperation.suffix));
      
      int parameterCount = 0;
      unpacker.UnpackInteger (&parameterCount);
      for (int pi = 0; pi < parameterCount; pi++) {
        Parameter omParameter;
        unpacker.UnpackString (&(omParameter.name));
        unpacker.UnpackString (&(omParameter.type));
        omOperation.parameterList.push_back (omParameter);
      }
      association.operationList.push_back(omOperation);
    }
    
    omModule->associationList.push_back (association);
  }
  return unpacker.GetUnpackStatus(); 
}

void PackOMModule(const OMModule &omModule,
                  int version,
                  MessagePacker &packer)
{
  packer.PackReplyStatus (0);
  packer.PackInteger(OMINFOVERSION);

  // Pack classes
  packer.PackInteger (omModule.classList.size());
  for (list<Class>::const_iterator ci = omModule.classList.begin();
       ci != omModule.classList.end();
       ++ci)
  {
    const Class &omClass = *ci;

    packer.PackString (omClass.name);

    if (version >= OMINFOVERSION34) {
      packer.PackString (omClass.stereotype);
      packer.PackString (omClass.property);  
    }
    
    // List attributes
    packer.PackInteger (omClass.attributeList.size());
    for (list<Attribute>::const_iterator ai = omClass.attributeList.begin();
         ai != omClass.attributeList.end();
         ai++)
    {
      const Attribute &attribute = *ai;
      packer.PackString (attribute.name);
      packer.PackInteger (attribute.visibility);
      packer.PackString (attribute.type);
      packer.PackString (attribute.initialValue);
      packer.PackString (attribute.note);
    }

    // List operations
    packer.PackInteger (omClass.operationList.size());
    for (list<Operation>::const_iterator oi = omClass.operationList.begin();
         oi != omClass.operationList.end();
         oi++)
    {
      const Operation &operation = *oi;
      
      packer.PackString (operation.name);
      packer.PackInteger (operation.visibility);
      packer.PackString (operation.returnType);
      packer.PackString (operation.note);

      // Not supported
      // packer.PackInteger (operation.suffix);
      
      packer.PackInteger (operation.parameterList.size());
      for (list<Parameter>::const_iterator pi = operation.parameterList.begin();
           pi != operation.parameterList.end();
           pi++)
      {
        const Parameter &omParameter = *pi;
        packer.PackString (omParameter.name);
        packer.PackString (omParameter.type);
      } 
    }    
  }

  packer.PackInteger (omModule.generalizationList.size());
  for (list<Generalization>::const_iterator gi = omModule.generalizationList.begin();
       gi != omModule.generalizationList.end();
       ++gi)
  {
    const Generalization &generalization = *gi;
    
    packer.PackString (generalization.discriminator);
    packer.PackString (generalization.supertype);
    packer.PackString (generalization.subtype);
  }

  // pack associations
  packer.PackInteger (omModule.associationList.size());
  for (list<AssociationClass>::const_iterator ai = omModule.associationList.begin();
       ai != omModule.associationList.end();
       ++ai)
  {
    const AssociationClass &assoc = *ai;

    // packer.PackString (assoc.Association::name);
    packer.PackBool (assoc.isAggregation);
    packer.PackBool (assoc.hasAssociationClass);

    // fromEnd
    packer.PackString (assoc.fromEnd.name);
    packer.PackString (assoc.fromEnd.type);
    packer.PackString (assoc.fromEnd.roleName);
    packer.PackString (assoc.fromEnd.multiplicity);
    packer.PackString (assoc.fromEnd.qualifier);
    packer.PackString (assoc.fromEnd.constraint);
    packer.PackBool (assoc.fromEnd.isOrdered);
    packer.PackBool (assoc.fromEnd.isSorted);
    packer.PackInteger (assoc.fromEnd.aggregation);

    // toEnd
    packer.PackString (assoc.toEnd.name);
    packer.PackString (assoc.toEnd.type);
    packer.PackString (assoc.toEnd.roleName);
    packer.PackString (assoc.toEnd.multiplicity);
    packer.PackString (assoc.toEnd.qualifier);
    packer.PackString (assoc.toEnd.constraint);
    packer.PackBool (assoc.toEnd.isOrdered);
    packer.PackBool (assoc.toEnd.isSorted);
    packer.PackInteger (assoc.toEnd.aggregation);
    
    // Class
    packer.PackString (assoc.Class::name);

    // attributes
    packer.PackInteger (assoc.attributeList.size());
    for (list<Attribute>::const_iterator ai = assoc.attributeList.begin();
         ai != assoc.attributeList.end();
         ai++)
    {
      const Attribute &attribute = *ai;
      packer.PackString (attribute.name);
      packer.PackInteger (attribute.visibility);
      packer.PackString (attribute.type);
      packer.PackString (attribute.initialValue);
      packer.PackString (attribute.note);
    }

    // List operations
    packer.PackInteger (assoc.operationList.size());
    for (list<Operation>::const_iterator oi = assoc.operationList.begin();
         oi != assoc.operationList.end();
         oi++)
    {
      const Operation &operation = *oi;
      
      packer.PackString (operation.name);
      packer.PackInteger (operation.visibility);
      packer.PackString (operation.returnType);
      packer.PackString (operation.note);

      // Not supported
      // packer.PackInteger (operation.suffix);
      
      packer.PackInteger (operation.parameterList.size());
      for (list<Parameter>::const_iterator pi = operation.parameterList.begin();
           pi != operation.parameterList.end();
           pi++)
      {
        const Parameter &omParameter = *pi;
        packer.PackString (omParameter.name);
        packer.PackString (omParameter.type);
      } 
    }    
  }
}


// end of file



