/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    Last edit by hansen on Mon Jan 19 13:58:55 2004
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "tkgate.h"

int scope_active = 0;

int clickValue = 0;


extern GScope *Scope;

void ReqScopeTraceRedisplay();

#if !TKGATE_BROKENCONFIGWIDGET
static Tk_ConfigSpec configSpecs[] = {
  {TK_CONFIG_COLOR, "-background", "background", "Background",
     "white", Tk_Offset(GScope,bgColor), 0, 0},
  {TK_CONFIG_SYNONYM, "-bg", "background", 0, 0, 0, 0, 0},
  {TK_CONFIG_SYNONYM, "-fg", "foreground", 0, 0, 0, 0, 0},
  {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
     "black", Tk_Offset(GScope,fgColor), 0, 0},
  {TK_CONFIG_INT, "-height", "height", "Height",
     STR(TKG_GATEWIN_HEIGHT), Tk_Offset(GScope,Height), 0, 0},
  {TK_CONFIG_INT, "-width", "width", "Width",
     STR(TKG_GATEWIN_WIDTH), Tk_Offset(GScope,Width), 0, 0},
  {TK_CONFIG_STRING, "-xscrollcommand", "xscrollcommand",  "Xscrollcommand", 
     "", Tk_Offset(GScope,xscroll), 0, 0},
  {TK_CONFIG_STRING, "-yscrollcommand", "yscrollcommand",  "Yscrollcommand", 
     "", Tk_Offset(GScope,yscroll), 0, 0},
  {TK_CONFIG_END, 0, 0, 0, 0, 0, 0, 0}
};
#endif

static int scopeWinConfigure(Tcl_Interp *tcl, GScope *sw, int argc, const char *argv[], int flags)
{
#if TKGATE_BROKENCONFIGWIDGET
  sw->Width = 500;
  sw->Height = 400;
  sw->xscroll = ".scope.main.horz set";
  sw->yscroll = ".scope.main.vert set";

  Tk_SetWindowBackground(sw->win, XWhitePixelOfScreen(XGate.S));
#else
  if (Tk_ConfigureWidget(tcl, sw->win, configSpecs, argc, argv, (char*) sw, flags) != TCL_OK)
    return TCL_ERROR;
  Tk_SetWindowBackground(sw->win, sw->bgColor->pixel);
#endif

  Tk_GeometryRequest(sw->win,sw->Width,sw->Height);

  if (sw->gc == None) {
    XGCValues gcv;

    gcv.function = GXcopy;
    gcv.graphics_exposures = False;
    sw->gc = Tk_GetGC(sw->win,GCFunction|GCGraphicsExposures, &gcv);
  }

  return TCL_OK;
}

static void scopeWinEvent(ClientData data, XEvent *E)
{
  if (E->type == DestroyNotify) {
    int old_mode = ob_get_mode();

    scope_active = 0;
    ob_mode(OM_DISABLED);
    ob_clear();
    delete_GScope(Scope);
    ob_mode(old_mode);
    Scope = 0;
  }
  ReqScopeRedisplay();
}

static void scopeYViewCommand(GScope *S,const char *command,const char *arg)
{
  int visTraces = (S->Height-ScopeBOTTOMHEIGHT)/ScopeTRACEHEIGHT;
  int newStart = S->Start;
  double f;
  int n;

  if (strcmp(command,"moveto") == 0) {
    if (S->NumTraces) {
      sscanf(arg,"%lf",&f);
      newStart = S->NumTraces*(f+0.5/(double)S->NumTraces);
    }
  } else if (strcmp(command,"scroll") == 0) {
    sscanf(arg,"%d",&n);
    if (n < 0)
      newStart--;
    else if (n > 0)
      newStart++;
  }
  
  if (newStart >= S->NumTraces-visTraces)
    newStart = S->NumTraces-visTraces;
  if (newStart < 0)
    newStart = 0;
  
  if (newStart != S->Start) {
    S->Start = newStart;
    ReqScopeRedisplay();
  }
}

void scopeXViewCommand(GScope *S,const char *command,const char *arg)
{
  int newLeftTime = S->LeftTime;
  double f;
  int n;

  if (strcmp(command,"moveto") == 0) {
    int NI = S->Interval ? (S->Time/S->Interval) : 0;
    if (NI) {
      sscanf(arg,"%lf",&f);
      n = NI*(f+0.5/(double)NI);
      if (n < 0) n = 0;
      if (n > NI)
	n = NI-1;
      newLeftTime = n*S->Interval;
    }
  } else if (strcmp(command,"scroll") == 0) {
    sscanf(arg,"%d",&n);
    if (n < 0)
      newLeftTime -= S->Interval;
    else if (n > 0)
      newLeftTime += S->Interval;
  }
  if (newLeftTime > S->Time)
    newLeftTime = (S->Time/S->Interval)*S->Interval;
  if (newLeftTime < 0)
    newLeftTime = 0;
  
  if (newLeftTime != S->LeftTime) {
    S->LeftTime = newLeftTime;
    ReqScopeRedisplay();
  }
}

