// Please see the README file or the LEGAL NOTES-section in the manual before modifying, compiling or using 'xmrm'
//   Idea: Manfred Kopp
//   Programming: Gerhard Waldhör, Andreas Artmann

#include <forms.h>
#include "wavemorph.h"
#include "morphvec.h"
#include "io.h"
#include "init.h"
#include "xmrm.h"
#include "const.h"

//extern ControlClass control;
extern PictureClass *s_pic, *d_pic, *result_pic;
extern MorphVecClass *s_vec,*d_vec, *i_vec;
extern FL_OBJECT *obj_s, *obj_d, *obj_vs, *obj_vd, *obj_m, *obj_a;
extern WindowClass *m_win,*d_win;

extern FD_MRM *fd_MRM; 
extern FD_RESULT *fd_RESULT;
extern FD_MORPH *fd_MORPH;
extern FD_AREAS *fd_AREAS;
extern FD_SLID_CONTR *fd_SLID_CONTR;
extern FD_VEC_MENU *fd_VEC_MENU;

extern MyImageClass *cinema[MAX_PIC],*MyImage;

//extern double Bias(double x_bias,double b_bias);

extern ControlClass control;

void Interpol_Bias()
{
  int begin,end,k;
  double dist,add;

  if (control.advanced)
  {
    begin=control.select_level-1;
    end=control.select_level+1;

    if (begin<1) 
      begin=1;
    else
      while (!fl_get_button(fd_MRM->BT_Level[begin-1])) begin--;

    if (end>MAX_LEVELS)
      end=MAX_LEVELS;
    else
      while (!fl_get_button(fd_MRM->BT_Level[end-1])) end++;

    //  printf("begin: %d  select: %d  end: %d\n",begin,control.select_level,end);

    dist = (control.b_bias_val[control.select_level-1] - control.b_bias_val[begin-1]) / 
           (control.select_level-begin);
    add=0;
    for (k=begin; k<control.select_level; k++)
    {
      control.b_bias_val[k-1] = control.b_bias_val[begin-1] + add;
      add+=dist;
    }

    dist = (control.b_bias_val[end-1] - control.b_bias_val[control.select_level-1]) / 
           (end-control.select_level);
    add=0;
    for (k=control.select_level; k<end; k++)
    {
      control.b_bias_val[k-1] = control.b_bias_val[control.select_level-1] + add;
      add+=dist;
    }
  }
  else
  {
    dist = (1 - 2*control.b_bias_val[0]) / (MAX_LEVELS-1);
//    printf("%f \n",dist);
    add=0;
    for (k=0; k<MAX_LEVELS; k++)
    {
      control.b_bias_val[k] = control.b_bias_val[0] + add;
      add+=dist;
    }
  }
  
}

void Plot_Wave_Func(FL_OBJECT *obj)
{
  double x_val, y_val;
  int x_old, y_old;
  double scale_x, scale_y;
  double step_x;
  int i,j,l;
  FL_COLOR col;
  
  scale_x = (double)obj->w;
  scale_y = (double)obj->h;
  step_x = 1.0/(double)obj->h;

  if (fl_get_button(fd_MRM->CB_Use_Wavelets)) l=MAX_LEVELS; else l=1;

  for (i=0; i<l; i++)
  {
    x_val = y_val = 0.0;
    x_old = 0;

    if (fl_get_button(fd_MRM->BT_Level[i])) 
      col=FL_BLUE; 
    else 
      col=FL_BLACK;
    if (i == (control.select_level-1)) col=FL_RED;

    for (j=0; j<=(obj->h-1); j++)
    {
      y_old = (int)(y_val*scale_y);
      y_val = Bias(x_val,control.b_bias_val[i]);

      fl_line(x_old, obj->h-1 - y_old, (int)(x_val*scale_x), obj->h-1 - (int)(y_val*scale_y), col);

      x_old = (int)(x_val*scale_x);
      x_val += step_x;
    }
    y_old = (int)(y_val*scale_y);
    y_val = Bias(x_val,control.b_bias_val[i]);
    fl_line(x_old, obj->h-1 - y_old, (int)(x_val*scale_x), obj->h-1 - (int)(y_val*scale_y), col);
  }
}

