// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <gp_GTrsf.hxx>
#include <IGESData_ColorEntity.hxx>
#include <IGESData_DirChecker.hxx>
#include <IGESData_IGESEntity.hxx>
#include <IGESData_IGESReaderTool.hxx>
#include <IGESData_IGESType.hxx>
#include <IGESData_LabelDisplayEntity.hxx>
#include <IGESData_LevelListEntity.hxx>
#include <IGESData_LineFontEntity.hxx>
#include <IGESData_NameEntity.hxx>
#include <IGESData_SingleParentEntity.hxx>
#include <IGESData_TransfEntity.hxx>
#include <IGESData_ViewKindEntity.hxx>
#include <Interface_EntityIterator.hxx>
#include <Interface_EntityList.hxx>
#include <Interface_InterfaceError.hxx>
#include <MoniTool_Macros.hxx>
#include <Standard_PCharacter.hxx>
#include <Standard_Type.hxx>
#include <TCollection_HAsciiString.hxx>

#include <cstdio>
IMPLEMENT_STANDARD_RTTIEXT(IGESData_IGESEntity, Standard_Transient)

#define ThisEntity occ::down_cast<IGESData_IGESEntity>(This())

namespace
{
static const int IGESFlagAssocs  = 131072;
static const int IGESFlagProps   = 262144;
static const int IGESFourStatus  = 65535;
static const int IGESStatusField = 15;
static const int IGESShiftSubord = 4;
static const int IGESShiftUse    = 8;
static const int IGESShiftHier   = 12;
} // namespace

//=================================================================================================

IGESData_IGESEntity::IGESData_IGESEntity()
    : theType(0),
      theForm(0),
      theDefLevel(0),
      theStatusNum(0),
      theLWeightNum(0),
      theLWeightVal(0.0),
      theSubScriptN(0)
{
  theRes1[0] = theRes2[0] = '\0';
}

//=================================================================================================

void IGESData_IGESEntity::Clear()
{
  //  Handle and DefSwitch
  theStructure.Nullify();
  theDefLineFont.SetVoid();
  theLineFont.Nullify();
  theDefColor.SetVoid();
  theColor.Nullify();
  theDefLevel = 0;
  theLevelList.Nullify();
  theView.Nullify();
  theTransf.Nullify();
  theLabDisplay.Nullify();
  theSubScriptN = 0;
  theShortLabel.Nullify();

  //  Autres Valeurs, Listes
  theType = theForm = 0;
  theStatusNum = theLWeightNum = 0;
  theLWeightVal                = 0.;
  //  theRes1[0]    = theRes2[0]    = '\0';
  theProps.Clear();
  theAssocs.Clear();
}

//  #########################################################################
//  ....                Definition IGES : Directory Entry                ....

IGESData_IGESType IGESData_IGESEntity::IGESType() const
{
  return IGESData_IGESType(theType, theForm);
}

int IGESData_IGESEntity::TypeNumber() const
{
  return theType;
}

int IGESData_IGESEntity::FormNumber() const
{
  return theForm;
}

occ::handle<IGESData_IGESEntity> IGESData_IGESEntity::DirFieldEntity(const int num) const
{
  occ::handle<IGESData_IGESEntity> ent;
  if (num == 3)
    ent = theStructure;
  if (num == 4)
    ent = theLineFont;
  if (num == 5)
    ent = theLevelList;
  if (num == 6)
    ent = theView;
  if (num == 7)
    ent = theTransf;
  if (num == 8)
    ent = theLabDisplay;
  if (num == 13)
    ent = theColor;
  return ent;
}

bool IGESData_IGESEntity::HasStructure() const
{
  return (!theStructure.IsNull());
}

occ::handle<IGESData_IGESEntity> IGESData_IGESEntity::Structure() const
{
  return theStructure;
}

IGESData_DefType IGESData_IGESEntity::DefLineFont() const
{
  return theDefLineFont.DefType();
}

int IGESData_IGESEntity::RankLineFont() const
{
  return theDefLineFont.Value();
}

occ::handle<IGESData_LineFontEntity> IGESData_IGESEntity::LineFont() const
{
  return GetCasted(IGESData_LineFontEntity, theLineFont);
}

IGESData_DefList IGESData_IGESEntity::DefLevel() const
{
  if (theDefLevel > 0)
    return IGESData_DefOne;
  if (theDefLevel < 0)
    return IGESData_DefSeveral;
  return IGESData_DefNone;
}

int IGESData_IGESEntity::Level() const
{
  return theDefLevel;
}

occ::handle<IGESData_LevelListEntity> IGESData_IGESEntity::LevelList() const
{
  return GetCasted(IGESData_LevelListEntity, theLevelList);
}

