// ============================================================================
//  $Id: TRecordDisplay.cc,v 1.3 2004/06/24 15:15:24 goiwai Exp $
//  $Name: CLDAQ-1-14-03 $
//  $Log: TRecordDisplay.cc,v $
//  Revision 1.3  2004/06/24 15:15:24  goiwai
//  たくさんの変更
//  Tcout,Tcerr,Tlogをバッファ経由にした。
//  何個かexternした。Tlog,Tcout,Tcerr,Tinfo...Tcritなど出力関係。
//
//  Revision 1.2  2004/03/07 10:30:27  goiwai
//  ROOTに組みこむためのおまじないマクロを埋めこみました。
//  全てにおいて完全に動作するわけではありません。
//
//  Revision 1.1  2004/03/01 02:36:25  goiwai
//  RecordDisplay用のクラス群を置きました.
//  お蔵入りになってたものに手直しを加えたものですが,
//  まだ不充分な点も在ります.徐々に修正していきたいと思います.
//  DataRecordオブジェクトを表示するためのツール群なのでRecordDisplayと呼
//  んでいます,多分にオフライン要素が強いです.
//  オンラインよりにしたものを後でEventDisplay(Eventを表示)としてコミット
//  します.
//
// ============================================================================
#ifdef __CLDAQ_ROOT_USE
#include <TGFrame.h>
#include <TH1.h>
#include <TError.h>
#include <TDirectory.h>
#include <TBrowser.h>
#include "TRecordDisplay.hh"
#include "TRecordDisplayTab.hh"
#include "TRecordDisplayMenuBar.hh"
#include "TRecordDisplayCanvas.hh"
#include "TInputObjectStream.hh"
#include "TInputObjectFile.hh"
#include "TRecordDisplayPreference.hh"
#include "TRecordAssociationTable.hh"
#include "TRecordAssociationList.hh"
#include "TDataRecord.hh"
#include "TDataSection.hh"
#include "TDataSegment.hh"
#include "TDataElement.hh"
#include "Trootinit.h"

TApplication* TRecordDisplay::theApplication = rootinit( "Default", "CLDAQ RECORD DISPLAY" );

enum { RECORD, SECTION, SEGMENT, ELEMENT, NTAGS };
static const Tint _width = 680;
static const Tint _height = 580;
static const Tint _digits = 3;
static TFile* _file = 0;
static const Tstring _hist = "hist";
static const Tstring _cv = "cv";
static const Tstring _wname = "CLDAQ Record Display";

TRecordDisplay::TRecordDisplay()
  : TRecordDisplayComponent( this ), 
    TGMainFrame( gClient->GetRoot(), _width, _height ),
    thePreference( 0 ),
    theAssociationTable( 0 ),
    theNumberOfRecords( 0 ),
    theAutoBuild( Ttrue ),
    theTab( 0 ),
    theMenu( 0 )
{
  SetWindowName( _wname.c_str() );

  thePreference = new TRecordDisplayPreference();
  theAssociationTable = new TRecordAssociationTable();

  Associate();

  BuildMenuBar();
  BuildTabFrame();

  Update();
}

TRecordDisplay::TRecordDisplay( const Tstring& filename, TRecordAssociationTable* as )
  : TRecordDisplayComponent( this ), 
    TGMainFrame( gClient->GetRoot(), _width, _height ),
    thePreference( 0 ),
    theAssociationTable( as ),
    theNumberOfRecords( 0 ),
    theAutoBuild( Tfalse ),
    theTab( 0 ),
    theMenu( 0 )
{
  if ( !isexist( filename ) ) {
    Tcout << filename << ": No such a file." << Tendl;
    exit( EXIT_SUCCESS );
  }

  Tstring wname = _wname + " [" + filename + "]";
  SetWindowName( wname.c_str() );

  thePreference = 
    new TRecordDisplayPreference( new TInputObjectFile( filename ) );
  
  if ( !theAssociationTable ) {
    theAssociationTable = new TRecordAssociationTable();
    theAutoBuild = Ttrue;
  } else if ( theAssociationTable -> Empty() ) {
    theAutoBuild = Ttrue;
  }

  Associate();

  BuildMenuBar();
  BuildTabFrame();

  Update();
}