#define M_SIZE 4
void Gauss_Eliminate(double A[M_SIZE][M_SIZE],double b[M_SIZE],int n)
{
  int p,q,i,j;
  double op;
  
  /* ONLY FOR REGULAR EQUATION-SYSTEMS, NO Pivot-Strategy implemented (But that's enough for our purposes) */

  // First form a triangle matrix:
  for (q=0; q<M_SIZE-1; q++)
  {
    // if diagonal element==0 then swap with one != 0
    if (A[q][q] == 0)
    {
      j=q+1;
      while ( (A[j][q] == 0) && (j<M_SIZE) ) j++; // NO TEST for all elements == 0 (When regular, not needed (?))
      
      // Swap diagonal-element-line with no_zero-element-line:
      for(i=0; i<M_SIZE; i++) { op=A[j][i]; A[j][i]=A[q][i]; A[q][i]=op; }
      op=b[j]; b[j]=b[q]; b[q]=op;    
    }

    for (p=q+1; p<M_SIZE; p++)
    {
      if (A[p][q] != 0)
      {
        // Elimination Step:
        op=-A[p][q]/A[q][q];
        for (i=q; i<M_SIZE; i++) 
          A[p][i]+=A[q][i]*op;
        b[p]+=b[q]*op;
      }
    }
  }
  // Calculate coeffs recursively:
  for (p=M_SIZE-1; p>=0; p--)
  {
    op=0;
    for (q=p+1; q<M_SIZE; q++) op+=A[p][q]*control.akima_f[n][q];
    control.akima_f[n][p]=(b[p]-op) / (A[p][p]);
  }
}

void Calc_Akima(double P[AKIMA_MAX][2])
{
  double dq[AKIMA_MAX+4],s[AKIMA_MAX],norm,op1,op2;
  double A[4][4],b[4];
  int i,j,n;
  #define AD 2
  n=control.akima_nr-1;

  // Calculate dq_i = dy_i / dx_i
  for (i=0; i<n; i++)
    dq[i+AD] = (P[i+1][1] - P[i][1]) / (P[i+1][0] - P[i][0]);
  
  dq[-1+AD]=2*dq[0+AD]-dq[1+AD]; 
  dq[-2+AD]=2*dq[-1+AD]-dq[0+AD];
  dq[n+AD]=2*dq[n-1+AD]-dq[n-2+AD]; 
  dq[n+1+AD]=2*dq[n+AD]-dq[n-1+AD];
 
  // Calculate s_i
  for (i=0; i<=n; i++)
  {
    norm=fabs(dq[i+1+AD]-dq[i+AD])+fabs(dq[i-1+AD]-dq[i-2+AD]);
    
    if (norm==0)
      s[i]=(dq[i-1+AD]+dq[i+AD])/2;
    else
      s[i]=( fabs(dq[i+1+AD]-dq[i+AD])*dq[i-1+AD] + fabs(dq[i-1+AD]-dq[i-2+AD])*dq[i+AD] ) / norm;
  }
  
  // Loop over all segments:
  for (i=0; i<n; i++)
  {
    // f(x_l) = y_l ; f(x_r) = y_r
    op1=1; op2=1;
    for (j=3; j>=0; j--)
    {
      A[0][j]=op1; A[1][j]=op2;      
      op1=op1*P[i][0];
      op2=op2*P[i+1][0];
    }
    b[0]=P[i][1]; b[1]=P[i+1][1];
    
    // f'(x_l) = s_l ; f'(x_r) = s_r
    A[2][3]=0; A[3][3]=0;
    A[2][2]=1; A[3][2]=1;
    op1=P[i][0]; op2=P[i+1][0];
    A[2][1]=2*op1; A[3][1]=2*op2;
    A[2][0]=3*op1*op1; A[3][0]=3*op2*op2;
     
    b[2]=s[i]; b[3]=s[i+1];

    // Solve Equations with Gaussean Algorithm:
    Gauss_Eliminate(A,b,i);
  }
}

double Get_Akima(double x)
{
  int i,j;
  double op,result;

  // correctction of right border:
  if (x>=0.999) x=0.999;

  // Search the rigth segment:    
  j=0;
  while (control.akima_P[j][0] <= x) j++;
  j--;

  // Calculate y=akima_f(x)=ax^3+bx^2+cx+d;
  op=1; result=0;
  for (i=3; i>=0; i--)
  {
    result += control.akima_f[j][i]*op;
    op *= x;
  }
  
  return result;
}