IGESData_DefList IGESData_IGESEntity::DefView() const
{
  if (View().IsNull())
    return IGESData_DefNone;
  if (View()->IsSingle())
    return IGESData_DefOne;
  else
    return IGESData_DefSeveral;
}

occ::handle<IGESData_ViewKindEntity> IGESData_IGESEntity::View() const
{
  return GetCasted(IGESData_ViewKindEntity, theView);
}

occ::handle<IGESData_ViewKindEntity> IGESData_IGESEntity::SingleView() const
{
  occ::handle<IGESData_ViewKindEntity> nulvue;
  if (DefView() != IGESData_DefOne)
    return nulvue;
  return View();
}

occ::handle<IGESData_ViewKindEntity> IGESData_IGESEntity::ViewList() const
{
  occ::handle<IGESData_ViewKindEntity> nulvue;
  if (DefView() != IGESData_DefSeveral)
    return nulvue;
  return View();
}

bool IGESData_IGESEntity::HasTransf() const
{
  return (!theTransf.IsNull());
}

occ::handle<IGESData_TransfEntity> IGESData_IGESEntity::Transf() const
{
  return GetCasted(IGESData_TransfEntity, theTransf);
}

bool IGESData_IGESEntity::HasLabelDisplay() const
{
  return (!theLabDisplay.IsNull());
}

occ::handle<IGESData_LabelDisplayEntity> IGESData_IGESEntity::LabelDisplay() const
{
  return GetCasted(IGESData_LabelDisplayEntity, theLabDisplay);
}

// Status : an Integer for BlankStatus,SubordinateStatus,UseFlag,HierarchySt.
// Division : 4 bits each (BlankStatus on the right, etc)

int IGESData_IGESEntity::BlankStatus() const
{
  return (theStatusNum & IGESStatusField);
}

int IGESData_IGESEntity::SubordinateStatus() const
{
  return ((theStatusNum >> IGESShiftSubord) & IGESStatusField);
}

int IGESData_IGESEntity::UseFlag() const
{
  return ((theStatusNum >> IGESShiftUse) & IGESStatusField);
}

int IGESData_IGESEntity::HierarchyStatus() const
{
  return ((theStatusNum >> IGESShiftHier) & IGESStatusField);
}

int IGESData_IGESEntity::LineWeightNumber() const
{
  return theLWeightNum;
}

double IGESData_IGESEntity::LineWeight() const
{
  return theLWeightVal;
}

IGESData_DefType IGESData_IGESEntity::DefColor() const
{
  return theDefColor.DefType();
}

int IGESData_IGESEntity::RankColor() const
{
  return theDefColor.Value();
}

occ::handle<IGESData_ColorEntity> IGESData_IGESEntity::Color() const
{
  return GetCasted(IGESData_ColorEntity, theColor);
}

//=================================================================================================

bool IGESData_IGESEntity::CResValues(const char* res1, const char* res2) const
{
  bool                res = false;
  Standard_PCharacter pres1, pres2;
  //
  pres1 = (Standard_PCharacter)res1;
  pres2 = (Standard_PCharacter)res2;
  //
  for (int i = 0; i < 8; i++)
  {
    pres1[i] = theRes1[i];
    pres2[i] = theRes2[i];
    if (theRes1[i] > ' ' || theRes2[i] > ' ')
    {
      res = true;
    }
  }
  pres1[8] = '\0';
  pres2[8] = '\0';
  //
  return res;
}

bool IGESData_IGESEntity::HasShortLabel() const
{
  return (!theShortLabel.IsNull());
}

occ::handle<TCollection_HAsciiString> IGESData_IGESEntity::ShortLabel() const
{
  return theShortLabel;
}

bool IGESData_IGESEntity::HasSubScriptNumber() const
{
  return (theSubScriptN >= 0);
} // =0 nul mais defini, <0 absent

int IGESData_IGESEntity::SubScriptNumber() const
{
  if (theSubScriptN < 0)
    return 0;
  return theSubScriptN;
}

//  ....                (Re)Initialisation du Directory                 ....

void IGESData_IGESEntity::InitTypeAndForm(const int typenum, const int formnum)
{
  theType = typenum;
  theForm = formnum;
}

void IGESData_IGESEntity::InitDirFieldEntity(const int                               num,
                                             const occ::handle<IGESData_IGESEntity>& ent)
{
  if (num == 3)
    theStructure = ent;
  if (num == 4)
    theLineFont = ent;
  if (num == 5)
    theLevelList = ent;
  if (num == 6)
    theView = ent;
  if (num == 7)
    theTransf = ent;
  if (num == 8)
    theLabDisplay = ent;
  if (num == 13)
    theColor = ent;
}

