// File:	ViewerTest_ViewerCommands.cxx
// Created:	Tue Sep  1 10:28:35 1998
// Author:	Robert COUBLANC
//		<rob@robox.paris1.matra-dtv.fr>

// Robert Boehne 30 May 2000 : Dec Osf

#include <AIS_InteractiveContext.hxx>
#include <ViewerTest.hxx>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <string.h>
#include <Draw_Interpretor.hxx>
#include <Draw.hxx>
#include <Draw_Appli.hxx>
#include <DBRep.hxx>

#include <tcl.h>

#ifndef WNT
#include <tk.h>
#include <Graphic3d_GraphicDevice.hxx>
#include <Xw_GraphicDevice.hxx>
#include <Xw_WindowQuality.hxx>
#include <Xw_Window.hxx>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <Graphic3d_GraphicDevice.hxx>
#include <Xw_GraphicDevice.hxx>
#include <Xw_WindowQuality.hxx>
#include <Xw_Window.hxx>

#else

#include <Graphic3d_WNTGraphicDevice.hxx>
#include <WNT_WClass.hxx>
#include <WNT_Window.hxx>

#endif

#include <Standard_Address.hxx>



#include <TCollection_AsciiString.hxx>
#include <V3d_Viewer.hxx>
#include <V3d_View.hxx>
#include <V3d.hxx>
#include <AIS_DisplayMode.hxx>
#include <TColStd_MapOfInteger.hxx>
#include <AIS_MapOfInteractive.hxx>
#include <ViewerTest_DoubleMapOfInteractiveAndName.hxx>
#include <ViewerTest_DoubleMapIteratorOfDoubleMapOfInteractiveAndName.hxx>
#include <ViewerTest_EventManager.hxx>


#include <Draw_Window.hxx>
#include <AIS_ListIteratorOfListOfInteractive.hxx>
#include <AIS_ListOfInteractive.hxx>

#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif

#define OCC120

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

//==============================================================================
//  VIEWER GLOBAL VARIABLES
//==============================================================================



Standard_EXPORT int ViewerMainLoop(Standard_Integer , char** argv);


#ifdef WNT
static Handle(Graphic3d_WNTGraphicDevice)& GetG3dDevice(){
  static Handle(Graphic3d_WNTGraphicDevice) GD;
  return GD;
}

static Handle(WNT_Window)& VT_GetWindow() {
  static Handle(WNT_Window) WNTWin;
  return WNTWin;
}

#else
static Handle(Graphic3d_GraphicDevice)& GetG3dDevice(){
  static Handle(Graphic3d_GraphicDevice) GD;
  return GD;
}
static Handle(Xw_Window)& VT_GetWindow(){
  static Handle(Xw_Window) XWWin;
  return XWWin;
}
static Display *display;

static void VProcessEvents(ClientData,int);
#endif

#ifdef OCC120
static Standard_Boolean DegenerateMode = Standard_True;
#endif

#define ZCLIPWIDTH 1.

static void OSWindowSetup();

//==============================================================================
//  EVENT GLOBAL VARIABLES
//==============================================================================

  static int Start_Rot = 0;
  static int ZClipIsOn = 0;
  static int X_Motion= 0,Y_Motion=0; // Current cursor position
  static int X_ButtonPress = 0, Y_ButtonPress = 0; // Last ButtonPress position


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

#ifdef WNT
static LRESULT WINAPI ViewerWindowProc(
				       HWND hwnd,
				       UINT uMsg,
				       WPARAM wParam,
				       LPARAM lParam );
static LRESULT WINAPI AdvViewerWindowProc(
				       HWND hwnd,
				       UINT uMsg,
				       WPARAM wParam,
				       LPARAM lParam );
#endif


//==============================================================================
//function : WClass
//purpose  :
//==============================================================================

const Handle(MMgt_TShared)& ViewerTest::WClass()
{
  static Handle(MMgt_TShared) theWClass;
#ifdef WNT
  if (theWClass.IsNull()) {
    theWClass = new WNT_WClass ("GW3D_Class", AdvViewerWindowProc,
				CS_VREDRAW | CS_HREDRAW,0,0,
				::LoadCursor (NULL, IDC_ARROW));
  }
#endif
  return theWClass;
}


//==============================================================================
//function : ViewerInit
//purpose  : Create the window viewer and initialize all the global variable
//==============================================================================

void ViewerTest::ViewerInit()
{
  static int first = 1;

  if ( first ) {

    // Create the Graphic device
#ifdef WNT

    if ( GetG3dDevice().IsNull() ) GetG3dDevice()  =
				     new Graphic3d_WNTGraphicDevice();
    if ( VT_GetWindow().IsNull() ) {

      // Create the Graphic device and the window
      Handle( WNT_GraphicDevice ) g_Device = new WNT_GraphicDevice ();

      VT_GetWindow() = new WNT_Window
	  (g_Device,"Test3d", Handle(WNT_WClass)::DownCast (WClass()),
	   WS_OVERLAPPEDWINDOW,0.,0.20,0.40,0.40,Quantity_NOC_BLACK);
      VT_GetWindow()->Map();
    }
#else
    if ( GetG3dDevice().IsNull() ) GetG3dDevice()  =
				     new Graphic3d_GraphicDevice(getenv("DISPLAY"),Xw_TOM_READONLY);
    if ( VT_GetWindow().IsNull() )  {
      VT_GetWindow() = new Xw_Window(GetG3dDevice(),
				     "Test3d",
				     0.,0.60,0.40,0.40,
				     Xw_WQ_3DQUALITY,
				     Quantity_NOC_BLACK);
    }
#endif


    Handle(V3d_Viewer) a3DViewer,a3DCollector;
  // Viewer and View creation

    TCollection_ExtendedString NameOfWindow("Visu3D");

    a3DViewer = new V3d_Viewer(GetG3dDevice(),NameOfWindow.ToExtString());
    NameOfWindow = TCollection_ExtendedString("Collector");
    a3DCollector = new V3d_Viewer(GetG3dDevice(),NameOfWindow.ToExtString());
    a3DViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK);
    a3DCollector->SetDefaultBackgroundColor(Quantity_NOC_STEELBLUE);


    if ( ViewerTest::CurrentView().IsNull() ) {
      Handle (V3d_View) V = a3DViewer->CreateView();
      ViewerTest::CurrentView(V);
    }
    Handle(V3d_View) a3DViewCol;
    if ( a3DViewCol.IsNull() ) a3DViewCol    = a3DViewer->CreateView();

    // AIS setup
    if ( ViewerTest::GetAISContext().IsNull() ) {
      Handle(AIS_InteractiveContext) C = new AIS_InteractiveContext(a3DViewer,a3DCollector);
      ViewerTest::SetAISContext(C);
    }

    // Setup for X11 or NT
    OSWindowSetup();

    // Viewer and View creation

    a3DViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK);

    Handle (V3d_View) V = ViewerTest::CurrentView();

    V->SetDegenerateModeOn();
