#include <owl\owlpch.h>
#include <owl\applicat.h>
#include <owl\framewin.h>
#include <owl\dc.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "plot3d.h"

#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif

static int    external_to_plot(double,double);
static double f(double,double);
static int    red(double,double);

class SpikeWindow : public TFrameWindow
  {
    public:
      SpikeWindow(TWindow* parent, const char far* title);
      void CleanupWindow();
      BOOL IdleAction(long IdleCount);
        // So that the program remains responsive, the plotting is done here.
      void Paint(TDC &dc,BOOL erase,TRect &dirty);
    protected:
      void EvHScroll(UINT code,UINT pos,HWND wnd);
        // Processes a change in the horizontal scroll bar -- a request for
        // a new angle of rotation.
      void EvSize(UINT sizeType,TSize &size);
        // Processes resizing of the window -- a request for a different size
        // plot.
      void EvVScroll(UINT code,UINT pos,HWND wnd);
        // Processes a change in the vertical scroll bar -- a request for a
        // new angle of tilt.
    private:
      double light_x;
      double light_y;
      double light_z;
        // Vector to the major source of light.
      plot3d *plot3d_ptr;
        // Pointer to an instance of the 3D plotting class.
      TRect  region_to_paint;
      int    rotation;
        // The number of degrees the plot is rotated about a line
        // through its center running parallel to the z-axis.
      char   state;
        // State of the plotting; one of the following:
        //     'B' -- beginning
        //     'S' -- preparing plot
        //     'P' -- plotting
        //     'F' -- failure
        //     'D' -- done
      int    tilt;
        // The number of degrees the plot is tilted towards the viewer.
  
      DECLARE_RESPONSE_TABLE(SpikeWindow);
       // Associates user commands with methods in this program.
  };

DEFINE_RESPONSE_TABLE1(SpikeWindow,TFrameWindow)
  EV_WM_HSCROLL,
  EV_WM_VSCROLL,
  EV_WM_SIZE,
END_RESPONSE_TABLE;

SpikeWindow::SpikeWindow(
  TWindow        *parent,
  const char far *title) : TFrameWindow(parent,title)
    {
      Attr.Style |= WS_VSCROLL | WS_HSCROLL;
        // Scroll bars are used to specify tilt and rotation.
      AssignMenu("SPIKE_MENU");
      state='B';             // beginning plot
      rotation=0;            // (degrees)
      tilt=30;               // (degrees)
      light_x=(double) 1.0;  // vector to light source
      light_y=(double) -1.0;
      light_z=(double) 1.0;
      plot3d_ptr=new plot3d((TFrameWindow *) this);
    }

void SpikeWindow::CleanupWindow()
  {
    delete plot3d_ptr; // destroy 3D plotter
    TFrameWindow::CleanupWindow();
  }

BOOL SpikeWindow::IdleAction(long IdleCount)
// So that the program remains responsive, the plotting is done here.
  {
    switch (state)
      // State of the plotting; one of the following:
      //     'B' -- beginning
      //     'S' -- preparing plot
      //     'P' -- plotting
      //     'F' -- failure
      //     'D' -- done
      {
        case 'B': // begin
          GetClientRect(region_to_paint);
            // The whole plot is being done.
          SetScrollRange(SB_VERT,0,90);   
            // The plot may be tilted between 0 and 90 degrees.
          SetScrollRange(SB_HORZ,0,360);
            // The plot may be rotated between 0 and 360 degrees.
          SetScrollPos(SB_VERT,90-tilt);
            // Display the current tilt on the vertical scroll bar.
          SetScrollPos(SB_HORZ,rotation);
            // Display the current rotation on the horizontal scroll bar.
          if (IsIconic())
            state='F';
          else
            state='S';
          break;
        case 'S': // prepare plot
          switch (plot3d_ptr->prepare_plot(
           f,                        // function f(x,y) to be plotted
           -1.0,                     // minimum x
           1.0,                      // maximum x
           -1.0,                     // minimum y
           1.0,                      // maximum y
           external_to_plot,         // don't plot -- always FALSE
           red,                      // highlight -- always FALSE
           120,                      // number of x divisions
           120,                      // number of y divisions
           double(rotation),         // rotation in degrees
           double(tilt),             // tilt in degrees
           light_x,light_y,light_z)) // vector to light source
            {
              case 'S': // success; plot prepared
                state='P'; // proceed to plot
                break;
              case 'F': // failure (already announced in pop up)
                state='F';
                break;
              default:  // continue
                break;
            }
          break;
        case 'P': // plotting
          switch (plot3d_ptr->plot(
           region_to_paint,
           FALSE,                     // highlight flagged areas
           FALSE,                     // only plot flagged areas
           1.0))                      // contrast adjustment
            {
              case 'S': // success; plot complete
                state='D'; // proceed to wait for user input
                break;
              case 'F': // failure (already announced in pop up)
                {
                  state='F';
                  TClientDC *dc=new TClientDC(*this);
                  delete dc;
                }
                break;
              default:  // continue
                break;
            }
          break;
        case 'F': // failed; wait for user input
          break;
        default:  // done; wait for user input
          break;
      }
    TFrameWindow::IdleAction(IdleCount);
    return TRUE;
  }

