/* LICENSE:
  =========================================================================
    CMPack'04 Source Code Release for OPEN-R SDK 1.1.5-r2 for ERS7
    Copyright (C) 2004 Multirobot Lab [Project Head: Manuela Veloso]
    School of Computer Science, Carnegie Mellon University
    All rights reserved.
  ========================================================================= */

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <algorithm>
#include <iostream>

using namespace std;

#include <FL/x.H>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Double_Window.H>
#include <FL/fl_draw.H>

#include "../../agent/headers/CircBufPacket.h"
#include "../../agent/headers/SystemUtility.h"
#include "../../agent/Motion/MotionInterface.h"
#include "../log_processing/shared/PacketDecoder.h"
#include "../log_processing/shared/MotionPacketDecoder.h"
#include "../log_processing/shared/VisionPacketDecoder.h"

#include "local_model_test.h"

// Save a fltk window to a file
#include "window_capture.h"

using namespace LocalModel;
using namespace Motion;
using namespace VisionInterface;

char* model="ERS7";

class SImage;
SImage *DebugImage=NULL;

bool ProfileMode = false;
bool GoMode = false;
bool Lazy=true;

bool Interactive = !(ProfileMode || GoMode);

bool save_file_mode=false;
int save_file_count = 0;
char save_file_name[1024];

bool auto_playback_mode=false;
float auto_playback_rate=0.1;

static const int width =1024;
static const int height=1024;

AppWindow *win=NULL;

int RepeatCount=100;

//=========================================================================
void auto_repeat_cb(void * a) {
  AppWindow * appwin = (AppWindow*)a;
  appwin->advanceFrame();
  if (auto_playback_mode) {
    Fl::add_timeout(auto_playback_rate,auto_repeat_cb,a);
  }
}

//=========================================================================
void Canvas::set_size(int _w,int _h)
{
  w = _w;
  h = _h;
}

//=========================================================================
void Canvas::set_range(vector2f _minv,vector2f _maxv)
{
  minv = _minv;
  maxv = _maxv;
}

//=========================================================================
int Canvas::transform_x(float x)
{
  float res_x;

  res_x = w*(x - minv.x)/(maxv.x - minv.x);

  return (int)rint(res_x);
}

//=========================================================================
int Canvas::transform_y(float y)
{
  float res_y;

  res_y = h - h*(y - minv.y)/(maxv.y - minv.y);

  return (int)rint(res_y);
}

//=========================================================================
int Canvas::scale_x(float x)
{
  float res_x;

  res_x = w*x/(maxv.x - minv.x);

  return (int)rint(res_x);
}

//=========================================================================
int Canvas::scale_y(float y)
{
  float res_y;

  res_y = h*y/(maxv.y - minv.y);

  return (int)rint(res_y);
}

//=========================================================================
vector2i Canvas::operator()(vector2f world_coor)
{
  return vector2i(transform_x(world_coor.x),transform_y(world_coor.y));
}

//=========================================================================
float Canvas::reverse_transform_x(int x)
{
  float res_x;

  res_x = (maxv.x - minv.x)*((double)x)/w + minv.x;

  return res_x;
}

//=========================================================================
float Canvas::reverse_transform_y(int y)
{
  float res_y;

  res_y = (maxv.y - minv.y)*((double)(h-y))/h + minv.y;

  return res_y;
}

//=========================================================================
vector2f Canvas::reverse_transform(vector2i screen_coor)
{
  return vector2f(reverse_transform_x(screen_coor.x),reverse_transform_y(screen_coor.y));
}

//=========================================================================
void Canvas::point(float xc,float yc)
{
  int img_xc,img_yc;

  img_xc = transform_x(xc);
  img_yc = transform_y(yc);

  fl_point(img_xc,img_yc);
}

//=========================================================================
void Canvas::dot(float xc,float yc,int d)
{
  int img_xc,img_yc;
  int r;

  img_xc = transform_x(xc);
  img_yc = transform_y(yc);
  r = d/2;

  fl_pie(img_xc-r,img_yc-r,d,d,0,360);
}

//=========================================================================
void Canvas::line(float x1,float y1,float x2,float y2)
{
  int img_x1,img_y1;
  int img_x2,img_y2;

  img_x1 = transform_x(x1);
  img_y1 = transform_y(y1);
  img_x2 = transform_x(x2);
  img_y2 = transform_y(y2);

  fl_line(img_x1,img_y1,img_x2,img_y2);
}