#ifdef OCC120
    DegenerateMode = V->DegenerateModeIsOn();
#endif
    V->SetWindow(VT_GetWindow());

    V->SetZClippingDepth(0.5);
    V->SetZClippingWidth(ZCLIPWIDTH/2.);

    a3DViewer->SetDefaultLights();
    a3DViewer->SetLightOn();

#ifndef WNT
#if TCL_MAJOR_VERSION  < 8  
    Tk_CreateFileHandler((void*)ConnectionNumber(display),
			 TK_READABLE, VProcessEvents, (ClientData) 0);
#else
    Tk_CreateFileHandler(ConnectionNumber(display),
			 TK_READABLE, VProcessEvents, (ClientData) 0);
#endif
#endif

    first = 0;

  }
}



//==============================================================================
//function : Vinit
//purpose  : Create the window viewer and initialize all the global variable
//		Use Tk_CreateFileHandler on UNIX to cath the X11 Viewer event
//==============================================================================


static int VInit(Draw_Interpretor& , Standard_Integer , char** ) {

  ViewerTest::ViewerInit();

  return 0;

}



//==============================================================================
//function : ProcessKeyPress
//purpose  : Handle KeyPress event from a CString
//==============================================================================

static void ProcessKeyPress( char *buf_ret )
{
  //cout << "KeyPress" << endl;

  // Letter in alphabetic order

  if ( !strcasecmp(buf_ret, "A") ) {
    // AXO
    ViewerTest::CurrentView()->SetProj(V3d_XposYnegZpos);
  }
  else if ( !strcasecmp(buf_ret, "D") ) {
    // Reset
    ViewerTest::CurrentView()->Reset();
  }
  else if ( !strcasecmp(buf_ret, "F") ) {
    // FitAll
    ViewerTest::CurrentView()->FitAll();
  }
  else if ( !strcasecmp(buf_ret, "H") ) {
    // HLR
    cout << "HLR" << endl;
#ifdef OCC120
    if (ViewerTest::CurrentView()->DegenerateModeIsOn()) ViewerTest::CurrentView()->SetDegenerateModeOff();
    else ViewerTest::CurrentView()->SetDegenerateModeOn();
    DegenerateMode = ViewerTest::CurrentView()->DegenerateModeIsOn();
#else
    ViewerTest::CurrentView()->SetDegenerateModeOff();
#endif
  }
  else if ( !strcasecmp(buf_ret, "D") ) {
    // Reset
    ViewerTest::CurrentView()->Reset();
  }
  else if ( !strcasecmp(buf_ret, "S") ) {
    // SHADING
    cout << "passage en mode 1 (shading pour les shapes)" << endl;
#ifndef OCC120
    ViewerTest::CurrentView()->SetDegenerateModeOn();
#endif
    Handle(AIS_InteractiveContext) Ctx = ViewerTest::GetAISContext();
    if(Ctx->NbCurrents()==0 ||
       Ctx->NbSelected()==0)
      Ctx->SetDisplayMode(AIS_Shaded);
    else{
      if(Ctx->HasOpenedContext()){
	for(Ctx->InitSelected();Ctx->MoreSelected();Ctx->NextSelected())
	  Ctx->SetDisplayMode(Ctx->Interactive(),1,Standard_False);
      }
      else{
	for(Ctx->InitCurrent();Ctx->MoreCurrent();Ctx->NextCurrent())
	  Ctx->SetDisplayMode(Ctx->Current(),1,Standard_False);
      }
      Ctx->UpdateCurrentViewer();
    }
  }
  else if ( !strcasecmp(buf_ret, "U") ) {
    // SHADING
    cout<<"passage au mode par defaut"<<endl;
#ifndef OCC120
    ViewerTest::CurrentView()->SetDegenerateModeOn();
#endif
    Handle(AIS_InteractiveContext) Ctx = ViewerTest::GetAISContext();
    if(Ctx->NbCurrents()==0 ||
       Ctx->NbSelected()==0)
      Ctx->SetDisplayMode(AIS_WireFrame);
    else{
      if(Ctx->HasOpenedContext()){
	for(Ctx->InitSelected();Ctx->MoreSelected();Ctx->NextSelected())
	  Ctx->UnsetDisplayMode(Ctx->Interactive(),Standard_False);
      }
      else{
	for(Ctx->InitCurrent();Ctx->MoreCurrent();Ctx->NextCurrent())
	  Ctx->UnsetDisplayMode(Ctx->Current(),Standard_False);
      }
      Ctx->UpdateCurrentViewer();
    }

  }
  else if ( !strcasecmp(buf_ret, "T") ) {
    // Top
    ViewerTest::CurrentView()->SetProj(V3d_Zpos);
  }
  else if ( !strcasecmp(buf_ret, "B") ) {
    // Top
    ViewerTest::CurrentView()->SetProj(V3d_Zneg);
  }
  else if ( !strcasecmp(buf_ret, "L") ) {
    // Top
    ViewerTest::CurrentView()->SetProj(V3d_Xneg);
  }
  else if ( !strcasecmp(buf_ret, "R") ) {
    // Top
    ViewerTest::CurrentView()->SetProj(V3d_Xpos);
  }

  else if ( !strcasecmp(buf_ret, "W") ) {
    // WIREFRAME
#ifndef OCC120
    ViewerTest::CurrentView()->SetDegenerateModeOn();
#endif
    cout << "passage en mode 0 (filaire pour les shapes)" << endl;
#ifndef OCC120
    ViewerTest::CurrentView()->SetDegenerateModeOn();
#endif
    Handle(AIS_InteractiveContext) Ctx = ViewerTest::GetAISContext();
    if(Ctx->NbCurrents()==0 ||
       Ctx->NbSelected()==0)
      Ctx->SetDisplayMode(AIS_WireFrame);
    else{
      if(Ctx->HasOpenedContext()){
	for(Ctx->InitSelected();Ctx->MoreSelected();Ctx->NextSelected())
	  Ctx->SetDisplayMode(Ctx->Interactive(),0,Standard_False);
      }
      else{
	for(Ctx->InitCurrent();Ctx->MoreCurrent();Ctx->NextCurrent())
	  Ctx->SetDisplayMode(Ctx->Current(),0,Standard_False);
      }
      Ctx->UpdateCurrentViewer();
    }
 }
  else if ( !strcasecmp(buf_ret, "Z") ) {
    // ZCLIP

    if ( ZClipIsOn ) {
      cout << "ZClipping OFF" << endl;
      ZClipIsOn = 0;

      ViewerTest::CurrentView()->SetZClippingType(V3d_OFF);
      ViewerTest::CurrentView()->Redraw();
    }
    else {
      cout << "ZClipping ON" << endl;
      ZClipIsOn = 1;

      ViewerTest::CurrentView()->SetZClippingType(V3d_FRONT);
      ViewerTest::CurrentView()->Redraw();
    }
  }
  else if ( !strcasecmp(buf_ret, ",") ) {
    ViewerTest::GetAISContext()->HilightNextDetected(ViewerTest::CurrentView());


  }
  else if ( !strcasecmp(buf_ret, ".") ) {
    ViewerTest::GetAISContext()->HilightPreviousDetected(ViewerTest::CurrentView());
  }
  // Number
  else{
    Standard_Integer Num = atoi(buf_ret);
    if(Num>=0 && Num<=7)
      ViewerTest::StandardModeActivation(Num);
  }
}