TRecordDisplay::TRecordDisplay( TRecordDisplayPreference* pre, const Tstring& filename, TRecordAssociationTable* as )
  : TRecordDisplayComponent( this ), 
    TGMainFrame( gClient->GetRoot(), _width, _height ),
    thePreference( pre ),
    theAssociationTable( as ),
    theNumberOfRecords( 0 ),
    theAutoBuild( Tfalse ),
    theTab( 0 ),
    theMenu( 0 )
{
  if ( !isexist( filename ) ) {
    Tcout << filename << ": No such a file." << Tendl;
    exit( EXIT_SUCCESS );
  }
  thePreference -> SetStream( new TInputObjectFile( filename ) );

  Tstring wname = _wname + " [" + filename + "]";
  SetWindowName( wname.c_str() );

  if ( !theAssociationTable ) {
    theAssociationTable = new TRecordAssociationTable();
    theAutoBuild = Ttrue;
  } else if ( theAssociationTable -> Empty() ) {
    theAutoBuild = Ttrue;
  }

  Associate();

  BuildMenuBar();
  BuildTabFrame();

  Update();
}

TRecordDisplay::TRecordDisplay( TRecordDisplayPreference* pre, TRecordAssociationTable* as )
  : TRecordDisplayComponent( this ), 
    TGMainFrame( gClient->GetRoot(), _width, _height ),
    thePreference( pre ),
    theAssociationTable( as ),
    theNumberOfRecords( 0 ),
    theAutoBuild( Tfalse ),
    theTab( 0 ),
    theMenu( 0 )
{
  SetWindowName( _wname.c_str() );

  if ( !theAssociationTable ) {
    theAssociationTable = new TRecordAssociationTable();
    theAutoBuild = Ttrue;
  } else if ( theAssociationTable -> Empty() ) {
    theAutoBuild = Ttrue;
  }

  Associate();

  BuildMenuBar();
  BuildTabFrame();

  Update();
}

TRecordDisplay::~TRecordDisplay()
{
  delete thePreference;
  delete theAssociationTable;
  delete theMenu;
  delete theTab;
}

Tvoid TRecordDisplay::Associate()
{
  if ( theAutoBuild ) {
    return;
  }

  // ヒストグラムだけ作る
  TRecordAssociationList& alist = theAssociationTable -> GetAssociationList();
  for ( Tsize_t i = 0; i < alist.size(); i ++ ) {
    Tint nbin = alist[i].GetNumberOfBins();
    Tdouble xmin = alist[i].GetMinimumX();
    Tdouble xmax = alist[i].GetMaximumX();
    Tstring hname = GetHistogramName( i );
    Tstring htitle = alist[i].GetElementID();
    TH1D* histo =
      new TH1D( hname.c_str(), htitle.c_str(), nbin, xmin, xmax );
    alist[i].SetHistogram( histo );
  }

  return;
}

Tvoid TRecordDisplay::BuildMenuBar()
{
  theMenu = new TRecordDisplayMenuBar( this );
  return;
}

Tvoid TRecordDisplay::BuildTabFrame()
{
  theTab = new TRecordDisplayTab( this );

  if ( theAutoBuild ) {
    return;
  }

  static const Tint taglen = NTAGS;
  TRecordAssociationList& alist = 
    theAssociationTable -> GetAssociationList();
  for ( Tsize_t i = 0; i < alist.size(); i ++ ) {
    Tstring tags[ taglen ] = {
      alist[i].GetRecordID(),
      alist[i].GetSectionID(),
      alist[i].GetSegmentID(),
      alist[i].GetElementID()
    };
    TGCompositeFrame* aframe = GetFrame( tags );
    Tstring cvname = GetCanvasName( i );
    TRecordDisplayCanvas* cv =
      new TRecordDisplayCanvas( this, aframe, cvname, _width, _height );
    alist[i].SetCanvas( cv );
    cv -> GetCanvas() -> cd();
    alist[i].GetHistogram() -> Draw();
  }

  return;
}