//=========================================================================
void Canvas::rectf(float x1,float y1,float x2,float y2)
{
  int img_x1,img_y1,img_x2,img_y2;

  img_x1 = transform_x(x1);
  img_y1 = transform_y(y2);
  img_x2 = transform_x(x2);
  img_y2 = transform_y(y1);
  fl_rectf(img_x1,img_y1,img_x2-img_x1,img_y2-img_y1);
}

//=========================================================================
void Canvas::rectf_wh(float x,float y,float w,float h)
{
  int img_x,img_y,img_w,img_h;

  img_x = transform_x(x);
  img_y = transform_y(y);
  img_w = scale_x(w);
  img_h = scale_y(h);
  fl_rectf(img_x,img_y-img_h,img_w,img_h);
}

//=========================================================================
void Canvas::circlef(float xc,float yc,float r)
{
  int img_xc,img_yc;
  int d_x,d_y;
  int r_x,r_y;

  img_xc = transform_x(xc);
  img_yc = transform_y(yc);
  d_x = scale_x(2.0*r);
  d_y = scale_y(2.0*r);
  r_x = scale_x(r);
  r_y = scale_y(r);

  fl_pie(img_xc-r_x,img_yc-r_y,d_x,d_y,0,360);
}

//=========================================================================
void DisplayProcessor::add_loc(MapPoint *pt,vector2f loc)
{
  if(pt->timestamp < min_time)
    return;

  int size;
  size = (int)(2*(pt->timestamp - min_time)/1.0e6) + 1;

  //canvas->point(loc.x,loc.y);
  switch(pt->obj_id){
    case ROBJECT_WALL:        fl_color(FL_WHITE); break;
    case ROBJECT_ROBOT:       fl_color(FL_MAGENTA); break;
    case ROBJECT_RED_ROBOT:   fl_color(FL_RED  ); break;
    case ROBJECT_BLUE_ROBOT:  fl_color(FL_BLUE ); break;
    case ROBJECT_BALL:        fl_color(0xFFU,0x80U,0x00); break;
    case ROBJECT_CYAN_GOAL:   fl_color(FL_CYAN); break;
    case ROBJECT_YELLOW_GOAL: fl_color(FL_YELLOW); break;
    case ROBJECT_STRIPE:      fl_color(FL_GREEN); break;
    case ROBJECT_FIELD_CLEAR_START: fl_color(0x00U,0x40U,0x00); break;
    default:
      fl_color(FL_GRAY);
  }
  //printf("time=%lu pt->timestamp=%lu, size=%d\n",cur_time,pt->timestamp,size);
  if(size <= 1)
    canvas->point(loc.x,loc.y);
  else
    canvas->dot(loc.x,loc.y,size);
}

//=========================================================================
LocalModelTester::LocalModelTester() :
  vis_rle_decoder(8),
  vision(true)
{
  packet.data = NULL;

  cur_time = 0UL;

  imageXSize = MaxImageXSize;
  imageYSize = MaxImageYSize;

  draw_occ_grid  =true;
  draw_points    =true;
  draw_separators=true;
}

//=========================================================================
LocalModelTester::~LocalModelTester()
{
  delete[] packet.data;
  packet.data = NULL;

  delete[] img;
  img = NULL;
}

//=========================================================================
void LocalModelTester::init()
{
  packet.data = new uchar[MaxPacketLength];
  img = new uchar[MaxImageXSize*MaxImageYSize+1];

  model.init();
  vision.initialize("vision.cfg",MaxImageXSize,MaxImageYSize);
}

//=========================================================================
void LocalModelTester::run_vision(RadialObjectMap *rad_map,double *angles,uchar *img_data)
{
  CMVision::image_classified vis_img;
  VisionInterface::ObjectInfo obj_info;  

  vis_img.width  = imageXSize;
  vis_img.height = imageYSize;
  vis_img.buf = img_data;

  vision.setHeadAngles(&angles[2]);
  vision.setBodyParams(angles[0],angles[1]);
  vision.runLowLevelVision(vis_img);
  vision.runHighLevelVision(&obj_info,vis_img);

  memcpy(rad_map,vision.getRadialMap(),sizeof(RadialObjectMap));
}