//==============================================================================
//function : ProcessExpose
//purpose  : Redraw the View on an Expose Event
//==============================================================================

static void ProcessExpose(  )
{ //cout << "Expose" << endl;
  ViewerTest::CurrentView()->Redraw();
}

//==============================================================================
//function : ProcessConfigure
//purpose  : Resize the View on an Configure Event
//==============================================================================

static void ProcessConfigure()
{
  Handle(V3d_View) V = ViewerTest::CurrentView();
  V->MustBeResized();
  V->Update();
  V->Redraw();
}

//==============================================================================
//function : ProcessButton1Press
//purpose  : Picking
//==============================================================================

static Standard_Boolean ProcessButton1Press(
	Standard_Integer ,
	char** argv,
	Standard_Boolean pick,
	Standard_Boolean shift )
{
  Handle(ViewerTest_EventManager) EM = ViewerTest::CurrentEventManager();
  if ( pick ) {
	Standard_Real X, Y, Z;

	ViewerTest::CurrentView()->Convert(X_Motion, Y_Motion, X, Y, Z);

	Draw::Set(argv[1], X);
	Draw::Set(argv[2], Y);
	Draw::Set(argv[3], Z);}

  if(shift)
    EM->ShiftSelect();
  else
    EM->Select();

  pick = 0;
  return pick;
}

//==============================================================================
//function : ProcessButton3Press
//purpose  : Start Rotation
//==============================================================================

static void ProcessButton3Press()

{ // Start rotation
  Start_Rot = 1;
  ViewerTest::CurrentView()->SetDegenerateModeOn();
  ViewerTest::CurrentView()->StartRotation( X_ButtonPress, Y_ButtonPress );

}
//==============================================================================
//function : ProcessButtonRelease
//purpose  : Start Rotation
//==============================================================================

static void ProcessButtonRelease()

{ // End rotation
#ifdef OCC120
  if (Start_Rot) {
    Start_Rot = 0;
    if (!DegenerateMode) ViewerTest::CurrentView()->SetDegenerateModeOff();
  }
#else
  Start_Rot = 0;
  ViewerTest::CurrentView()->SetDegenerateModeOff();
#endif
}

//==============================================================================
//function : ProcessZClipMotion
//purpose  : Zoom
//==============================================================================