void SpikeWindow::Paint(TDC &dc,BOOL erase,TRect &dirty)
// This method is invoked when part of the display has been made "dirty"
// (for example, when another window has been moved from atop the display).
  {
     switch (state)
       {
         case 'B': // beginning
             // Nothing is displayed yet; nothing need be restored.
           break;
         case 'S': // preparing plot
           dc.PatBlt(this->GetClientRect(),WHITENESS);
           dc.TextOut(0,0,"Preparing plot...");
             // The internal state of the 3D plotter is not available here,
             // so display this message.
           break;
         case 'P': // still plotting
           state='P';
           GetClientRect(region_to_paint);
           plot3d_ptr->restart_plot();
             // Proceed to restore the entire display.  Because the painter's
             // algorithm is used, areas outside the "dirty" region may still
             // need plotting.
           break;
         case 'F': // failed
             // Nothing is being displayed; nothing need be restored.
           break;
         default:  // done
           state='P';
           region_to_paint=dirty;
           plot3d_ptr->restart_plot();
             // Redo the "dirty" region.
           break;
       }
  }

void SpikeWindow::EvHScroll(UINT code,UINT pos,HWND wnd)
// Processes a change in the horizontal scroll bar -- a request for a new angle
// of rotation.
  {
    TFrameWindow::EvHScroll(code,pos,wnd);
    int change=FALSE;
    switch (code) 
      {
        case SB_LINEUP:
          rotation--;
          change=TRUE;
          break;
        case SB_LINEDOWN:
          rotation++;
          change=TRUE;
          break;
        case SB_PAGEUP:
          rotation-=10;
          change=TRUE;
          break;
        case SB_PAGEDOWN:
          rotation+=10;
          change=TRUE;
          break;
        case SB_THUMBPOSITION:
          rotation=pos;
          change=TRUE;
          break;
        case SB_THUMBTRACK:
          break;
        default:
          break;
      }
    if (change)
      {
        delete plot3d_ptr;
        state='B'; // proceed to completely redo plot
        plot3d_ptr=new plot3d((TFrameWindow *) this);
      }
    return;
  }

void SpikeWindow::EvSize(UINT sizeType,TSize &size)
// Processes resizing of the window -- a request for a different size plot.
  {
    delete plot3d_ptr;  // get rid of old 3D plotter
    state='B';          // proceed to completely redo plot
    plot3d_ptr=new plot3d((TFrameWindow *) this);
    return;
  }

void SpikeWindow::EvVScroll(UINT code,UINT pos,HWND wnd)
// Processes a change in the vertical scroll bar -- a request for a new angle of
// tilt.
  {
    TFrameWindow::EvVScroll(code,pos,wnd);
    int change=FALSE;
    switch (code) 
      {
        case SB_LINEUP:
          tilt++;
          if (tilt > 90)
            tilt=90;
          change=TRUE;
          break;
        case SB_LINEDOWN:
          tilt--;
          if (tilt < 0)
            tilt=0;
          change=TRUE;
          break;
        case SB_PAGEUP:
          tilt+=5;
          if (tilt > 90)
            tilt=90;
          change=TRUE;
          break;
        case SB_PAGEDOWN:
          tilt-=5;
          if (tilt < 0)
            tilt=0;
          change=TRUE;
          break;
        case SB_THUMBPOSITION:
          tilt=90-pos;
          change=TRUE;
          break;
        case SB_THUMBTRACK:
          break;
        default:
          break;
      }
    if (change)
      {
        delete plot3d_ptr;
        state='B'; // proceed to completely redo plot
        plot3d_ptr=new plot3d((TFrameWindow *) this);
      }
    return;
  }

static int external_to_plot(
  double x,
  double y)
// Returns TRUE if and only if a point (x,y) is external to the plot.
    {
       return FALSE;
    }

static int red(
  double x,
  double y)
// Returns TRUE if and only if a point (x,y) is flagged to be highlighted.
    {
       return FALSE;
    }

static double f(
  double x,
  double y)
// The function to be plotted.
    {
       double t1=x*x+y*y;
       double t2=cos(7.0*sqrt(t1));
       return 2.0*t2*t2/(1.0+30.0*t1);
    }

class SpikeApp : public TApplication
  {
    public:
           SpikeApp() : TApplication("Spike") {}
      void InitMainWindow();
  };

void SpikeApp::InitMainWindow()
  {
    MainWindow=new SpikeWindow(0,"Spike");
    MainWindow->SetIcon(this,"SPIKE_ICON");
  }

int OwlMain(int /*argc*/, char* /*argv*/ [])
// Execution of this program starts here.
  {
    return SpikeApp().Run(); // Process windows messages until user is done.
  }