static void scopeZoomCommand(GScope *S,const char *arg)
{
  int d,r;

  r = S->RangePos;
  if (sscanf(arg,"%d",&d) == 1)
    r += d;

  if (r < 0) r = 0;
  if (r >= MAXRANGEPOS) r = MAXRANGEPOS-1;

  if (r != S->RangePos) {
    extern int RangeVals[];
    int oldMiddle;
    double f;

    if (S->Time < RangeVals[r]) {
      oldMiddle = 0;
      f = 0.0;
    } else if (S->Time <= S->LeftTime + S->Range) {
      oldMiddle = S->Time;
      f = ((double)S->Time-S->LeftTime)/(double)S->Range;
    } else {
      oldMiddle = S->LeftTime + S->Range/2;
      f = 0.5;
    }

    S->RangePos = r;
    S->Range = RangeVals[S->RangePos];
    PickInterval(&S->Range,&S->Interval);

    Scope->LeftTime = ((oldMiddle - Scope->Range*f)/Scope->Interval)*Scope->Interval;
    if (S->LeftTime < 0) S->LeftTime = 0;

    ReqScopeRedisplay();
  }
}

void scopeButtonMotion(int x,int y,int n,int state)
{
  int pmin = ScopeLEFTMARGIN;
  int pmax = Scope->Width-ScopeRIGHTMARGIN;
  double f;

  GScope_drawCrossLine(Scope,x);

  /*  printf("scopeButtonMotion(%d, %d)\n",x,y);*/
  if (Scope->mark_count == 1) {
    GScope_moveSelection(Scope);
  }

  if (!(state & Button1MotionMask)) return;

  if (x < pmin || x > pmax || clickValue < 0) return;
  f = ((double)x-pmin)/(double)(pmax-pmin);

  Scope->LeftTime = ((clickValue - Scope->Range*f)/Scope->Interval)*Scope->Interval;
  if (Scope->LeftTime < 0) Scope->LeftTime = 0;

  ReqScopeTraceRedisplay();
}

void scopeButtonRelease(int x,int y,int n,int state)
{
  scopeButtonMotion(x,y,n,state);
  if (n == 1)
    ReqScopeRedisplay();
  else if (n == 3)
    GScope_setMark(Scope,x,0,state);
}

void scopeButtonPress(int x,int y,int n,int state)
{
  int pmin = ScopeLEFTMARGIN;
  int pmax = Scope->Width-ScopeRIGHTMARGIN;
  extern int RangeVals[];
  double f;

  if (x < pmin || x > pmax) {
    clickValue = -1;
    return;
  }

  f = ((double)x-pmin)/(double)(pmax-pmin);
  clickValue = Scope->LeftTime + Scope->Range*f;

#if 0
  if (clickValue > Scope->Time)
    clickValue = Scope->Time;
#endif

  if ((state & ControlMask)) {
    int r = 0;

    if (n == 1)
      r = Scope->RangePos-1;
    else if (n == 3)
      r = Scope->RangePos+1;
    
    if (r < 0) r = 0;
    if (r >= MAXRANGEPOS) r = MAXRANGEPOS-1;

    Scope->RangePos = r;
    Scope->Range = RangeVals[Scope->RangePos];
    PickInterval(&Scope->Range,&Scope->Interval);
  }

  if (n == 1) {
    Scope->LeftTime = ((clickValue - Scope->Range*f)/Scope->Interval)*Scope->Interval;
    if (Scope->LeftTime < 0) Scope->LeftTime = 0;
  } else if (n == 3) {
    GScope_setMark(Scope,x,1,state);
  }

  ReqScopeRedisplay();
}

static int scopeWinCommand(ClientData data,Tcl_Interp *tcl,int argc,const char *argv[])
{
  if (strcmp(argv[1],"yview") == 0) {
    scopeYViewCommand(Scope,argv[2],argv[3]);
  } else if (strcmp(argv[1],"xview") == 0) {
    scopeXViewCommand(Scope,argv[2],argv[3]);
  } else if (strcmp(argv[1],"zoom") == 0) {
    scopeZoomCommand(Scope,argv[2]);
  }

  return TCL_OK;
}

void scopeWinDestroy(ClientData data)
{
}


/*
   Create a scope window
*/
int gat_scope(ClientData data, Tcl_Interp *tcl, int argc, const char *argv[])
{
  Tk_Window root = (Tk_Window) data;
  GScope *sw;
  Tk_Window w;

  if (argc < 2) {
    Tcl_AppendResult(tcl, "wrong # args: should be \"",argv[0], " pathName ?options?\"", 0);
    return TCL_ERROR;
  }

  w = Tk_CreateWindowFromPath(tcl, root, argv[1], 0);
  if (!w) return TCL_ERROR;
  Tk_MakeWindowExist(w);
  Tk_SetClass(w, "ScopeWin");

  sw = new_GScope();
  sw->win = w;
  sw->d = Tk_Display(w);
  sw->tcl = tcl;
  sw->x = sw->y = 0;
  sw->Width = TGK_GATEWIN_WIDTH;
  sw->Height = TGK_GATEWIN_HEIGHT;
  sw->gc = None;
  sw->bgColor = sw->fgColor = 0;
  sw->xscroll = 0;
  sw->yscroll = 0;

  Scope = sw;
  XGate.ScopeW = Tk_WindowId(sw->win);

  Tk_CreateEventHandler(w, ExposureMask|StructureNotifyMask, scopeWinEvent, sw);
  Tcl_CreateCommand(tcl, Tk_PathName(w), scopeWinCommand, sw, scopeWinDestroy);
  if (scopeWinConfigure(tcl, sw, argc-2, argv+2,0) != TCL_OK) {
    Tk_DestroyWindow(w);
    return TCL_ERROR;
  }

  tcl->result = Tk_PathName(w);

  scope_active = 1;

  return TCL_OK;
}
