#include <MoniTool_DB.ixx>
#include <Standard_Failure.hxx>
#include <TCollection_AsciiString.hxx>
#include <TopoDS_HShape.hxx>
#include <Geom_CartesianPoint.hxx>
#include <Geom2d_CartesianPoint.hxx>

#include <TopAbs.hxx>
//#include <GeomTools.hxx>
#include <TCollection_HAsciiString.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom_Curve.hxx>
#include <Geom_Surface.hxx>
#include <OSD_Timer.hxx>


static  MoniTool_DB  thecur;
static int   iflock = 0;
static char* varloc = NULL;
static char* vardbm = NULL;


static Standard_Boolean stachr = Standard_False;

//static OSD_Timer chrono;
// because merdouille link dynamique, ne creer le static qu au 1er usage
static OSD_Timer& chrono() {
  static OSD_Timer chr;
  return chr;
}


MoniTool_DB::MoniTool_DB ()
{
  thelast = 0;  thelock = thetrace = Standard_False;
}


void  MoniTool_DB::SetSignEntity
  (const Handle(MoniTool_SignText)& asign)
{
  thesignent = asign;
}


MoniTool_DB&  MoniTool_DB::Cur ()
{
  if (!iflock) {
//   en mode WB : lock off, trace off  si DEBUGMODE absent; sinon selon valeur:
//    0 met lock on; 1 lock off trace on; vide : lock off trace off
//   hors WB : lock on, trace off  si DEBUGMODE absent; sinon selon DEBUGMODE
//    0 lock on; 1 lock off trace on; vide : lock off trace off
//  Donc DEBUGMODE si absent passe la main a WB
    iflock = 1;
    varloc = getenv ("WOKHOME");
    vardbm = getenv ("DEBUGMODE");
    Standard_Boolean lock = Standard_False, trace = Standard_False;
    if (!varloc) lock = Standard_True;
    if (vardbm) {
      if      (vardbm[0] == '\0')  lock = Standard_False;
      else if (vardbm[0] == '0')   lock = Standard_True;
      else if (vardbm[0] == '1') { lock = Standard_False; trace = Standard_True; }
    }
    thecur.Lock() = lock;  thecur.Trace() = trace;
  }
  return thecur;
}


void  MoniTool_DB::Clear ()
{
  thesets.Nullify();
  thelast = 0;  therec.Nullify();  thered.Nullify();
}


Standard_Boolean&  MoniTool_DB::Lock  ()
{
  return thelock;
}


Standard_Boolean&  MoniTool_DB::Trace ()
{
  return thetrace;
}


void  MoniTool_DB::Record (const Handle(Standard_Transient)& ent)
{
  therec = ent;  thered.Nullify();
}


void  MoniTool_DB::RecordClear ()
{
  therec.Nullify();  thered.Nullify();
}


void  MoniTool_DB::RemoveSet (const Standard_Integer num)
{
  if (num > 0 && num <= thelast)   {
    thesets->Remove (num);
    thelast --;
  }
}


void  MoniTool_DB::AddFrom (const MoniTool_DB& other)
{
  Standard_Integer i,j,nd,nb = other.NbSets();
  for (i = 1; i <= nb; i ++) {
    Standard_CString name = other.Name(i);
    nd = other.NbData(i);
    Add (name);
    for (j = 1; j <= nd; j ++) {
      AddData (other.Data (i,j) , other.Kind (i,j) , other.DataName(i,j).ToCString() );
    }
  }
}


void  MoniTool_DB::AddSet (const Handle(MoniTool_CaseData)& aset)
{
  if (aset.IsNull()) return;
  if (thesets.IsNull()) thesets = new TColStd_HSequenceOfTransient();
  thesets->Append(aset);
  thelast ++;
  if (thetrace) cout<<"MoniTool_DB, new Set n0 "<<thelast<<"  name:"<<aset->Name()<<endl;
}