Tvoid TRecordDisplay::Build()
{
  TInputObjectStream* stream = thePreference -> GetStream();
  TDataRecord recbuf;
  TDataElement elebuf;
  Tstring endid = thePreference -> GetEndOfRecordID();
  Tint update = thePreference -> GetUpdateCycle();
  const TRecordAssociationList& alist = 
    theAssociationTable -> GetAssociationList();

  static const Tint taglen = NTAGS;
  while ( stream -> Read( recbuf ) ) {
    theNumberOfRecords ++;

    if ( ! endid.empty() && recbuf.GetID() == endid ) {
      break;
    }


    for ( Tsize_t i = 0; i < alist.size(); i ++ ) {
      Tstring tags[ taglen ] = {
        alist[i].GetRecordID(),
        alist[i].GetSectionID(),
        alist[i].GetSegmentID(),
        alist[i].GetElementID()
      };

      if ( recbuf == tags[RECORD] && recbuf.FindDataElement(tags[SECTION],tags[SEGMENT],tags[ELEMENT],elebuf) ) {
        Tint ndata = elebuf.GetNumberOfPrimitives();

        if ( ndata == 1 ) {
          Tdouble databuf;
          elebuf.StorePrimitive( databuf );
          alist[i].GetHistogram() -> Fill( databuf );
        } else {
          Tdouble* databuf = new Tdouble[ndata];
          elebuf.StorePrimitives( databuf );
          for ( Tint j = 0; j < ndata; j ++ ) {
            alist[i].GetHistogram() -> Fill( databuf[j] );
          }
          delete [] databuf;
        }
      }
    }

    if ( update > 0 && theNumberOfRecords % update == 0 ) {
      Update();
    }

    gSystem -> ProcessEvents();
  }

  return;
}

Tvoid TRecordDisplay::AutoBuild()
{
  TInputObjectStream* stream = thePreference -> GetStream();
  TDataRecord recbuf;
  Tstring endid = thePreference -> GetEndOfRecordID();
  Tint update = thePreference -> GetUpdateCycle();

  static const Tint taglen = NTAGS;
  Tstring tag[taglen];
  while ( stream -> Read( recbuf ) ) {
    theNumberOfRecords ++;

    tag[RECORD] = recbuf.GetID();
    if ( ! endid.empty() && tag[RECORD] == endid ) {
      break;
    }
    for ( Tint secid = 0; secid < recbuf.Size(); secid ++ ) {
      tag[SECTION] = recbuf[secid].GetID();
      for ( Tint segid = 0; segid < recbuf[secid].Size(); segid ++ ) {
        tag[SEGMENT] = recbuf[secid][segid].GetID();
        for ( Tint eleid = 0; eleid < recbuf[secid][segid].Size(); eleid ++ ) {
          tag[ELEMENT] = recbuf[secid][segid][eleid].GetID();
          if ( recbuf[secid][segid][eleid].GetElementType() == tTypeString ) {
            Tint ndata = recbuf[secid][segid][eleid].GetNumberOfPrimitives();
            TDataElement& e = recbuf[secid][segid][eleid];
            Tstring databuf;
            e.StorePrimitive( databuf );
            Tcout << "#" << itostr(theNumberOfRecords) << ":"
                  << "[" << tag[RECORD] << "]"
                  << "[" << tag[SECTION] << "]"
                  << "[" << tag[SEGMENT] << "]"
                  << "[" << tag[ELEMENT] << "]"
                  << " >>> \"" << databuf << "\"";
            if ( ndata == 1 ) {
              Tcout << Tendl;
            } else {
              Tcout << ", ...(omitted " << ndata-1 << " messages)" << Tendl;
            }
            continue;
          }

          if ( ! theAssociationTable -> HasAssociation( tag ) ) {
            Tint nbin = thePreference -> GetDefaultNumberOfBins();
            Tdouble xmin = thePreference -> GetDefaultMinimumX();
            Tdouble xmax = thePreference -> GetDefaultMaximumX();
            Tint nas = theAssociationTable -> Size();
            Tstring hname = GetHistogramName( nas );
            Tstring htitle = tag[ELEMENT];


            TGCompositeFrame* aframe = GetFrame( tag );

            Tstring cvname = GetCanvasName( nas );
            TRecordDisplayCanvas* cv =
              new TRecordDisplayCanvas( this, aframe, cvname, _width, _height );

            TH1D* histo =
              new TH1D( hname.c_str(), htitle.c_str(), nbin, xmin, xmax );
            histo->Draw();

            theAssociationTable
              -> AddAssociation( TRecordAssociation( tag[RECORD],
                                                     tag[SECTION],
                                                     tag[SEGMENT],
                                                     tag[ELEMENT],
                                                     nbin, xmin, xmax,
                                                     cv,
                                                     histo
                                                     ) );
          } else {

            // すでに関連づけられているけど,CanvasとかHISTOなきゃ作ってやる
            TRecordDisplayCanvas* cv = theAssociationTable -> GetCanvas( tag );
            if ( cv == 0 ) {
              Tint nas = theAssociationTable -> Size();
              Tstring cvname = GetCanvasName( nas );
              TGCompositeFrame* aframe = GetFrame( tag );              
              cv = new TRecordDisplayCanvas(this,aframe,cvname,_width,_height);
              theAssociationTable -> SetCanvas( tag, cv );
            }

            TH1D* histo = theAssociationTable -> GetHistogram( tag );
            if ( histo == 0 ) {
              // このときはAssociationからひろってくる
              Tint nbin = theAssociationTable -> GetNumberOfBins( tag );
              Tdouble xmin = theAssociationTable -> GetMinimumX( tag );
              Tdouble xmax = theAssociationTable -> GetMaximumX( tag );
              Tint nas = theAssociationTable -> Size();
              Tstring hname = GetHistogramName( nas );
              Tstring htitle = tag[ELEMENT];
              TH1D* histo =
                new TH1D( hname.c_str(), htitle.c_str(), nbin, xmin, xmax );
              cv -> GetCanvas() -> cd();
              histo->Draw();
              theAssociationTable -> SetHistogram( tag, histo );
            }

          }

          TH1D* histo = theAssociationTable -> GetHistogram( tag );
          if ( ! histo ) {
            Tcout << "#" << itostr(theNumberOfRecords) << ":"
                  << "[" << tag[RECORD] << "]"
                  << "[" << tag[SECTION] << "]"
                  << "[" << tag[SEGMENT] << "]"
                  << "[" << tag[ELEMENT] << "]"
                  << " no histogram." << Tendl;
            continue;
          }

          Tint ndata = recbuf[secid][segid][eleid].GetNumberOfPrimitives();
          if ( ndata == 1 ) {
            Tdouble databuf;
            recbuf[secid][segid][eleid].StorePrimitive( databuf );
            histo -> Fill( databuf );
          } else {
            Tdouble* databuf = new Tdouble[ndata];
            recbuf[secid][segid][eleid].StorePrimitives( databuf );
            for ( Tint i = 0; i < ndata; i ++ ) {
              histo -> Fill( databuf[i] );
            }
            delete [] databuf;
          }


          if ( update > 0 && theNumberOfRecords % update == 0 ) {
            Update();
          }

          gSystem -> ProcessEvents();
        }
      }
    }
  }

  return;
}