void Draw_Akima(FL_OBJECT *obj)
{
  int i,j,k,ox,oy,bx,by;
  double x,y,dx;
  
  Calc_Akima(control.akima_P);              

  bx=obj->x; by=obj->y+obj->h;
  ox=0; oy=(int)(Get_Akima(0)*obj->h); 
  x=0; y=0; dx=1 / (double)obj->w;

  for (k=0; k<obj->w; k++)
  {
    y=Get_Akima(x); j=(int)((double)y*obj->h);
    fl_line(bx+ox,by-oy,bx+k,by-j,FL_BLUE);

    ox=k; oy=j;
    x+=dx;
  }
}

void Draw_Akima_Points(FL_OBJECT *obj, int colored_point)
{
  int i,x,y;
  
  for (i=0; i<(control.akima_nr); i++)
  {
    x=obj->x-POINT_SIZE/2 + (int)(control.akima_P[i][0]*(double)obj->w);
    y=obj->y+obj->h-POINT_SIZE/2 - (int)(control.akima_P[i][1]*(double)obj->h);

    fl_ovall(x, y, POINT_SIZE, POINT_SIZE, i==colored_point ? FL_RED : FL_BLACK);
  }
}

int obj_akima_handler(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, int key, void *xev)
{
  int i,j,x,y,same_x;
  unsigned long dx,dy,dist;
  static int edit=0,found,colored_point=-1,lx,rx;
  #define A_SEARCH_RAD (POINT_SIZE/2)*(POINT_SIZE/2)
  
  x=mx-obj->x; y=obj->y+obj->h-my;

  switch(event)
  {
    case FL_ENTER:
      fl_set_object_label(fd_MRM->Infoline,"X-axis: morph-sequence - Y-axis: bottom:source- top:destination-image");
      break;
      
    case FL_LEAVE:
      fl_set_object_label(fd_MRM->Infoline,NULL);
      colored_point = -1;
      fl_redraw_object(obj);
      break;
      
    case FL_DRAW:
      fl_rectf(obj->x,obj->y,obj->w,obj->h,FL_COL1);
      Draw_Akima(obj);
      Draw_Akima_Points(obj, colored_point);
      break;

    case FL_MOUSE:
      if (edit)
      {
        if (x<lx) x=lx;
        if (x>rx) x=rx;
          
        if (y<0) y=0;
        if (y>obj->h) y=obj->h;

        control.akima_P[found][0] = (double)x / obj->w;
        control.akima_P[found][1] = (double)y / obj->h;        
          
        fl_redraw_object(obj);
      }
      break;

    case FL_MOTION:
      colored_point = -1;
      for (i=0; i<control.akima_nr; i++)
      {
        dx = x-(int)(control.akima_P[i][0]*(double)obj->w);
        dy = y-(int)(control.akima_P[i][1]*(double)obj->h);
        if ( (dx*dx+dy*dy) <= A_SEARCH_RAD )
          colored_point = i;
      }
      fl_redraw_object(obj);
      break;
      
    case FL_PUSH:
      // SET NEW POINT:
      found=-1;
      for (i=0; i<control.akima_nr; i++)
      {
        dx=x-(int)(control.akima_P[i][0]*(double)obj->w);
        dy=y-(int)(control.akima_P[i][1]*(double)obj->h);
        if ( (dx*dx+dy*dy)<=A_SEARCH_RAD ) found=i;
      }

      if (found == -1)
      {
        // test new point possible:
        same_x=0;
        for (i=0; i<control.akima_nr; i++)
        { 
          j=(int)(control.akima_P[i][0]*(double)obj->w);
          if ( ((j-POINT_SIZE) < x) && (x < (j+POINT_SIZE)) ) same_x=1;      
        }
          
        if ( (same_x) || (control.akima_nr == AKIMA_MAX) || (key != 1) ) break;
        
        // position of new point:
        j=0;
        while ((int)(control.akima_P[j][0]*(double)obj->w) < x) j++;
                
        for (i=control.akima_nr; i>j; i--)
        {
          control.akima_P[i][0] = control.akima_P[i-1][0];
          control.akima_P[i][1] = control.akima_P[i-1][1];          
        }
        // get new point data:
        control.akima_P[j][0] = (double)x / obj->w;
        control.akima_P[j][1] = (double)y / obj->h;        
        control.akima_nr++;
        // redraw:
        fl_redraw_object(obj);
      }
      else
      {
        // EDIT MODE ON:
        if (key == 1)
        {
          edit=1;
          
          if (found == 0) { lx=rx=0; break; }
          else rx=(int)(control.akima_P[found+1][0]*(double)obj->w)-POINT_SIZE;
          
          if (found == control.akima_nr-1) { lx=rx=obj->w; break; }
          else lx=(int)(control.akima_P[found-1][0]*(double)obj->w)+POINT_SIZE;
        }
        
        // DELETE POINT:
        if (key == 3)
        {
          if ( (control.akima_nr==AKIMA_MIN) || (found==0) || (found==control.akima_nr-1) )break;
        
          for (i=found; i<control.akima_nr-1; i++)
          {
            control.akima_P[i][0]=control.akima_P[i+1][0];
            control.akima_P[i][1]=control.akima_P[i+1][1];
          }
          control.akima_nr--;
          
          fl_redraw_object(obj);
        }
      }
      break;
      
    case FL_RELEASE:
      // EDIT MODE OFF:
      if (edit) edit=0;
      break;
  }
  return 0;
}