//=========================================================================
void LocalModelTester::run_next_packet(bool &did_something,bool &out_of_data)
{
  did_something = false;
  out_of_data = false;

  if(log_reader.readNextPacket(&packet,MaxPacketLength)){
    cur_time = packet.timestamp;
    switch(packet.dataType){
      case PacketDecoder::MotionUpdateLead:
        {
          printf("motion update\n");

          MotionLocalizationUpdate motion_update;
          
          motion_update.pos_delta.set(0.0,0.0);
          motion_update.heading_delta.set(0.0,0.0);
          motion_update.body.neck_offset.set(100.0,0.0);

          if(mot_decoder.decode(&motion_update,&packet)){
            //printf("update for motion, pos_delta (%g,%g), heading_delta (%g,%g), neck_offset (%g,%g)\n",
            //       V2COMP(motion_update.pos_delta),
            //       V2COMP(motion_update.heading_delta),
            //       V2COMP(motion_update.body.neck_offset));

            //printf("corrected update, pos_delta (%g,%g), heading_delta (%g,%g), neck_offset (%g,%g)\n",
            //       V2COMP(motion_update.pos_delta),
            //       V2COMP(motion_update.heading_delta),
            //       V2COMP(motion_update.body.neck_offset));

            model.process_motion_update(&motion_update,packet.timestamp);
          }else{
            printf("problem decoding motion update\n");
          }

          did_something = true;
        }
        break;
      case PacketDecoder::VisionRadialMapLead:
        {
          printf("radial vision update\n");

          RadialObjectMap rad_map;

          if(vis_decoder.decode(&rad_map,&packet)){
            printf("update for radial vision\n");
            
            model.process_vision_update(&rad_map,packet.timestamp);
          }else{
            printf("problem decoding radial vision\n");
          }

          did_something = true;
        }
        break;
      case PacketDecoder::VisionRLELead:
        {
          static int frame_num=0;

          printf("vision update\n");

          RadialObjectMap rad_map;

          double angles[5];
          vis_rle_decoder.decode(angles,&packet);
          vis_rle_decoder.decode(MaxImageXSize,MaxImageYSize,img,&packet);

          printf("processing frame %d\n",frame_num);

          run_vision(&rad_map,angles,img);

          model.process_vision_update(&rad_map,packet.timestamp);
          if(!Lazy)
            model.calc_occ_grid_cells(0,0,OccGrid::EntriesHoriz-1,OccGrid::EntriesVert-1);
          //OccGridEntry cell;
          //model.calc_occupancy(&cell,vector2f(1.0,0.0),vector2f(100.0,-600.0),vector2f(200.0,200.0));
          //printf("cell obs=%g evid=%g\n",cell.obs,cell.evidence);
          //printf("confs: wall=%g unknown_obs=%g red_robot=%g blue_robot=%g ball=%g cyan_goal=%g yellow_goal=%g stripe=%g\n",
          //       cell.wall_conf,cell.unknown_obs_conf,cell.red_robot_conf,cell.blue_robot_conf,cell.ball_conf,
          //       cell.cyan_goal_conf,cell.yellow_goal_conf,cell.stripe_conf);

          printf("processed frame %d\n",frame_num);

          frame_num++;

          did_something = true;
        }
        break;
      case PacketDecoder::VisionRLE2Lead:
        {
          static int frame_num=0;

          printf("vision update2\n");

          RadialObjectMap rad_map;

          double angles[5];
          vis_rle_decoder.decode(angles,&packet);
          vis_rle_decoder.decode(imageXSize,imageYSize,img,MaxImageXSize,MaxImageYSize,&packet);

          printf("processing frame %d\n",frame_num);

          run_vision(&rad_map,angles,img);

          model.process_vision_update(&rad_map,packet.timestamp);
          if(!Lazy)
            model.calc_occ_grid_cells(0,0,OccGrid::EntriesHoriz-1,OccGrid::EntriesVert-1);
          //OccGridEntry cell;
          //model.calc_occupancy(&cell,vector2f(1.0,0.0),vector2f(100.0,-600.0),vector2f(200.0,200.0));
          //printf("cell obs=%g evid=%g\n",cell.obs,cell.evidence);
          //printf("confs: wall=%g unknown_obs=%g red_robot=%g blue_robot=%g ball=%g cyan_goal=%g yellow_goal=%g stripe=%g\n",
          //       cell.wall_conf,cell.unknown_obs_conf,cell.red_robot_conf,cell.blue_robot_conf,cell.ball_conf,
          //       cell.cyan_goal_conf,cell.yellow_goal_conf,cell.stripe_conf);

          printf("processed frame %d\n",frame_num);

          frame_num++;

          did_something = true;
        }
        break;
      default:
        //printf("packet of type 0x%x\n",packet.dataType);
        break;
    }
  }else{
    printf("end of log\n");
    out_of_data = true;
    auto_playback_mode = false;
  }
}