TGCompositeFrame* TRecordDisplay::GetFrame( Tstring* tags )
{
  static const Tint taglen = NTAGS;
  TGCompositeFrame* aframe = 0;
  TRecordDisplayTab* atab = theTab;
  for ( Tint i = 0; i < taglen; i ++ ) {
    aframe = atab -> GetFrame( tags[i] );
    if ( i != 3 ) {
      atab = atab->GetTab( aframe );
    }
  }
  return aframe;
}

TDirectory* TRecordDisplay::GetDirectory( Tstring* tags )
{
  static const Tint taglen = NTAGS;
  static const Tint tmpLV = 3000;

  // エラーがうざいので
  Tint tmp = gErrorIgnoreLevel;
  gErrorIgnoreLevel = tmpLV;

  _file -> cd();
  TDirectory* d = gDirectory;

  Tstring path;
  for ( int i =0; i < taglen; i ++ ) {
    if ( ! path.empty() ) {
      path += "/";
    }
    path += tags[i];
    if ( _file -> cd( path.c_str() ) ) {
      d = gDirectory;
    } else {
      d = d -> mkdir( tags[i].c_str() );
    }
  }

  gErrorIgnoreLevel = tmp;

  return d;
}


Tstring TRecordDisplay::GetHistogramName( Tint id ) const
{
  Tstring retval = _hist + itostr(id,_digits);
  return retval;
}

Tstring TRecordDisplay::GetCanvasName( Tint id ) const
{
  Tstring retval = _cv + itostr(id,_digits);
  return retval;
}