void  MoniTool_DB::Add (const Standard_CString name)
{
  if (thelock) return;
  Handle(MoniTool_CaseData) aset = new MoniTool_CaseData ("--DB--",name);
  if (thesets.IsNull()) thesets = new TColStd_HSequenceOfTransient();
  thesets->Append (aset);
  thelast ++;
  if (thetrace) cout<<"MoniTool_DB, new Set n0 "<<thelast<<"  name:"<<name<<endl;
  thered = therec;  therec.Nullify();
}

    void  MoniTool_DB::AddData
  (const Handle(Standard_Transient)& val,
   const Standard_Integer kind, const Standard_CString name)
{
  if (thelock) return;
  Set(thelast)->AddData (val,kind,name);
}

    void  MoniTool_DB::AddRaised (const Standard_CString name)
      {  AddData ( Standard_Failure::Caught(),1, name);  }

    void  MoniTool_DB::AddShape
  (const TopoDS_Shape& sh, const Standard_CString name)
      {  AddData ( new TopoDS_HShape(sh), 4, name);  }

    void  MoniTool_DB::AddXYZ
  (const gp_XYZ& aXYZ, const Standard_CString name)
      {  AddData ( new Geom_CartesianPoint (aXYZ), 5,name);  }

    void  MoniTool_DB::AddXY
  (const gp_XY&  aXY, const Standard_CString name)
      {  AddData ( new Geom2d_CartesianPoint (aXY), 6,name);  }

    void  MoniTool_DB::AddReal
  (const Standard_Real val, const Standard_CString name)
      {  AddData ( new Geom2d_CartesianPoint (val,0.), 8,name);  }

    void  MoniTool_DB::AddReals
  (const Standard_Real v1, const Standard_Real v2, const Standard_CString name)
      {  AddData ( new Geom2d_CartesianPoint (v1,v2), 7,name);  }

    void  MoniTool_DB::AddCPU
  (const Standard_Real lastCPU, const Standard_Real curCPU, const Standard_CString name)
{
  Standard_Real cpu = curCPU;
  if (cpu == 0.) {
    Standard_Real sec;  Standard_Integer i1,i2;
    chrono().Show (sec,i1,i2,cpu);
  }
  cpu = cpu - lastCPU;
  AddData ( new Geom2d_CartesianPoint (cpu,0.), 9,name);
}

    Standard_Real  MoniTool_DB::GetCPU () const
{
  if (!stachr) { chrono().Start(); stachr = Standard_True; }
  Standard_Real sec,cpu;  Standard_Integer i1,i2;
  chrono().Show (sec,i1,i2,cpu);
  return cpu;
}

    Standard_Boolean  MoniTool_DB::LargeCPU
  (const Standard_Real maxCPU,
   const Standard_Real lastCPU, const Standard_Real curCPU) const
{
  Standard_Real cpu = curCPU;
  if (cpu == 0.) {
    Standard_Real sec;  Standard_Integer i1,i2;
    chrono().Show (sec,i1,i2,cpu);
  }
  cpu = cpu - lastCPU;
  return (cpu >= maxCPU);
}

    void  MoniTool_DB::AddGeom
  (const Handle(Standard_Transient)& val, const Standard_CString name)
      {  AddData (val,3,name);  }

    void  MoniTool_DB::AddEntity
  (const Handle(Standard_Transient)& val, const Standard_CString name)
      {  AddData (val,2,name);  }

    void  MoniTool_DB::AddRecorded (const Standard_CString name)
{
  if (!thered.IsNull()) AddEntity (therec,name);
  thered.Nullify();
}

    void  MoniTool_DB::AddText
  (const Standard_CString text, const Standard_CString name)
      {  AddData (new TCollection_HAsciiString(text),10, name);  }

    void  MoniTool_DB::AddInteger
  (const Standard_Integer val, const Standard_CString name)
{
  Standard_Real rval = val;
  AddData ( new Geom2d_CartesianPoint (rval,0.),11,name);
}

    void  MoniTool_DB::AddAny
  (const Handle(Standard_Transient)& val, const Standard_CString name)
      {  AddData (val,0,name);  }