//=========================================================================
void LocalModelTester::draw_data(AppWindow *win,Canvas &canvas)
{
  //canvas.set_range(vector2f(000.0,-500.0),vector2f(600.0,100.0));
  canvas.set_range(vector2f(-2000.0,-2000.0),vector2f(2000.0,2000.0));

  fl_color(FL_BLACK);
  fl_rectf(0,0,canvas.w,canvas.h);

  fl_color(0x00,0x80U,0x80U);
  canvas.rectf(-200.0,-100.0,0.0,100.0);

  fl_color(FL_WHITE);

  static const float block_size=OccGrid::CellSize;
  static const float query_area=2000.0;

  for(int x=(int)(-query_area/block_size); x<(int)(query_area/block_size); x++){
    float xf;
    xf = x * block_size;
    canvas.line(xf,-query_area,xf,query_area);
  }  
  
  for(int y=(int)(-query_area/block_size); y<(int)(query_area/block_size); y++){
    float yf;
    yf = y * block_size + block_size/2.0;
    canvas.line(-query_area,yf,query_area,yf);
  }  

  if(draw_occ_grid){
    // draw occ grid
    double x1,y1;
    double xh,yh;
    xh = yh = OccGrid::CellSize*0.9;
    for(int x_cell=0; x_cell<OccGrid::EntriesHoriz; x_cell++){
      x1 = OccGrid::CellSize*(x_cell - OccGrid::EntryCenterX) + OccGrid::CellSize*0.05;

      for(int y_cell=0; y_cell<OccGrid::EntriesVert; y_cell++){
        y1 = OccGrid::CellSize*(y_cell - OccGrid::EntryCenterY)-OccGrid::CellSize/2.0 + OccGrid::CellSize*0.05;

        const OccGridEntry *cell;
        float obs,evidence;
        
        cell = model.get_occ_grid_cell(x_cell,y_cell);
        obs      = cell->obs     ;
        evidence = cell->evidence;

        if(evidence > OccGridEntry::KnownThreshold){
          uchar grey;

          grey = (uchar)(255.0*obs);
          if(obs >= OccGridEntry::ObsPresentThreshold)
            fl_color(grey,0,0); // draw in red scale
          else
            fl_color(grey,grey,grey); // draw in grey scale
        }else{
          // draw in unknown color
          fl_color(0x00U,0x00,0xFF);
        }
        canvas.rectf_wh(x1,y1,xh,yh);
      }
    }
  }
  if(draw_points){
    DisplayProcessor proc;
    proc.canvas = &canvas;
    static const ulong time_window = SecToTime(3.0);
    proc.cur_time = cur_time;
    if(cur_time > time_window)
      proc.min_time = cur_time - time_window;
    else
      proc.min_time = 0UL;
    for(int x=(int)(-query_area/block_size); x<(int)(query_area/block_size); x++){
      for(int y=(int)(-query_area/block_size); y<(int)(query_area/block_size); y++){
        float xf1,xf2,yf1,yf2;
        
        xf1 = x * block_size;
        xf2 = xf1 + block_size;
        
        yf1 = y * block_size - block_size/2.0;
        yf2 = yf1 + block_size;
        
        model.query_simple(vector2f(xf1,yf1),vector2f(xf2,yf2),proc);
      }
    }
  }
  if(draw_separators){
    double x1,y1;
    double xh,yh;
    xh = yh = OccGrid::CellSize*0.9;
    for(int x_cell=0; x_cell<OccGrid::EntriesHoriz; x_cell++){
      x1 = OccGrid::CellSize*(x_cell - OccGrid::EntryCenterX) + OccGrid::CellSize*0.05;

      for(int y_cell=0; y_cell<OccGrid::EntriesVert; y_cell++){
        y1 = OccGrid::CellSize*(y_cell - OccGrid::EntryCenterY)-OccGrid::CellSize/2.0 + OccGrid::CellSize*0.05;

        //pprintf(TextOutputStream,"calculating cell for (%g,%g)\n",x1,y1);

        const OccGridEntry *cell;
        float obs;
        
        cell = model.get_occ_grid_cell(x_cell,y_cell);
        obs      = cell->obs     ;

        if(obs < OccGridEntry::ObsPresentThreshold)
          continue;

        SeparatorEntry entry;
        
        vector2f center;
        float xb1,xb2,yb1,yb2;
        xb1 = OccGrid::CellSize * (x_cell     - OccGrid::EntryCenterX);
        xb2 = OccGrid::CellSize * (x_cell + 1 - OccGrid::EntryCenterX);
        yb1 = OccGrid::CellSize * (y_cell     - OccGrid::EntryCenterY) - OccGrid::CellSize/2.0;
        yb2 = OccGrid::CellSize * (y_cell + 1 - OccGrid::EntryCenterY) - OccGrid::CellSize/2.0;
        center.set((xb1+xb2)/2,(yb1+yb2)/2);
        
        model.calc_separator(&entry,vector2f(1.0,0.0),center,vector2f(OccGrid::CellSize,OccGrid::CellSize));

        vector2f plane_pt,p1,p2;
        float side_dist;
        side_dist = center.dot(entry.plane_n.perp());
        plane_pt = entry.plane_n * entry.orig_dist;
        plane_pt += entry.plane_n.perp() * side_dist;
        p1 = plane_pt + entry.plane_n.perp() * (+OccGrid::CellSize/2.0);
        p2 = plane_pt + entry.plane_n.perp() * (-OccGrid::CellSize/2.0);

        fl_color(0xFFU,0xFFU,0x00U);
        canvas.line(V2COMP(p1),V2COMP(p2));
        fl_color(0xFFU,0x00U,0x00U);
        canvas.dot(V2COMP(plane_pt),5);
        //pprintf(TextOutputStream,"drawing line from (%g,%g) to (%g,%g)\n",V2COMP(p1),V2COMP(p2));
      }
    }
  }
}