Tvoid TRecordDisplay::Open()
{
  Tcout << "TODO: Open(): under construction." << Tendl;
  return;
}

Tvoid TRecordDisplay::Browse()
{
  new TBrowser( "Browser", "CLDAQ Record Display", _width, _height );
  return;
}

Tvoid TRecordDisplay::Save()
{
  static const Tint taglen = NTAGS;
  if ( thePreference -> SaveRootAtLast() ) {
    Tstring rootfile = thePreference -> RootFile();
    _file = new TFile( rootfile.c_str(), "recreate" );
    const TRecordAssociationList& list = 
      theAssociationTable -> GetAssociationList();
    for ( Tsize_t i = 0; i < list.size(); i ++ ) {
      Tstring tags[ taglen ] = {
        list[i].GetRecordID(),
        list[i].GetSectionID(),
        list[i].GetSegmentID(),
        list[i].GetElementID()
      };
      GetDirectory( tags ) -> cd();
      TH1D* histo = list[i].GetHistogram();
      if ( histo ) {
        histo -> Write();
      }
    }
    _file -> Close();
    delete _file;
    _file = 0;
  }

  if ( thePreference -> SavePsAtLast() ) {
    const TRecordAssociationList& list = 
      theAssociationTable -> GetAssociationList();
    for ( Tsize_t i = 0; i < list.size(); i ++ ) {
      TCanvas* cv = list[i].GetCanvas()->GetCanvas();
      if ( cv ) {
        cv -> Print();
      }
    }
  }

  if ( thePreference -> SaveGifAtLast() ) {
    const TRecordAssociationList& list = 
      theAssociationTable -> GetAssociationList();
    for ( Tsize_t i = 0; i < list.size(); i ++ ) {
      TCanvas* cv = list[i].GetCanvas()->GetCanvas();
      if ( cv ) {
        Tstring gif = cv -> GetName();
        gif += ".gif";
        cv -> Print( gif.c_str() );
      }
    }
  }

  return;
}

Tvoid TRecordDisplay::SaveAs()
{
  Tcout << "TODO: SaveAs(): under construction." << Tendl;
  return;
}

Tvoid TRecordDisplay::Print()
{
  const TRecordAssociationList& list = 
    theAssociationTable -> GetAssociationList();
  for ( Tsize_t i = 0; i < list.size(); i ++ ) {
    TCanvas* cv = list[i].GetCanvas()->GetCanvas();
    if ( cv ) {
      cv -> Print();
    }
  }

  for ( Tsize_t i = 0; i < list.size(); i ++ ) {
    TCanvas* cv = list[i].GetCanvas()->GetCanvas();
    if ( cv ) {
      Tstring gif = cv -> GetName();
      gif += ".gif";
      cv -> Print( gif.c_str() );
    }
  }

  return;
}

Tvoid TRecordDisplay::PrintAs()
{
  Tcout << "TODO: PrintAs(): under construction." << Tendl;
  return;
}

Tvoid TRecordDisplay::Quit()
{
  theApplication -> Terminate( 0 );
}

Tvoid TRecordDisplay::Start()
{
  if ( IsAutoBuild() ) {
    Tbool modified = Tfalse;
    if ( thePreference -> GetUpdateCycle() == 1 ) {
      // auto build で update cycle がデフォルトなら
      // オフラインな利用が多いと思う
      thePreference -> SetUpdateCycle( 0 );
      modified = Ttrue;
    }
    AutoBuild();
    if ( modified ) {
      // こっそり元にもどしておこう
      thePreference -> SetUpdateCycle( 1 );
    }
  } else {
    Build();
  }

  Update();
  Save();

  if ( thePreference -> TerminateAtLast() ) {
    Quit();
  }

  theApplication -> Run();

  return;
}

Tvoid TRecordDisplay::Start( const Tstring& filename )
{
  if ( !isexist( filename ) ) {
    Tcout << filename << ": No such a file." << Tendl;
    return;
  }

  Tstring wname = _wname + " [" + filename + "]";
  SetWindowName( wname.c_str() );

  thePreference -> SetStream( new TInputObjectFile( filename ) );
  Start();
  return;
}

Tvoid TRecordDisplay::Pause()
{
  Tcout << "TODO: Pause(): under construction." << Tendl;
  return;
}