//    	--  Querying

    Standard_Integer  MoniTool_DB::NbSets () const
      {  return thelast;  }

    Handle(MoniTool_CaseData)  MoniTool_DB::Set  (const Standard_Integer num) const
{
  Handle(MoniTool_CaseData) aset;
  if (num < 1 || num > thelast) return aset;
  return Handle(MoniTool_CaseData)::DownCast(thesets->Value(num));
}

    Standard_CString  MoniTool_DB::Name   (const Standard_Integer num) const
{
  if (num < 1 || num > thelast) return "";
  return Set(num)->Name();
}

    Standard_Integer  MoniTool_DB::NameNum
  (const Standard_CString name, const Standard_Integer after) const
{
  for (Standard_Integer i = after+1; i <= thelast; i ++) {
    Handle(MoniTool_CaseData) aset = Set(i);
    if (!strcmp (name,aset->Name()) ) return i;
  }
  return 0;
}

    Standard_Integer  MoniTool_DB::NbData (const Standard_Integer num) const
{
  if (num < 1 || num > thelast) return 0;
  return Set (num)->NbData();
}

    Handle(Standard_Transient)  MoniTool_DB::Data
  (const Standard_Integer num, const Standard_Integer nd) const
{
  Handle(Standard_Transient) val;
  if (num < 1 || num > thelast) return val;
  return Set (num)->Data(nd);
}

    Standard_Integer  MoniTool_DB::Kind
  (const Standard_Integer num, const Standard_Integer nd) const
{
  if (num < 1 || num > thelast) return 0;
  return Set (num)->Kind(nd);
}


static    const TCollection_AsciiString&  nulname ()
{
  static TCollection_AsciiString nuln;
  return nuln;
}

    const TCollection_AsciiString&  MoniTool_DB::DataName
  (const Standard_Integer num, const Standard_Integer nd) const
{
  if (num < 1 || num > thelast) return nulname();
  return Set (num)->Name(nd);
}

    Standard_Integer  MoniTool_DB::DataNum
  (const Standard_Integer num, const Standard_CString name) const
{
  if (num < 1 || num > thelast) return 0;
  return Set (num)->NameNum(name);
}


    TopoDS_Shape  MoniTool_DB::Shape
  (const Standard_Integer num, const Standard_Integer nd) const
{
  TopoDS_Shape nulsh;
  if (num < 1 || num > thelast) return nulsh;
  return Set(num)->Shape(nd);
}

    Standard_Boolean  MoniTool_DB::XYZ
  (const Standard_Integer num, const Standard_Integer nd, gp_XYZ& val) const
{
  if (num < 1 || num > thelast) return Standard_False;
  return Set(num)->XYZ(nd,val);
}

    Standard_Boolean   MoniTool_DB::XY
  (const Standard_Integer num, const Standard_Integer nd, gp_XY& val) const
{
  if (num < 1 || num > thelast) return Standard_False;
  return Set(num)->XY(nd,val);
}

    Standard_Boolean   MoniTool_DB::Reals
  (const Standard_Integer num, const Standard_Integer nd,
   Standard_Real& v1, Standard_Real& v2) const
{
  if (num < 1 || num > thelast) return Standard_False;
  return Set(num)->Reals(nd,v1,v2);
}


    Standard_Boolean   MoniTool_DB::Real
  (const Standard_Integer num, const Standard_Integer nd,
   Standard_Real& val) const
{
  if (num < 1 || num > thelast) return Standard_False;
  return Set(num)->Real(nd,val);
}

    Standard_Boolean   MoniTool_DB::Text
  (const Standard_Integer num, const Standard_Integer nd,
   Standard_CString& text) const
{
  if (num < 1 || num > thelast) return Standard_False;
  return Set(num)->Text(nd,text);
}

    Standard_Boolean   MoniTool_DB::Integer
  (const Standard_Integer num, const Standard_Integer nd,
   Standard_Integer& val) const
{
  if (num < 1 || num > thelast) return Standard_False;
  return Set(num)->Integer (nd,val);
}

//          ######  IMPRESSSION  ######

    void  MoniTool_DB::PrintCPU
  (Standard_OStream& S, const Standard_CString mess,
   const Standard_Real lastCPU, const Standard_Real curCPU) const
{
  Standard_Real cpu = curCPU;
  if (cpu == 0.) {
    Standard_Real sec;  Standard_Integer i1,i2;
    chrono().Show (sec,i1,i2,cpu);
  }
  cpu = cpu - lastCPU;
  S<<"MoniTool_DB : CPU count for "<<mess<<" : "<<cpu<<" seconds"<<endl;
}

    void  MoniTool_DB::Print
  (Standard_OStream& S, const Standard_Integer n1, const Standard_Integer n2) const
{
  Handle(Standard_Transient) nulctx;
  Print (S,nulctx,n1,n2);
}