//=========================================================================
void LocalModelTester::run_debug_xy(float x,float y)
{
  model.debug_support(x,y);
}

//=========================================================================
AppWindow::AppWindow() :
  Fl_Double_Window(0,0,width,height)
{
  resizable(this);

  canvas.set_size(width,height);

  keep_running = true;

  log_filename = NULL;
  log_file = NULL;

  tester.init();
}

//=========================================================================
AppWindow::~AppWindow()
{
  if(log_filename)
    free(log_filename);

  if(log_file)
    fclose(log_file);
}

//=========================================================================
void AppWindow::set_log_file(const char *_log_filename)
{
  if(log_filename)
    free(log_filename);

  if(log_file)
    fclose(log_file);

  log_filename = strdup(_log_filename);

  log_file = fopen(log_filename,"r");
  if(log_file == NULL){
    cerr << "Couldn't open log file 'test.log'" << endl;
    exit(2);
  }
  tester.log_reader.setSource(log_file);
}

//=========================================================================
void AppWindow::resize(int x,int y,int w, int h)
{
  Fl_Double_Window::resize(x,y,w,h);

  canvas.set_size(w,h);
}

//=========================================================================
void AppWindow::draw()
{
  tester.draw_data(this,canvas);
}

//=========================================================================
void AppWindow::advanceFrame() {

  ulong start_time,end_time;
    
  start_time = GetTime();
  cout << "Drawing time " << start_time << endl;
  bool did_something=false;
  bool out_of_data=false;
  while(!(did_something || out_of_data)){
    tester.run_next_packet(did_something,out_of_data);
  }
  
  end_time = GetTime();
  
  if(did_something){
    double elapsed_time;
    elapsed_time = (end_time-start_time)/1.0e6;
    if(ProfileMode)
      elapsed_time /= RepeatCount;
    
    fprintf(stderr,"took %g seconds\n",elapsed_time);
  }
  redraw();
  if(save_file_mode) {
    snprintf(save_file_name,1024,"lmt_%.6d.xwd",save_file_count++);
    saveFrame(save_file_name);
  }
}

void AppWindow::saveFrame(const char * save_file_name) {
  if (win->visible()) {
    if (0!=window_capture(fl_display,
    			fl_xid(win), 
			save_file_name)){
      cerr << "Warning: Couldn't save " << save_file_name << endl;
    }
  }
}

//=========================================================================
void AppWindow::printKeyHelp() {
  cout << "a      : toggle auto-advance" << endl;
  cout << "c      : capture a single frame" << endl;
  cout << "o      : toggle draw occupancy grid" << endl;
  cout << "p      : toggle draw points" << endl;
  cout << "ESC, q : quit the program" << endl;
  cout << "s      : toggle saving each frame to a file" << endl;
  cout << "' '    : advance the time" << endl;
}