void IGESData_IGESEntity::InitTransf(const occ::handle<IGESData_TransfEntity>& ent)
{
  theTransf = ent;
}

void IGESData_IGESEntity::InitView(const occ::handle<IGESData_ViewKindEntity>& ent)
{
  theView = ent;
}

void IGESData_IGESEntity::InitLineFont(const occ::handle<IGESData_LineFontEntity>& ent,
                                       const int                                   rank)
{
  theDefLineFont.SetRank((ent.IsNull() ? rank : -1));
  theLineFont = ent;
}

void IGESData_IGESEntity::InitLevel(const occ::handle<IGESData_LevelListEntity>& ent, const int val)
{
  theLevelList = ent;
  theDefLevel  = (ent.IsNull() ? val : -1);
}

void IGESData_IGESEntity::InitColor(const occ::handle<IGESData_ColorEntity>& ent, const int rank)
{
  theDefColor.SetRank((ent.IsNull() ? rank : -1));
  theColor = ent;
}

void IGESData_IGESEntity::InitStatus(const int blank,
                                     const int subordinate,
                                     const int useflag,
                                     const int hierarchy)
{
  theStatusNum = (theStatusNum & (!IGESFourStatus));
  theStatusNum += (blank & IGESStatusField) | ((subordinate & IGESStatusField) << IGESShiftSubord)
                  | ((useflag & IGESStatusField) << IGESShiftUse)
                  | ((hierarchy & IGESStatusField) << IGESShiftHier);
}

void IGESData_IGESEntity::SetLabel(const occ::handle<TCollection_HAsciiString>& label,
                                   const int                                    sub)
{
  theShortLabel = label;
  theSubScriptN = sub;
}

void IGESData_IGESEntity::InitMisc(const occ::handle<IGESData_IGESEntity>&         str,
                                   const occ::handle<IGESData_LabelDisplayEntity>& lab,
                                   const int                                       weightnum)
{
  theStructure  = str;
  theLabDisplay = lab;
  if (theLWeightNum != 0)
    theLWeightVal *= (weightnum / theLWeightNum);
  else if (weightnum == 0)
    theLWeightVal = 0;
  theLWeightNum = weightnum;
}

//  ....                  Notions derivees importantes                  ....

//  SingleParent : ici on ne traite que l Associativity SingleParent
//  Pour considerer le partage implicite, il faut remonter au Modele ...

bool IGESData_IGESEntity::HasOneParent() const
{
  return (NbTypedProperties(STANDARD_TYPE(IGESData_SingleParentEntity)) == 1);
}

occ::handle<IGESData_IGESEntity> IGESData_IGESEntity::UniqueParent() const
{
  if (NbTypedProperties(STANDARD_TYPE(IGESData_SingleParentEntity)) != 1)
    throw Interface_InterfaceError("IGESEntity : UniqueParent");
  else
  {
    DeclareAndCast(IGESData_SingleParentEntity,
                   PP,
                   TypedProperty(STANDARD_TYPE(IGESData_SingleParentEntity)));
    return PP->SingleParent();
  }
}

gp_GTrsf IGESData_IGESEntity::Location() const
{
  // szv#4:S4163:12Mar99 unreachcble eliminated
  // if (!HasTransf()) return gp_GTrsf();    // Identite
  // else return Transf()->Value();          // c-a-d Compoound
  if (!HasTransf())
    return gp_GTrsf(); // Identite
  occ::handle<IGESData_TransfEntity> trsf = Transf();
  return (trsf.IsNull()) ? gp_GTrsf() : trsf->Value();
}

gp_GTrsf IGESData_IGESEntity::VectorLocation() const
{
  if (!HasTransf())
    return gp_GTrsf();               // Identite
                                     //    Take Location and cancel TranslationPart
  gp_GTrsf loca = Transf()->Value(); // c-a-d Compoound
  loca.SetTranslationPart(gp_XYZ(0., 0., 0.));
  return loca;
}

gp_GTrsf IGESData_IGESEntity::CompoundLocation() const
{
  gp_GTrsf loca = Location();
  if (!HasOneParent())
    return loca;
  gp_GTrsf locp = UniqueParent()->CompoundLocation();
  loca.PreMultiply(locp);
  return loca;
}

bool IGESData_IGESEntity::HasName() const
{
  if (HasShortLabel())
    return true;
  return (NbTypedProperties(STANDARD_TYPE(IGESData_NameEntity)) == 1);
}