void  MoniTool_DB::Print (Standard_OStream& S,
                          const Handle(Standard_Transient)& context,
                          const Standard_Integer n1,
                          const Standard_Integer n2) const
{
  Standard_Integer i;
  if        (n1 == 0 && n2 == 0) {
    S<<"MoniTool_DB : Nb Sets = "<<thelast;
    if (thelock) S<<"  (locked)";
    S<<endl;
  }
  else if (n1 > 0) {
    Standard_Integer nn = n2;  if (nn == 0) nn = thelast;
    S<<"MoniTool_DB : Index , Sets ( "<<n1<<" - "<<nn<<" )"<<endl;
    S<<"Num	NbData	Name"<<endl;
    for (i = n1; i <= nn; i ++) {
      S<<i<<"	"<<NbData(i)<<"	"<<Name(i)<<endl;
    }
  }
  else {    //  n1 = 0 et n2 > 0 : Set n0 n2 avec un peu plus de detail
              //  si n2 < 0 : idem mais entete allege
    Standard_Integer nn2 = (n2 > 0 ? n2 : -n2);
    Handle(MoniTool_CaseData) cda = Set(nn2);
    if (cda.IsNull()) return;
    Standard_Integer nn = NbData(nn2);
    S<<(n2 > 0 ? "MoniTool_DB : Set " : "Set ")<<nn2<<" NbData="<<nn;
//    if (cda->IsCheck()) S<<"  Status : "<<(cda->IsWarning() ? "Warning" : "Fail");
    S<<endl;
    Standard_CString caseid = cda->CaseId();
    if (caseid[0] != '\0') S<<"  CaseId : "<<caseid;
    S<<"  Name : "<<Name(nn2)<<endl;
    S<<"Num	Kind	Name	Description"<<endl;
    for (i = 1; i <= nn; i ++) {
      S<<i<<"	";
      Standard_Integer kind = Kind (nn2,i);
      switch (kind) {
	case  0 : S<<"(any)";   break;
	case  1 : S<<"Raised";  break;
	case  2 : S<<"Entity";  break;
	case  3 : S<<"Geom..";  break;
	case  4 : S<<"Shape";   break;
	case  5 : S<<"XYZ";     break;
	case  6 : S<<"XY";      break;
	case  7 : S<<"Real1,2"; break;
	case  8 : S<<"Real";    break;
	case  9 : S<<"CPU";     break;
	case 10 : S<<"Text";    break;
	case 11 : S<<"Integer"; break;
	default : S<<kind;     break;
      }

      const TCollection_AsciiString dataname = DataName (nn2,i);
      if (dataname.Length() > 0) S<<"	"<<dataname.ToCString()<<"	";
      else S<<"		";

      Handle(Standard_Transient) val = Data (nn2,i);
      if (val.IsNull()) { S<<"(undefined value)"<<endl; continue; }
      switch (kind) {
        case 0 : S<<val->DynamicType()->Name();  break;
        case 1 : {
	  Handle(Standard_Failure) fl= Handle(Standard_Failure)::DownCast(val);
	  if (!fl.IsNull()) fl->Print (S);
	  else S<<val->DynamicType()->Name();
	  S<<endl;
	  break;
	}
        case 2 : {
	  if (!thesignent.IsNull() && !context.IsNull()) {
	    TCollection_AsciiString signval = thesignent->Text(val,context);
	    S<<signval.ToCString();
	  }
	  else S<<val->DynamicType()->Name();  break;
	}
        case 3 : S<<val->DynamicType()->Name();  break;
        case 4 : {
	  TopoDS_Shape sh = Shape (nn2,i);
	  if (sh.IsNull()) { S<<val->DynamicType()->Name();  break; }
//	  S << TopAbs::Print (sh.ShapeType(),S);
	  switch (sh.ShapeType()) {
	    case TopAbs_VERTEX    : S << "VERTEX";  break;
	    case TopAbs_EDGE      : S << "EDGE";  break;
	    case TopAbs_WIRE      : S << "WIRE";  break;
	    case TopAbs_FACE      : S << "FACE";  break;
	    case TopAbs_SHELL     : S << "SHELL";  break;
	    case TopAbs_SOLID     : S << "SOLID";  break;
	    case TopAbs_COMPSOLID : S << "COMPSOLID";  break;
	    case TopAbs_COMPOUND  : S << "COMPOUND";  break;
	    default : S<<"SHAPE";  break;
	  }
//  .. on pourrait donner plus d info selon le type : lecon suivante
	  break;
	}
        case 5 : {
	  gp_XYZ xyz;
	  if (!XYZ (nn2,i,xyz)) { S<<val->DynamicType()->Name();  break; }
	  S<<"X="<<xyz.X()<<"	Y="<<xyz.Y()<<"	Z="<<xyz.Z();
	  break;
	}
        case 6 : {
	  gp_XY xy;
	  if (!XY (nn2,i,xy )) { S<<val->DynamicType()->Name();  break; }
	  S<<"X="<<xy.X()<<"	Y="<<xy.Y();
	  break;
	}
        case 7 : {
	  Standard_Real r1,r2;
	  if (!Reals (nn2,i,r1,r2)) { S<<val->DynamicType()->Name();  break; }
	  S<<"R1="<<r1<<"	R2="<<r2;
	  break;
	}
        case 8 :
        case 9 : {
	  Standard_Real r;
	  if (!Real (nn2,i,r)) { S<<val->DynamicType()->Name();  break; }
	  S<<r;
	  break;
	}
        case 10 : {
	  Standard_CString t;
	  if (!Text (nn2,i,t)) { S<<val->DynamicType()->Name();  break; }
	  S<<t;
	  break;
	}
        case 11 : {
	  Standard_Integer iv;
	  if (!Integer (nn2,i,iv)) { S<<val->DynamicType()->Name();  break; }
	  S<<iv;
	  break;
	}
        default : S<<val->DynamicType()->Name();  break;
      }
      if (kind != 1) S<<endl;
    }
  }
}