Tvoid TRecordDisplay::Update()
{
  MapSubwindows();
  Layout();
  MapWindow();

  const TRecordAssociationList& list = 
    theAssociationTable -> GetAssociationList();
  for ( Tsize_t i = 0; i < list.size(); i ++ ) {
    TCanvas* cv = list[i].GetCanvas() -> GetCanvas();
    if ( cv ) {
      cv -> Modified();
      cv -> Update();
    }
  }

  return;
}

Tvoid TRecordDisplay::Stop()
{
  Tcout << "TODO: Stop(): under construction." << Tendl;
  return;
}

Tvoid TRecordDisplay::Edit()
{
  Tcout << "TODO: Edit(): under construction." << Tendl;
  return;
}

Tvoid TRecordDisplay::Undo()
{
  Tcout << "TODO: Undo(): under construction." << Tendl;
  return;
}

Tvoid TRecordDisplay::Clear()
{
  // キャンバスだけ真っ白にする オブジェクトはのこる
  // ヒストグラムの内容もクリアする オブジェクトと軸情報はのこる
  const TRecordAssociationList& list = 
    theAssociationTable -> GetAssociationList();
  for ( Tsize_t i = 0; i < list.size(); i ++ ) {
    TCanvas* cv = list[i].GetCanvas()->GetCanvas();
    TH1D* histo = list[i].GetHistogram();
    if ( cv ) {
      cv -> Clear();
    }
    if ( histo ) {
      histo -> Reset();
    }
  }

  return;
}

Tvoid TRecordDisplay::Initialize()
{
  // メニューバーのこして全て消す
  // キャンバスとかも全部消す方向で
  // TODO: まだうまく動かない
  TRecordAssociationList& alist = 
    theAssociationTable -> GetAssociationList();
  for ( Tsize_t i = 0; i < alist.size(); i ++ ) {
    TCanvas* cv = alist[i].GetCanvas()->GetCanvas();
    TH1D* histo = alist[i].GetHistogram();
    if ( cv ) {
      delete cv;
      alist[i].SetCanvas( 0 );
    }
    if ( histo ) {
      delete histo;
      alist[i].SetHistogram( 0 );
    }
  }

  Update();
  return;
}

Tvoid TRecordDisplay::Preferences()
{
  Tcout << "TODO: Preferences(): under construction." << Tendl;
  return;
}

Tbool TRecordDisplay::ProcessMessage( Tlong msg, Tlong id, Tlong arg )
{
  if ( GET_SUBMSG( msg ) == kCM_MENU ) {
    switch ( id ) {
      case MENU_FILE_OPEN:
        Open();
        break;
      case MENU_FILE_BROWSE:
        Browse();
        break;
      case MENU_FILE_SAVE:
        Save();
        break;
      case MENU_FILE_SAVEAS:
        SaveAs();
        break;
      case MENU_FILE_PRINT:
        Print();
        break;
      case MENU_FILE_PRINTAS:
        PrintAs();
        break;
      case MENU_FILE_QUIT:
        Quit();
        break;

      case MENU_CTRL_START:
        Start();
        break;
      case MENU_CTRL_PAUSE:
        Pause();
        break;
      case MENU_CTRL_UPDATE:
        Update();
        break;
      case MENU_CTRL_STOP:
        Stop();
        break;

      case MENU_EDIT_EDIT:
        Edit();
        break;
      case MENU_EDIT_UNDO:
        Undo();
        break;
      case MENU_EDIT_CLEAR:
        Clear();
        break;
      case MENU_EDIT_INITIALIZE:
        Initialize();
        break;
      case MENU_EDIT_PREFERENCES:
        Preferences();
        break;

      case MENU_VIEW_FULL_SCREEN:
      case MENU_VIEW_ICONIFY:
      case MENU_VIEW_COLOR_LIST:
      case MENU_VIEW_FONT_LIST:
      case MENU_VIEW_MARKER_LIST:
      case MENU_VIEW_X3D:
      case MENU_VIEW_OPEN_GL:
        Tcout << "TODO: View Directory: under construction..." << Tendl;
        break;
    }
  }
  return Ttrue;
}

#endif

#ifdef __CLDAQ_ROOT_DLL
    ClassImp(TRecordDisplay)
#endif