void ProcessZClipMotion()
{
  Handle(V3d_View)  a3DView = ViewerTest::CurrentView();
  if ( Abs(X_Motion - X_ButtonPress) > 2 ) {
        static Standard_Real CurZPos = 0.;

	Quantity_Length VDX, VDY;

	a3DView->Size(VDX,VDY);

#ifdef DEB
	Standard_Real VDZ =
#endif
	  a3DView->ZSize();

	//printf("View size (%lf,%lf,%lf)\n", VDX, VDY, VDZ);

	Quantity_Length dx = a3DView->Convert(X_Motion - X_ButtonPress);

	// Front = Depth + width/2.
	Standard_Real D = 0.5;
	Standard_Real W = 0.1;

	CurZPos += (dx);

	D += CurZPos;

	//printf("dx %lf Depth %lf Width %lf\n", dx, D, W);

	a3DView->SetZClippingType(V3d_OFF);
	a3DView->SetZClippingDepth(D);
	a3DView->SetZClippingWidth(W);
	a3DView->SetZClippingType(V3d_FRONT);

	a3DView->Redraw();

	X_ButtonPress = X_Motion;
	Y_ButtonPress = Y_Motion;
  }
}

//==============================================================================
//function : ProcessControlButton1Motion
//purpose  : Zoom
//==============================================================================

static void ProcessControlButton1Motion()
{
  ViewerTest::CurrentView()->Zoom( X_ButtonPress, Y_ButtonPress, X_Motion, Y_Motion);

  X_ButtonPress = X_Motion;
  Y_ButtonPress = Y_Motion;
}

//==============================================================================
//function : ProcessControlButton2Motion
//purpose  : Pann
//==============================================================================

static void ProcessControlButton2Motion()
{
  Quantity_Length dx = ViewerTest::CurrentView()->Convert(X_Motion - X_ButtonPress);
  Quantity_Length dy = ViewerTest::CurrentView()->Convert(Y_Motion - Y_ButtonPress);

  dy = -dy; // Xwindow Y axis is from top to Bottom

  ViewerTest::CurrentView()->Panning( dx, dy );

  X_ButtonPress = X_Motion;
  Y_ButtonPress = Y_Motion;
}

//==============================================================================
//function : ProcessControlButton3Motion
//purpose  : Rotation
//==============================================================================

static void ProcessControlButton3Motion()
{
  if ( Start_Rot ) ViewerTest::CurrentView()->Rotation( X_Motion, Y_Motion);
}

//==============================================================================
//function : ProcessPointerMotion
//purpose  : Rotation
//==============================================================================

static void ProcessMotion()
{
  //pre-hilights detected objects at mouse position

  Handle(ViewerTest_EventManager) EM = ViewerTest::CurrentEventManager();
  EM->MoveTo(X_Motion, Y_Motion, ViewerTest::CurrentView());
}


void ViewerTest::GetMousePosition(Standard_Integer& Xpix,Standard_Integer& Ypix)
{
  Xpix = X_Motion;Ypix=Y_Motion;
}

//==============================================================================
//function : VAxo
//purpose  : Switch to an Axonometric view
//Draw arg : No args
//==============================================================================

static int VAxo(Draw_Interpretor& di, Standard_Integer , char** )
{ if ( ViewerTest::CurrentView().IsNull() ) {
    di<<"La commande vinit n'a pas ete appele avant"<<"\n";
//	VInit(di, argc, argv);
  }

  ViewerTest::CurrentView()->SetProj(V3d_XposYnegZpos);

  return 0;
}

//==============================================================================
//function : VTop
//purpose  : Switch to a Top View
//Draw arg : No args
//==============================================================================

static int VTop(Draw_Interpretor& di, Standard_Integer , char** )
{

  if ( ViewerTest::CurrentView().IsNull() ) {
    di<<"La commande vinit n'a pas ete appele avant"<<"\n";

//	VInit(di, , argv);
  }

  ViewerTest::CurrentView()->SetProj(V3d_Zpos);
  return 0;

}

//==============================================================================
//function : VHelp
//purpose  : Dsiplay help on viewer Keyboead and mouse commands
//Draw arg : No args
//==============================================================================

static int VHelp(Draw_Interpretor& di, Standard_Integer , char** )
{

  di << "Q : Quit the application" << "\n";

  di << "========================="<<"\n";
  di << "F : FitAll" << "\n";
  di << "T : TopView" << "\n";
  di << "A : AxonometricView" << "\n";
  di << "R : ResetView" << "\n";

  di << "========================="<<"\n";
  di << "S : Shading" << "\n";
  di << "W : Wireframe" << "\n";
  di << "H : HidelLineRemoval" << "\n";

  di << "========================="<<"\n";
  di << "Selection mode "<<"\n";
  di << "0 : Shape" <<"\n";
  di << "1 : Vertex" <<"\n";
  di << "2 : Edge" <<"\n";
  di << "3 : Wire" <<"\n";
  di << "4 : Face" <<"\n";
  di << "5 : Shell" <<"\n";
  di << "6 : Solid" <<"\n";
  di << "7 : Compound" <<"\n";

  di << "=========================="<<"\n";
  di << "D : Remove Selected Object"<<"\n";
  di << "=========================="<<"\n";

  return 0;
}

Standard_Boolean IsDragged = Standard_False;

Standard_Integer xx1, yy1, xx2, yy2;
//the first and last point in viewer co-ordinates

Standard_Boolean DragFirst;


#ifdef WNT

static Standard_Boolean Ppick = 0;
static Standard_Integer Pargc = 0;
static char**           Pargv = NULL;