int obj_wavelets_handler(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, int key, void *xev)
{
  char *s1, *s2, *s3;

  switch(event)
  {
    case FL_ENTER:
      fl_set_object_label(fd_MRM->Infoline,"Press right button for detailed information...");
      break;
      
    case FL_LEAVE:
      fl_set_object_label(fd_MRM->Infoline,NULL);
      break;
      
    case FL_DRAW:
      fl_rectf(obj->x,obj->y,obj->w,obj->h,FL_COL1);
      Interpol_Bias();
      Plot_Wave_Func(obj);
      break;
      
    case FL_PUSH:
      if (key >= 2)
      {
        s1="When using the wavelet decomposition, use the right slider to control";
        s2="the speed at which the different detail-levels are morphed !\n";
        s3="The higher the value of F(x), the sooner the details will emerge !";
        fl_show_message(s1,s2,s3);
      }
      break;
      
    default:
      break;
  }
  return 0;
}


/* callbacks for form MRM */

void callback_Sliders(FL_OBJECT *ob, long arg)
{
  switch (arg)
  {
    case 1:// Distance influence
      control.warp_a = fl_get_slider_value(ob);
      break;
    case 2:// different lines influence
      control.warp_b = fl_get_slider_value(ob);
      break;
    case 3:// length influence
      control.warp_p = fl_get_slider_value(ob);
      break;
    case 4:// Wavelet-level
      control.slider_bias = fl_get_slider_value(ob);
      if (control.advanced) 
        control.b_bias_val[control.select_level-1] = control.slider_bias;
      else
        control.b_bias_val[0] = control.slider_bias;
      fl_redraw_object(fd_MRM->Wave_Plot);
      break;

    case 5:// Delay slider
      control.delay=1/fl_get_slider_value(fd_MRM->SL_fps);
      break;
    case 6:// Wait-Box Slider (progress indicator)
      fl_set_slider_value(ob,control.wait);
      break;
    default: 
      ;
  }
}