//=========================================================================
int AppWindow::handle(int event)
{
  switch(event){
    case FL_KEYDOWN:
      {
        int key = Fl::event_key();
        switch(key){
	  case 'h':
	    // There's nothing like on-line help information to make
	    // your life easier...
	    printKeyHelp();
	  break;
 	  case 'a':
	    // Toggle auto-playback mode
	    // Starts a process which plays the files back at a fixed
	    // rate
	    cout << "Auto-playback mode = " << endl;
	    auto_playback_mode=!auto_playback_mode;
	    if (auto_playback_mode) {
	      Fl::add_timeout(auto_playback_rate,auto_repeat_cb,this);
	      cout << "on" << endl;
	    } else {
	      cout << "off" << endl;
	    }
	    break;
	  case 'c':
	    saveFrame("capture.xwd");
	    break;
   	  case 's':
	    // Toggle file-save mode
	    // Every frame that gets drawn is saved as a file whose
	    // filename is timestamped
	    cout << "Auto-save mode = " << endl;
	    save_file_mode=!save_file_mode;
	    if (save_file_mode) {
	      cout << "on" << endl;
	    } else {
	      cout << "off" << endl;
	    }
	    break;
          case FL_Escape:
          case 'q':
            keep_running = false;
            break;
          case 'p':
	    cout << "Toggling drawn points" << endl;
            tester.toggle_draw_points();
            draw();
            break;
          case 'o':
	    cout << "Toggling occupancy grid" << endl;
            tester.toggle_draw_occ_grid();
            draw();
            break;
          case ']':
          case FL_Right:
          case FL_Down:
          case ' ':
          case FL_Page_Down:
	    advanceFrame();
	    break;
	default:
	  return 0;
        }
      }
      return 1;
      break;
    case FL_PUSH:
      {
        int xs,ys;

        xs = Fl::event_x();
        ys = Fl::event_y();

        printf("xs=%02d ys=%02d\n",xs,ys);

        float x,y;

        x = canvas.reverse_transform_x(xs);
        y = canvas.reverse_transform_y(ys);

        printf("x=%g ys=%g\n",x,y);

        tester.run_debug_xy(x,y);
      }
      return 1;
      break;
  }

  return 0;
}

//=========================================================================
void AppWindow::run()
{
  while(keep_running) {
    if(Interactive) {
      Fl::check();
    }

    if(!Interactive) {
      ulong start_time,end_time;
    
      start_time = GetTime();
              
      bool did_something=false;
      bool out_of_data=false;
      while(!(did_something || out_of_data)){
        tester.run_next_packet(did_something,out_of_data);
      }
      
      end_time = GetTime();
      
      if(did_something){
        double elapsed_time;
        elapsed_time = (end_time-start_time)/1.0e6;
        if(ProfileMode)
          elapsed_time /= RepeatCount;
        
        fprintf(stderr,"took %g seconds\n",elapsed_time);
      }

      if(out_of_data)
        keep_running = false;
    }

    if(Interactive)
      usleep(10000);
  }
}

//=========================================================================
int main(int argc, char *argv[])
{
  int next_opt;
  for(next_opt=1; next_opt < argc && argv[next_opt][0]=='-'; next_opt++) {
    char *opt;
    opt = argv[next_opt];
    if(strcmp(opt,"--profile")==0) {
      ProfileMode = true;
      Lazy = false;
    } else if(strcmp(opt,"--go")==0) {
      GoMode = true;
    } else if(strcmp(opt,"--repeat")==0) {
      next_opt++;
      if(!(next_opt < argc)) {
        fprintf(stderr,"must specify numeric argument after --repeat\n");
        exit(5);
      }
      RepeatCount = atoi(argv[next_opt]);
    }
  }
  argc -= next_opt;
  argv = &argv[next_opt];

  Interactive = !(ProfileMode || GoMode);

  if(argc < 1) {
    fprintf(stderr,"must specify log file\n");
    exit(1);
  }

  Fl::visual(FL_DOUBLE|FL_INDEX);

  win = new AppWindow;
  win->label("Local Model Test");

  win->set_log_file(argv[0]);

  win->show();

  cout << "Press 'h' to see on-line help options" << endl;

  win->run();

  return(0);
}