static LRESULT WINAPI AdvViewerWindowProc( HWND hwnd,
				       UINT Msg,
				       WPARAM wParam,
				       LPARAM lParam )
{
  if ( !ViewerTest::CurrentView().IsNull() ) {

    WPARAM fwKeys = wParam;

    switch( Msg ) {

    case WM_LBUTTONUP:
      IsDragged = Standard_False;
      if( !DragFirst )
      {
         HDC hdc = GetDC( hwnd );
         HGDIOBJ anObj = SelectObject( hdc, GetStockObject( WHITE_PEN ) );
         SelectObject( hdc, GetStockObject( HOLLOW_BRUSH ) );
         SetROP2( hdc, R2_NOT );
         Rectangle( hdc, xx1, yy1, xx2, yy2 );
         ReleaseDC( hwnd, hdc );

         Handle( AIS_InteractiveContext ) aContext = ViewerTest::GetAISContext();
         if( aContext.IsNull() )
           cout << "The context is null. Please use vinit before createmesh" << endl;
         else
           if ( fwKeys & MK_SHIFT )
             aContext->ShiftSelect( min( xx1, xx2 ), min( yy1, yy2 ),
                                    max( xx1, xx2 ), max( yy1, yy2 ),
                                    ViewerTest::CurrentView() );
           else
             aContext->Select( min( xx1, xx2 ), min( yy1, yy2 ),
                               max( xx1, xx2 ), max( yy1, yy2 ),
                               ViewerTest::CurrentView() );
      }
      return ViewerWindowProc( hwnd, Msg, wParam, lParam );

    case WM_LBUTTONDOWN:
      if( fwKeys == MK_LBUTTON || fwKeys == ( MK_LBUTTON | MK_SHIFT ) )
      {
        IsDragged = Standard_True;
        DragFirst = Standard_True;
        xx1 = LOWORD(lParam);
        yy1 = HIWORD(lParam);
      }
      return ViewerWindowProc( hwnd, Msg, wParam, lParam );

      break;

    case WM_MOUSEMOVE:
      if( IsDragged )
      {
	HDC hdc = GetDC( hwnd );

        HGDIOBJ anObj = SelectObject( hdc, GetStockObject( WHITE_PEN ) );
        SelectObject( hdc, GetStockObject( HOLLOW_BRUSH ) );
        SetROP2( hdc, R2_NOT );

        if( !DragFirst )
          Rectangle( hdc, xx1, yy1, xx2, yy2 );

        DragFirst = Standard_False;
        xx2 = LOWORD(lParam);
        yy2 = HIWORD(lParam);

        Rectangle( hdc, xx1, yy1, xx2, yy2 );

        SelectObject( hdc, anObj );

	ReleaseDC( hwnd, hdc );
      }
      else
        return ViewerWindowProc( hwnd, Msg, wParam, lParam );
      break;

    default:
        return ViewerWindowProc( hwnd, Msg, wParam, lParam );
    }
    return 0;
  }
  return ViewerWindowProc( hwnd, Msg, wParam, lParam );
}


static LRESULT WINAPI ViewerWindowProc( HWND hwnd,
				       UINT Msg,
				       WPARAM wParam,
				       LPARAM lParam )
{
  /*static Standard_Boolean Ppick = 0;
  static Standard_Integer Pargc = 0;
  static char**           Pargv = NULL;*/

  static int Up = 1;

  if ( !ViewerTest::CurrentView().IsNull() ) {
    PAINTSTRUCT    ps;

    switch( Msg ) {
    case WM_PAINT:
      //cout << "\t WM_PAINT" << endl;
      BeginPaint(hwnd, &ps);
      EndPaint(hwnd, &ps);
      ProcessExpose();
      break;

    case WM_SIZE:
      //cout << "\t WM_SIZE" << endl;
      ProcessConfigure();
      break;

    case WM_KEYDOWN:
      //cout << "\t WM_KEYDOWN " << (int) wParam << endl;

      if ( (wParam != VK_SHIFT) && (wParam != VK_CONTROL) ) {
	char c[2];
	c[0] = (char) wParam;
	c[1] = '\0';
	ProcessKeyPress( c);
      }
      break;

    case WM_LBUTTONUP:
    case WM_MBUTTONUP:
    case WM_RBUTTONUP:
      //cout << "\t WM_xBUTTONUP" << endl;
      Up = 1;
      ProcessButtonRelease();
      break;

    case WM_LBUTTONDOWN:
    case WM_MBUTTONDOWN:
    case WM_RBUTTONDOWN:
      {
	//cout << "\t WM_xBUTTONDOWN" << endl;
	WPARAM fwKeys = wParam;

	Up = 0;

	X_ButtonPress = LOWORD(lParam);
	Y_ButtonPress = HIWORD(lParam);

	if ( Msg == WM_LBUTTONDOWN) {
		if(fwKeys & MK_CONTROL) {
			Ppick = ProcessButton1Press( Pargc, Pargv, Ppick,  (fwKeys & MK_SHIFT) );
		} else
			ProcessButton1Press( Pargc, Pargv, Ppick,  (fwKeys & MK_SHIFT) );
	}
	else if ( Msg == WM_RBUTTONDOWN ) {
	  // Start rotation
	  ProcessButton3Press( );
	}
      }
      break;

    case WM_MOUSEMOVE:
      {
	//cout << "\t WM_MOUSEMOVE" << endl;
	WPARAM fwKeys = wParam;
	X_Motion = LOWORD(lParam);
	Y_Motion = HIWORD(lParam);

	if ( Up &&
	    fwKeys & ( MK_LBUTTON|MK_MBUTTON|MK_RBUTTON ) ) {
	  Up = 0;
	  X_ButtonPress = LOWORD(lParam);
	  Y_ButtonPress = HIWORD(lParam);

	  if ( fwKeys & MK_RBUTTON ) {
	    // Start rotation
	    ProcessButton3Press();
	  }
	}

	if ( fwKeys & MK_CONTROL ) {
	  if ( fwKeys & MK_LBUTTON ) {
	    ProcessControlButton1Motion();
	  }
	  else if ( fwKeys & MK_MBUTTON ||
		   ((fwKeys&MK_LBUTTON) &&
		    (fwKeys&MK_RBUTTON) ) ){
	    ProcessControlButton2Motion();
	  }
	  else if ( fwKeys & MK_RBUTTON ) {
	    ProcessControlButton3Motion();
	  }
	}
#ifdef BUG
	else if ( fwKeys & MK_SHIFT ) {
	  if ( fwKeys & MK_MBUTTON ||
	      ((fwKeys&MK_LBUTTON) &&
	       (fwKeys&MK_RBUTTON) ) ) {
	    cout << "ProcessZClipMotion()" << endl;
	    ProcessZClipMotion();
	  }
	}
#endif
	else
	  if (( fwKeys & MK_MBUTTON || ((fwKeys&MK_LBUTTON) && (fwKeys&MK_RBUTTON) ) )){
	    ProcessZClipMotion();
	  }
	else {
	  ProcessMotion();
	}
      }
      break;

    default:
      return( DefWindowProc( hwnd, Msg, wParam, lParam ));
    }
    return 0L;
  }

  return DefWindowProc( hwnd, Msg, wParam, lParam );
}