Standard_Boolean  MoniTool_DB::Dump (Standard_OStream& S,
                                     const Standard_Integer num,
                                     const Standard_Integer nd,
                                     const Standard_Integer level) const
{
  Standard_Integer kind = Kind (num,nd);
  if (kind == 4) {
    TopoDS_Shape sh = Shape (num,nd);
    if (sh.IsNull()) return Standard_False;
    S<<"MoniTool_DB : Dump  Set n0 "<<num<<"  Data n0 "<<nd<<" : Shape"<<endl<<endl;
    return Standard_True;
  }
  Handle(Standard_Transient) val = Data (num,nd);
  Handle(Geom_Surface) SU = Handle(Geom_Surface)::DownCast (val);
  if (!SU.IsNull()) {
    S<<"MoniTool_DB : Dump  Set n0 "<<num<<"  Data n0 "<<nd<<" : Surface"<<endl<<endl;
//    GeomTools::Dump (SU,S);
    return Standard_False;
  }
  Handle(Geom_Curve) C3 = Handle(Geom_Curve)::DownCast (val);
  if (!C3.IsNull()) {
    S<<"MoniTool_DB : Dump  Set n0 "<<num<<"  Data n0 "<<nd<<" : Curve3D"<<endl<<endl;
//    GeomTools::Dump (C3,S);
    return Standard_False;
  }
  Handle(Geom2d_Curve) C2 = Handle(Geom2d_Curve)::DownCast (val);
  if (!C2.IsNull()) {
    S<<"MoniTool_DB : Dump  Set n0 "<<num<<"  Data n0 "<<nd<<" : Curve2D"<<endl<<endl;
//    GeomTools::Dump (C2,S);
    return Standard_False;
  }
//  yaurait aussi Transformation, Point et son orchestre ...
  return Standard_False;
}


Standard_Boolean  MoniTool_DB::Write (const Standard_CString file,
                                      const Standard_Integer num,
                                      const Standard_Integer nd,
                                      const Standard_Integer level) const
{
  return Standard_False;  // rien d implemente
}