void callback_CheckButtons(FL_OBJECT *ob, long arg)
{
  int i;
  static int mouse_x = 0;
  int mouse_y;
  unsigned int keymask;
  static int win_areas_x, win_areas_y;
  static int first_time = TRUE;

  switch (arg)
  {
    case 1:// Morph Radio_Butt
        control.Morph=MORPH_MORPH;
        if (fd_AREAS->AREAS->visible)
        {
          fl_get_win_origin(fd_AREAS->AREAS->window, &win_areas_x, &win_areas_y);
          XUnmapWindow(fl_get_display(), fd_AREAS->AREAS->window);
        }
        fl_set_menu_item_mode(fd_MRM->MN_Windows, 4, FL_PUP_BOX+FL_PUP_GREY);

        fl_show_object(fd_MRM->SL_Level_Adjust);
        fl_show_object(fd_MRM->Wave_Plot_Box);        
        fl_show_object(fd_MRM->Wave_Plot);        
        fl_show_object(fd_MRM->CT_Level_Select);
        if (control.advanced) { for (i=0; i<MAX_LEVELS; i++) fl_show_object(fd_MRM->BT_Level[i]);}
      break;
    case 2:// Warp Radio_Butt
        control.Morph=MORPH_WARP;
        if (fd_AREAS->AREAS->visible)
        {
          fl_get_win_origin(fd_AREAS->AREAS->window, &win_areas_x, &win_areas_y);
          XUnmapWindow(fl_get_display(), fd_AREAS->AREAS->window);
        }
        fl_set_menu_item_mode(fd_MRM->MN_Windows, 4, FL_PUP_BOX+FL_PUP_GREY);

        fl_hide_object(fd_MRM->SL_Level_Adjust);
        fl_hide_object(fd_MRM->Wave_Plot);        
        fl_hide_object(fd_MRM->Wave_Plot_Box);        
        fl_hide_object(fd_MRM->CT_Level_Select);
        if (control.advanced) { for (i=0; i<MAX_LEVELS; i++) fl_hide_object(fd_MRM->BT_Level[i]);}
      break;
    case 3:// Animate Detail Radio_Butt
        control.Morph=MORPH_WAVE;
        if (fd_AREAS->AREAS->visible)
        {
          fl_get_win_origin(fd_AREAS->AREAS->window, &win_areas_x, &win_areas_y);
          XUnmapWindow(fl_get_display(), fd_AREAS->AREAS->window);
        }
        fl_set_menu_item_mode(fd_MRM->MN_Windows, 4, FL_PUP_BOX+FL_PUP_GREY);

        fl_hide_object(fd_MRM->SL_Level_Adjust);
        fl_hide_object(fd_MRM->Wave_Plot);        
        fl_hide_object(fd_MRM->Wave_Plot_Box);        
        fl_hide_object(fd_MRM->CT_Level_Select);
        if (control.advanced) { for (i=0; i<MAX_LEVELS; i++) fl_hide_object(fd_MRM->BT_Level[i]);}
      break;
    case 4:// Border Vectors
      break;
    case 5:// Advanced User Mode
      if (s_pic->GetPicPointer() != NULL)
      {
        control.advanced=fl_get_button(fd_MRM->CB_Advanced_Mode);
        if (control.advanced)
        {
          if (control.Morph == MORPH_MORPH) { for (i=0; i<MAX_LEVELS; i++) fl_show_object(fd_MRM->BT_Level[i]); }
          fl_set_button(fd_MRM->BT_Level[0],1);
          fl_set_button(fd_MRM->BT_Level[MAX_LEVELS-1],1);
        }
        else
        {
          for (i=1; i<MAX_LEVELS; i++) fl_set_button(fd_MRM->BT_Level[i],0);
          for (i=0; i<MAX_LEVELS; i++) fl_hide_object(fd_MRM->BT_Level[i]);
        }
        fl_call_object_callback(fd_MRM->CT_Level_Select);
      }
      break;
    case 6:// Select to show animation sequence
      if ( fl_get_button(ob) )
      {
        fl_hide_object(fd_MRM->CT_Frame_Nr);
        fl_show_object(fd_MRM->CB_Anim_Cycle);
        fl_show_object(fd_MRM->BT_Animate);
        fl_show_object(fd_MRM->SL_fps);
      }
      control.AnimateOrShow=1;
      break;
    case 7:// Select to show picture number
      if ( fl_get_button(ob) )
      {
        fl_hide_object(fd_MRM->CB_Anim_Cycle);
        fl_hide_object(fd_MRM->BT_Animate);
        fl_hide_object(fd_MRM->SL_fps);                
        fl_show_object(fd_MRM->CT_Frame_Nr);
      }
      control.AnimateOrShow=0;
      break;  
    case 8:// Cycle SRC->DEST->SRC->STOP
      control.Cycle = fl_get_button(ob);
      break;  
    case 9:// Check_Save_Calc:
      break;  
    case 10:// Check_Save_Cinema:
      break;  
    case 11:// CB_Area_Map
      control.Morph = MORPH_AREA;
      if ( first_time )
      {
        fl_show_form(fd_AREAS->AREAS,FL_PLACE_ASPECT,FL_FULLBORDER,"DETAIL MAP");
        fl_get_win_origin(fd_AREAS->AREAS->window, &win_areas_x, &win_areas_y);
        first_time = FALSE;
      }
      else
      {
        XMapWindow(fl_get_display(), fd_AREAS->AREAS->window);
        XMoveWindow(fl_get_display(), fd_AREAS->AREAS->window, win_areas_x, win_areas_y);
      }
      fl_set_menu_item_mode(fd_MRM->MN_Windows, 4, FL_PUP_BOX + FL_PUP_CHECK);
      fl_redraw_object(obj_a);
      
      fl_hide_object(fd_MRM->SL_Level_Adjust);
      fl_hide_object(fd_MRM->Wave_Plot);        
      fl_hide_object(fd_MRM->Wave_Plot_Box);        
      fl_hide_object(fd_MRM->CT_Level_Select);
      if (control.advanced) { for (i=0; i<MAX_LEVELS; i++) fl_hide_object(fd_MRM->BT_Level[i]);}
      break;
/*AAA    case 12:// Check_MPEG_Cycle
      break;*/
    case 13:// CB_High_Quality: NOP
      break;
    case 14:// Check_Use_Wavelets:
      if ( fl_get_button(fd_MRM->CB_Use_Wavelets) )
        fl_set_object_label(fd_MRM->Wave_Plot_Box,"Wavelet Interpolation Levels");
      else
        fl_set_object_label(fd_MRM->Wave_Plot_Box,"Weighting-Progress");

      fl_redraw_object(fd_MRM->Wave_Plot);
      break;

    default:
      ;
  }
}