//==============================================================================
//function : ViewerMainLoop
//purpose  : Get a Event on the view and dispatch it
//==============================================================================


static int ViewerMainLoop(Standard_Integer argc, char** argv)
{

//cout << "No yet implemented on WNT" << endl;
/*static Standard_Boolean Ppick = 0;
static Standard_Integer Pargc = 0;
static char**           Pargv = NULL;*/

  //Ppick = (argc > 0)? -1 : 0;
  Ppick = (argc > 0)? 1 : 0;
  Pargc = argc;
  Pargv = argv;

  if ( Ppick ) {
	MSG msg;
	msg.wParam = 1;

	cout << "Start picking" << endl;

	//while ( Ppick == -1 ) {
	while ( Ppick == 1 ) {
		// Wait for a ProcessButton1Press() to toggle pick to 1 or 0
		if (GetMessage(&msg, NULL, 0, 0) ) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	cout << "Picking done" << endl;
  }

  return Ppick;
}


#else

int min( int a, int b )
{
	if( a<b )
		return a;
	else
		return b;
}

int max( int a, int b )
{
	if( a>b )
		return a;
	else
		return b;
}

int ViewerMainLoop(Standard_Integer argc, char** argv)

{ Standard_Boolean pick = argc > 0;

  // X11 Event loop

  static XEvent report;

  XNextEvent( display, &report );
//    cout << "rep type = " << report.type << endl;
//    cout << "rep button = " << report.xbutton.button << endl;

    switch ( report.type ) {
      case Expose:
	{
	  ProcessExpose();
	}
	break;
      case ConfigureNotify:
	{
	  ProcessConfigure();
	}
	break;
      case KeyPress:
	{

	  KeySym ks_ret ;
	  char buf_ret[11] ;
	  int ret_len ;
	  XComposeStatus status_in_out;

	  ret_len = XLookupString( ( XKeyEvent *)&report ,
				  (char *) buf_ret , 10 ,
				  &ks_ret , &status_in_out ) ;


	  buf_ret[ret_len] = '\0' ;

	  if ( ret_len ) {
	  ProcessKeyPress( buf_ret);
	  }
	}
	break;
      case ButtonPress:
//	cout << "ButtonPress" << endl;
	{
	  X_ButtonPress = report.xbutton.x;
	  Y_ButtonPress = report.xbutton.y;

	  if ( report.xbutton.button == Button1 )
		if(  report.xbutton.state & ControlMask )
		  pick = ProcessButton1Press( argc, argv, pick,
		  	 		     ( report.xbutton.state & ShiftMask) );
		else
		{
		   IsDragged = Standard_True;
		   xx1 = X_ButtonPress;
		   yy1 = Y_ButtonPress;
		   DragFirst = Standard_True;
		}
	  else if ( report.xbutton.button == Button3 )
	    // Start rotation
	    ProcessButton3Press();
	}
	break;
      case ButtonRelease:
	{
//	  cout<<"relachement du bouton "<<(report.xbutton.button==3 ? "3": "on s'en fout") <<endl;
//	  cout << IsDragged << endl;
//	  cout << DragFirst << endl;

  	  if( IsDragged )
  	  {
             if( !DragFirst )
             {
                Aspect_Handle aWindow = VT_GetWindow()->XWindow();
                GC gc = XCreateGC( display, aWindow, 0, 0 );
                //  XSetFunction( display, gc, GXinvert );
                XDrawRectangle( display, aWindow, gc, min( xx1, xx2 ), min( yy1, yy2 ), abs( xx2-xx1 ), abs( yy2-yy1 ) );
             }

             Handle( AIS_InteractiveContext ) aContext = ViewerTest::GetAISContext();
             if( aContext.IsNull() )
             {
               cout << "The context is null. Please use vinit before createmesh" << endl;
               return 0;
             }

             Standard_Boolean ShiftPressed = ( report.xbutton.state & ShiftMask );
             if( report.xbutton.button==1 )
               if( DragFirst )
                 if( ShiftPressed )
                 {
                   aContext->ShiftSelect();
//                   cout << "shift select" << endl;
                 }
                 else
                 {
                   aContext->Select();
//                   cout << "select" << endl;
                 }
               else
                 if( ShiftPressed )
                 {
                   aContext->ShiftSelect( min( xx1, xx2 ), min( yy1, yy2 ),
                                          max( xx1, xx2 ), max( yy1, yy2 ),
                                          ViewerTest::CurrentView() );
//                   cout << "shift select" << endl;
                 }
                 else
                 {
                   aContext->Select( min( xx1, xx2 ), min( yy1, yy2 ),
                                     max( xx1, xx2 ), max( yy1, yy2 ),
                                     ViewerTest::CurrentView() );
//                   cout << "select" << endl;
                 }
             else
               ProcessButtonRelease();

             IsDragged = Standard_False;
          }
          else 
            ProcessButtonRelease();
        }
	break;
      case MotionNotify:
        {
//	  XEvent dummy;

	  X_Motion = report.xmotion.x;
	  Y_Motion = report.xmotion.y;

	  if( IsDragged )
	  {
	     Aspect_Handle aWindow = VT_GetWindow()->XWindow();
	     GC gc = XCreateGC( display, aWindow, 0, 0 );
	     XSetFunction( display, gc, GXinvert );

	     if( !DragFirst )
		XDrawRectangle( display, aWindow, gc, min( xx1, xx2 ), min( yy1, yy2 ), abs( xx2-xx1 ), abs( yy2-yy1 ) );

	     xx2 = X_Motion;
	     yy2 = Y_Motion;
	     DragFirst = Standard_False;

	     //cout << "draw rect : " << xx2 << ", " << yy2 << endl;
	     XDrawRectangle( display, aWindow, gc, min( xx1, xx2 ), min( yy1, yy2 ), abs( xx2-xx1 ), abs( yy2-yy1 ) );
	  }
	  else
	  {

	    //cout << "MotionNotify " << X_Motion << "," << Y_Motion << endl;

	    // remove all the ButtonMotionMask
	    while( XCheckMaskEvent( display, ButtonMotionMask, &report) ) ;

	    if ( ZClipIsOn && report.xmotion.state & ShiftMask ) {
	      if ( Abs(X_Motion - X_ButtonPress) > 2 ) {

	        Quantity_Length VDX, VDY;

	        ViewerTest::CurrentView()->Size(VDX,VDY);
	        Standard_Real VDZ =0 ;
	        VDZ = ViewerTest::CurrentView()->ZSize();

//	        printf("%lf,%lf,%lf\n", VDX, VDY, VDZ);
	        printf("%f,%f,%f\n", VDX, VDY, VDZ);

	        Quantity_Length dx = 0 ;
	        dx = ViewerTest::CurrentView()->Convert(X_Motion - X_ButtonPress);

	        cout << dx << endl;

	        dx = dx / VDX * VDZ;

	        cout << dx << endl;

	        // Front = Depth + width/2.
	        //ViewerTest::CurrentView()->SetZClippingDepth(dx);
	        //ViewerTest::CurrentView()->SetZClippingWidth(0.);

	        ViewerTest::CurrentView()->Redraw();
	      }
	    }

	    if ( report.xmotion.state & ControlMask ) {
	      if ( report.xmotion.state & Button1Mask ) {
	        ProcessControlButton1Motion();
	      }
	      else if ( report.xmotion.state & Button2Mask ) {
	        ProcessControlButton2Motion();
	      }
	      else if ( report.xmotion.state & Button3Mask ) {
	        ProcessControlButton3Motion();
	      }
	    }
	    else {
	      ProcessMotion();
	    }
          }
	}
	break;
      }


  return pick;
}

//==============================================================================
//function : VProcessEvents
//purpose  : call by Tk_CreateFileHandler() to be able to manage the
//	     event in the Viewer window
//==============================================================================

static void VProcessEvents(ClientData,int)
{
  //cout << "VProcessEvents" << endl;

  // test for X Event
  while (XPending(display)) {
    ViewerMainLoop( 0, NULL);
  }
}
#endif

//==============================================================================
//function : OSWindowSetup
//purpose  : Setup for the X11 window to be able to cath the event
//==============================================================================


static void OSWindowSetup()
{
#ifndef WNT
  // X11

  Window  window   = VT_GetWindow()->XWindow();

  Standard_Address theDisplay = GetG3dDevice()->XDisplay();
  display = (Display * ) theDisplay;
//  display = (Display *)GetG3dDevice()->XDisplay();

  XSynchronize(display, 1);

  VT_GetWindow()->Map();

  // X11 : For keyboard on SUN
  XWMHints wmhints;
  wmhints.flags = InputHint;
  wmhints.input = 1;

  XSetWMHints( display, window, &wmhints);

  XSelectInput( display, window, 	ExposureMask | KeyPressMask |
					ButtonPressMask | ButtonReleaseMask |
					StructureNotifyMask |
	                         	PointerMotionMask |
	                         	Button1MotionMask | Button2MotionMask |
					Button3MotionMask
	       );

  XSynchronize(display, 0);

#else
  // WNT
#endif

}


//==============================================================================
//function : VFit
//purpose  : Fitall, no DRAW arguments
//Draw arg : No args
//==============================================================================

static int VFit(Draw_Interpretor& , Standard_Integer , char** )
{
  Handle(V3d_View) V = ViewerTest::CurrentView();
  if ( !V.IsNull() ) V->FitAll(); return 0; }

//==============================================================================
//function : VZFit
//purpose  : ZFitall, no DRAW arguments
//Draw arg : No args
//==============================================================================

static int VZFit(Draw_Interpretor& , Standard_Integer , char** )
{
  Handle(V3d_View) V = ViewerTest::CurrentView();
  if ( !V.IsNull() ) V->ZFitAll(); return 0; }


static int VRepaint(Draw_Interpretor& , Standard_Integer , char** )
{
  Handle(V3d_View) V = ViewerTest::CurrentView();
  if ( !V.IsNull() ) V->Redraw(); return 0;
}


//==============================================================================
//function : VClear
//purpose  : Remove all the object from the viewer
//Draw arg : No args
//==============================================================================

static int VClear(Draw_Interpretor& , Standard_Integer , char** )
{
  Handle(V3d_View) V = ViewerTest::CurrentView();
  if(!V.IsNull())
    ViewerTest::Clear();
  return 0;
}

//==============================================================================
//function : VPick
//purpose  :
//==============================================================================

static int VPick(Draw_Interpretor& di, Standard_Integer argc, char** argv)
{ if (ViewerTest::CurrentView().IsNull() ) return 1;

  if ( argc < 4 ) {
    di << argv[0] << "Invalid number of arguments" << "\n";
    return 1;
  }

  while (ViewerMainLoop( argc, argv)) {
  }

  return 0;
}



//==============================================================================
//function : InitViewerTest
//purpose  : initialisation de toutes les variables static de  ViewerTest (dp)
//==============================================================================

void ViewerTest_InitViewerTest (const Handle(AIS_InteractiveContext)& context)
{
  Handle(V3d_Viewer) viewer = context->CurrentViewer();
  ViewerTest::SetAISContext(context);
  viewer->InitActiveViews();
  Handle(V3d_View) view = viewer->ActiveView();
  if (viewer->MoreActiveViews()) ViewerTest::CurrentView(view);
  ViewerTest::ResetEventManager();
  Handle(Aspect_GraphicDevice) device = viewer->Device();
  Handle(Aspect_Window) window = view->Window();
#ifndef WNT
  // X11
  VT_GetWindow() = Handle(Xw_Window)::DownCast(window);
  GetG3dDevice() = Handle(Graphic3d_GraphicDevice)::DownCast(device);
  OSWindowSetup();
  static int first = 1;
  if ( first ) {
#if TCL_MAJOR_VERSION  < 8 
    Tk_CreateFileHandler((void*)ConnectionNumber(display),
			     TK_READABLE, VProcessEvents, (ClientData) 0);
#else
    Tk_CreateFileHandler(ConnectionNumber(display),
			     TK_READABLE, VProcessEvents, (ClientData) 0);
#endif
    first = 0;
  }
#endif
}


//==============================================================================
//function : VSetBg
//purpose  : Load image as background
//==============================================================================

static int VSetBg(Draw_Interpretor& di, Standard_Integer argc, char** argv)
{
  if (argc < 2 || argc > 3)
  {
    di << "Usage : " << argv[0] << " imagefile [filltype] : Load image as background" << "\n";
    return 1;
  }

  Handle(AIS_InteractiveContext) AISContext = ViewerTest::GetAISContext();
  if(AISContext.IsNull())
  {
    di << "use 'vinit' command before " << argv[0] << "\n";
    return 1;
  }

  Aspect_FillMethod aFillType = Aspect_FM_CENTERED;
  if (argc == 3)
  {
    char* szType = argv[2];
    if      (strcmp(szType, "NONE"    ) == 0) aFillType = Aspect_FM_NONE;
    else if (strcmp(szType, "CENTERED") == 0) aFillType = Aspect_FM_CENTERED;
    else if (strcmp(szType, "TILED"   ) == 0) aFillType = Aspect_FM_TILED;
    else if (strcmp(szType, "STRETCH" ) == 0) aFillType = Aspect_FM_STRETCH;
    else
    {
      di << "Wrong fill type : " << szType << "\n";
      di << "Must be one of CENTERED, TILED, STRETCH, NONE" << "\n";
      return 1;
    }
  }

  Handle(V3d_View) V3dView = ViewerTest::CurrentView();
  V3dView->SetBackgroundImage(argv[1], aFillType, Standard_True);

  return 0;
}

//==============================================================================
//function : VScale
//purpose  : View Scaling
//==============================================================================

static int VScale(Draw_Interpretor& di, Standard_Integer argc, char** argv)
{
  Handle(V3d_View) V3dView = ViewerTest::CurrentView();
  if ( V3dView.IsNull() ) return 1;

  if ( argc != 4 ) {
    di << argv[0] << "Invalid number of arguments" << "\n";
    return 1;
  }
  V3dView->SetAxialScale( atof(argv[1]),  atof(argv[2]),  atof(argv[3]) );
  return 0;
}

//=======================================================================
//function : ViewerCommands
//purpose  :
//=======================================================================

void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
{

  char *group = "ZeViewer";
  theCommands.Add("vinit" ,
		  "vinit            : Create the Viewer window",
		  __FILE__,VInit,group);
  theCommands.Add("vhelp" ,
		  "vhelp            : display help on the viewer commands",
		  __FILE__,VHelp,group);
  theCommands.Add("vtop" ,
		  "vtop or <T>         : Top view" ,
		  __FILE__,VTop,group);
  theCommands.Add("vaxo" ,
		  " vaxo or <A>     : Axonometric view ",
		  __FILE__,VAxo,group);
  theCommands.Add("vpick" ,
		  "vpick           : vpick X Y Z [shape subshape] ( all variables as string )",
		  VPick,group);
  theCommands.Add("vfit"    ,
		  "vfit or <F>         : vfit",
		  __FILE__,VFit,group);
  theCommands.Add("vzfit"    ,
		  "vzfit",
		  __FILE__,VZFit,group);
  theCommands.Add("vrepaint",
		  "vrepaint        : vrepaint, force redraw",
		  __FILE__,VRepaint,group);
  theCommands.Add("vclear",
		  "vclear          : vclear",
		  __FILE__,VClear,group);
  theCommands.Add("vsetbg",
		  "vsetbg          : vsetbg imagefile [filltype] : Load image as background",
		  __FILE__,VSetBg,group);
  theCommands.Add("vscale",
		  "vscale          : vscale X Y Z",
		  __FILE__,VScale,group);

}