occ::handle<TCollection_HAsciiString> IGESData_IGESEntity::NameValue() const
{
  occ::handle<TCollection_HAsciiString> nom; // au depart vide
  //   Question : concatene-t-on le SubScript ?  Oui, forme label(subscript)
  int nbname = NbTypedProperties(STANDARD_TYPE(IGESData_NameEntity));
  if (nbname == 0)
  {
    if (!HasShortLabel())
      return nom;
    if (theSubScriptN < 0)
      return theShortLabel;
    char lenom[50];
    Sprintf(lenom, "%s(%d)", theShortLabel->ToCString(), theSubScriptN);
    nom = new TCollection_HAsciiString(lenom);
  }
  else if (nbname > 0)
  {
    DeclareAndCast(IGESData_NameEntity, name, TypedProperty(STANDARD_TYPE(IGESData_NameEntity), 1));
    nom = name->Value();
  }

  return nom;
}

//  ....            Listes d'infos Optionnelles (Assocs,Props)            ....

bool IGESData_IGESEntity::ArePresentAssociativities() const
{
  if (!theAssocs.IsEmpty())
    return true;
  return (theStatusNum & IGESFlagAssocs) != 0;
}

int IGESData_IGESEntity::NbAssociativities() const
{
  if (theAssocs.IsEmpty())
    return 0;
  return theAssocs.NbEntities();
}

Interface_EntityIterator IGESData_IGESEntity::Associativities() const
{
  Interface_EntityIterator iter;
  theAssocs.FillIterator(iter);
  return iter;
}

int IGESData_IGESEntity::NbTypedAssociativities(const occ::handle<Standard_Type>& atype) const
{
  return theAssocs.NbTypedEntities(atype);
}

occ::handle<IGESData_IGESEntity> IGESData_IGESEntity::TypedAssociativity(
  const occ::handle<Standard_Type>& atype) const
{
  return GetCasted(IGESData_IGESEntity, theAssocs.TypedEntity(atype));
}

void IGESData_IGESEntity::AddAssociativity(const occ::handle<IGESData_IGESEntity>& ent)
{
  theAssocs.Append(ent);
}

void IGESData_IGESEntity::RemoveAssociativity(const occ::handle<IGESData_IGESEntity>& ent)
{
  theAssocs.Remove(ent);
}

void IGESData_IGESEntity::LoadAssociativities(const Interface_EntityList& list)
{
  theAssocs    = list;
  theStatusNum = (theStatusNum | IGESFlagAssocs);
}

void IGESData_IGESEntity::ClearAssociativities()
{
  theAssocs.Clear();
}

void IGESData_IGESEntity::Associate(const occ::handle<IGESData_IGESEntity>& ent) const
{
  if (!ent.IsNull())
    ent->AddAssociativity(ThisEntity);
}

void IGESData_IGESEntity::Dissociate(const occ::handle<IGESData_IGESEntity>& ent) const
{
  if (!ent.IsNull())
    ent->RemoveAssociativity(ThisEntity);
}

bool IGESData_IGESEntity::ArePresentProperties() const
{
  if (!theProps.IsEmpty())
    return true;
  return (theStatusNum & IGESFlagProps) != 0;
}

int IGESData_IGESEntity::NbProperties() const
{
  if (theProps.IsEmpty())
    return 0;
  return theProps.NbEntities();
}

Interface_EntityIterator IGESData_IGESEntity::Properties() const
{
  Interface_EntityIterator iter;
  theProps.FillIterator(iter);
  return iter;
}

int IGESData_IGESEntity::NbTypedProperties(const occ::handle<Standard_Type>& atype) const
{
  return theProps.NbTypedEntities(atype);
}

occ::handle<IGESData_IGESEntity> IGESData_IGESEntity::TypedProperty(
  const occ::handle<Standard_Type>& atype,
  const int                         anum) const
{
  return GetCasted(IGESData_IGESEntity, theProps.TypedEntity(atype, anum));
}

void IGESData_IGESEntity::AddProperty(const occ::handle<IGESData_IGESEntity>& ent)
{
  theProps.Append(ent);
}

void IGESData_IGESEntity::RemoveProperty(const occ::handle<IGESData_IGESEntity>& ent)
{
  theProps.Remove(ent);
}

void IGESData_IGESEntity::LoadProperties(const Interface_EntityList& list)
{
  theProps     = list;
  theStatusNum = (theStatusNum | IGESFlagProps);
}

void IGESData_IGESEntity::ClearProperties()
{
  theProps.Clear();
}

// ....                     Actions liees au Transfert                     ....

void IGESData_IGESEntity::SetLineWeight(const double defw, const double maxw, const int gradw)
{
  if (theLWeightNum == 0)
    theLWeightVal = defw;
  else if (gradw == 1)
    theLWeightVal = maxw * theLWeightNum;
  else
    theLWeightVal = (maxw * theLWeightNum) / gradw;
}