void callback_Counters(FL_OBJECT *ob, long arg)
{
  static int old_val=1;
  int val;

  switch (arg)
  {
    case 1:// Select Wavelet-Level
      if (control.advanced)
      {
        val = (int)fl_get_counter_value(ob);
      
        if (val > MAX_LEVELS)
          val = MAX_LEVELS;
        else
        {
          if (val>old_val)
            while (!fl_get_button(fd_MRM->BT_Level[val-1])) val++;
          else if (val<old_val)
            while (!fl_get_button(fd_MRM->BT_Level[val-1])) val--;
        }
        fl_set_counter_value(ob,val);
        old_val=val;
        control.select_level = (int) fl_get_counter_value(ob);
      }
      else
      {
        fl_set_counter_value(ob,1);
        control.select_level=1;
      }
      fl_set_slider_value(fd_MRM->SL_Level_Adjust,control.b_bias_val[control.select_level-1]);
      fl_redraw_object(fd_MRM->Wave_Plot);      
      break;
    case 2:// Number of Pics to generate
      control.HowManyPics = (int) fl_get_counter_value(ob);
      if (control.WhatPic > control.HowManyPics)
      {
        fl_set_counter_value(fd_MRM->CT_Frame_Nr,(double) control.HowManyPics);
        control.WhatPic = control.HowManyPics;
      }        
      break;
    case 3:// Picture Number to show
//      int im_x,im_y;

      control.WhatPic = (int) fl_get_counter_value(ob);
      if (control.WhatPic > control.HowManyPics)
      {
        fl_set_counter_value(ob,(double) control.HowManyPics);
        control.WhatPic = control.HowManyPics;
      }
        
      if (cinema[control.WhatPic]->GetImage() != NULL)
        cinema[control.WhatPic]->ShowImage(obj_m,m_win->GetWin());      
/*
      {
        im_x=cinema[control.WhatPic]->image_x; im_y=cinema[control.WhatPic]->image_y;
        if ( (obj_m->w != im_x) || (obj_m->h != im_y) )
        {
          fl_hide_form(fd_RESULT->RESULT);
  //        fl_set_form_maxsize(fd_RESULT->RESULT,SHOW_MAX,(im_y*SHOW_MAX)/im_x);
          fl_set_form_size(fd_RESULT->RESULT,SHOW_SIZE,(im_y*SHOW_SIZE)/im_x);
          fl_show_form(fd_RESULT->RESULT,FL_PLACE_ASPECT,FL_FULLBORDER,"MORPH RESULT");      
        }
*/
      break;
    case 4:// Start to save with number #
      control.save_start=(int) fl_get_counter_value(ob);
      break;
    case 5:// Save in steps of #
      control.save_step=(int) fl_get_counter_value(ob);
      break;
    default:
      ;
  }
}

