//<copyright>
//
// Copyright (c) 1995
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
//
//</copyright>



//<file>
//
//
// Name:        $RCSfile: dateeditor.C,v $
//
// Purpose:     Implementation of class DateEditor
//
// Created:     10 Sept 1996   Thomas Starlinger
// Modified:    $Date: 1997/01/31 14:58:14 $
//
// Description: Puts a date and/or time editor to the users disposal. 
//
// Function:    The date and time fields are up and down counted by steppers
//              which control the AdjValue of the FieldEditor31 that has the
//              focus.
//
// $Id: dateeditor.C,v 1.4 1997/01/31 14:58:14 bmarsch Exp $
//
// $Log: dateeditor.C,v $
// Revision 1.4  1997/01/31 14:58:14  bmarsch
// IntAction moved into this file
//
// Revision 1.3  1996/11/27 16:46:31  bmarsch
// Replaced general Label by WidgetKit::label
//
// Revision 1.2  1996/09/20 13:45:37  bmarsch
// Corrected includes
//
// Revision 1.1  1996/09/20 13:29:57  bmarsch
// Initial revision
//
// Revision 1.1  1996/09/20 13:08:06  tstar
// Initial revision
//
//
//</file>


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/times.h>
#include <limits.h>

#include <strstream.h>
#include <iomanip.h>
#include <fstream.h>
#include <iostream.h>

#include "numbereditor.h"
#include "dateeditor.h"

#include <InterViews/action.h>
#include <InterViews/adjust.h>
#include <InterViews/font.h>
#include <InterViews/layout.h>
#include <InterViews/label.h>
#include <InterViews/observe.h>
#include "adjvalue.h"
#include "field.h"
#include <IV-look/slider.h>
#include <IV-look/bevel.h>

// ---------------------------------------------------------------------------------
// IntCallback

#if defined(__STDC__) || defined(__ANSI_CPP__)
#define __IntActionCallback(T) T##_IntActionCallback
#define IntActionCallback(T) __IntActionCallback(T)
#define __IntActionMemberFunction(T) T##_IntActionMemberFunction
#define IntActionMemberFunction(T) __IntActionMemberFunction(T)
#else
#define __IntActionCallback(T) T/**/_IntActionCallback
#define IntActionCallback(T) __IntActionCallback(T)
#define __IntActionMemberFunction(T) T/**/_IntActionMemberFunction
#define IntActionMemberFunction(T) __IntActionMemberFunction(T)
#endif

#define declareIntActionCallback(T) \
typedef void (T::*IntActionMemberFunction(T))(int); \
class IntActionCallback(T):public Action { \
public: \
    IntActionCallback(T)(T*, IntActionMemberFunction(T), int); \
\
    virtual void execute(); \
private: \
    int param_; \
    T* obj_; \
    IntActionMemberFunction(T) func_; \
};

#define implementIntActionCallback(T) \
IntActionCallback(T)::IntActionCallback(T)(T* obj, IntActionMemberFunction(T) func, int p) { \
    obj_ = obj; \
    func_ = func; \
    param_ = p; \
} \
\
void IntActionCallback(T)::execute() { if (obj_ && func_) (obj_->*func_)(param_); }

const int DateEditor::monLength[MONTH_END] = { 0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

// ---------------------------------------------------------------------------------
// Callbacks for buttons.

declareActionCallback(DateEditor)
implementActionCallback(DateEditor)
declareIntActionCallback(DateEditor)
implementIntActionCallback(DateEditor)

// ---------------------------------------------------------------------------------

void DateEditor::init(RString now, Type type) {
  int idx=0, oidx=0;
  int ye=0, mo=1, da=1, ho=0, mi=0, se=0;

  // Initialize.
  neYear_=nil;
  adYear_=nil;
  neMonth_=nil;
  adMonth_=nil;
  neDay_=nil;
  adDay_=nil;
  neHour_=nil;
  adHour_=nil;
  neMinute_=nil;
  adMinute_=nil;
  neSecond_=nil;
  adSecond_=nil;

  // Save type of editor.
  type_=type;

  // Widget- and LayoutKit init:
  kit_ = WidgetKit::instance();
  layout_ = LayoutKit::instance();

  // Parse time string.
  idx=now.index('/');
  ye=::atoi(now.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  idx=now.index(oidx,'/');
  mo=::atoi(now.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  idx=now.index(oidx,' ');
  da=::atoi(now.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  idx=now.index(oidx,':');
  ho=::atoi(now.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  idx=now.index(oidx,':');
  mi=::atoi(now.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  se=::atoi(now.gRight(oidx));

  // Create NumberEditors.
  // Date
  if (type_ & DATE) {
    adYear_=new AdjValue(0, 99, ye, 1, 10);
    neYear_=new NumberEditor(
  		  adYear_,
  		  NumberEditor::NONE,
  		  new IntActionCallback(DateEditor)(this,&DateEditor::setLastAdj,0),
  		  new ActionCallback(DateEditor)(this,&DateEditor::monChanged),
		  0,"88");
  
    if (mo>M_DEC || mo<M_JAN) mo=1;
    adMonth_=new AdjValue(1, 12, mo, 1, 3);
    neMonth_=new NumberEditor(
  		   adMonth_,
  		   NumberEditor::NONE,
  		   new IntActionCallback(DateEditor)(this,&DateEditor::setLastAdj,1),
  		   new ActionCallback(DateEditor)(this,&DateEditor::monChanged),
		   0,"88");
  
    int daUpper=monLength[mo];
    if (!daUpper) daUpper=lenFeb(ye);
    if (da>daUpper || da<1) da=1;
    adDay_=new AdjValue(1, daUpper, da, 1, 8);
    neDay_=new NumberEditor(
  		 adDay_,  
  		 NumberEditor::NONE,
  		 new IntActionCallback(DateEditor)(this,&DateEditor::setLastAdj,2),
		 nil,0,"88");
  }

  // Time
  if (type_ & TIME) {
    if (ho>23 || ho<0) ho=0;
    adHour_=new AdjValue(0, 23, ho, 1, 6);
    neHour_=new NumberEditor(
  		  adHour_,  
  		  NumberEditor::NONE,
  		  new IntActionCallback(DateEditor)(this,&DateEditor::setLastAdj,3),
		  nil,0,"88");
  
    if (mi>59 || mi<0) mi=0;
    adMinute_=new AdjValue(0, 59, mi, 1, 10);
    neMinute_=new NumberEditor(
  		    adMinute_,
  		    NumberEditor::NONE,
  		    new IntActionCallback(DateEditor)(this,&DateEditor::setLastAdj,4),
		    nil,0,"88");
  
    if (se>59 || se<0) se=0;
    adSecond_=new AdjValue(0, 59, se, 1, 10);
    neSecond_=new NumberEditor(
  		    adSecond_,  
  		    NumberEditor::NONE,
  		    new IntActionCallback(DateEditor)(this,&DateEditor::setLastAdj,5),
		    nil,0,"88");
  }

  // Set initial NumberEditor.
  if (type_ & DATE)
    lastAdj_=adYear_;
  else
    lastAdj_=adHour_;

  // Space and "betweeners" for date and time.
  Glyph *glTmp3=layout_->hspace(3.0);
  Glyph *glTmp2=layout_->hspace(2.0);
  Glyph *glTmp15=layout_->hspace(15.0);
  Glyph *glTmpSlash=layout_->hbox(glTmp3,
				  layout_->vcenter(kit_->label("/")),
				  glTmp2);
  Glyph *glTmpColon=layout_->hbox(glTmp3,
				  layout_->vcenter(kit_->label(":")),
				  glTmp2);

  Glyph *glTmpStepUp = new DateStepper(kit_->up_mover_look(new TelltaleState(TelltaleState::is_enabled)),
				       kit_->style(),
				       new TelltaleState(TelltaleState::is_enabled),
				       new ActionCallback(DateEditor)(this,&DateEditor::upStep));
  Glyph *glTmpStepDo = new DateStepper(kit_->down_mover_look(new TelltaleState(TelltaleState::is_enabled)),
				       kit_->style(),
				       new TelltaleState(TelltaleState::is_enabled),
				       new ActionCallback(DateEditor)(this,&DateEditor::downStep));

  // Get height of a FieldEditor31.
  Requisition reqTmp;
  if (type_ & DATE)
    neYear_->request(reqTmp);
  else
    neHour_->request(reqTmp);
  Coord coSize=reqTmp.y_requirement().natural();

  // Assemble DateEditor.
  PolyGlyph *hbTmp=layout_->hbox(13);
  if (type_ & DATE) {
    hbTmp->append(neYear_);
    hbTmp->append(glTmpSlash);
    hbTmp->append(neMonth_);
    hbTmp->append(glTmpSlash);
    hbTmp->append(neDay_);
    hbTmp->append(glTmp15);
  }

  if (type_ & TIME) {
    hbTmp->append(neHour_);
    hbTmp->append(glTmpColon);
    hbTmp->append(neMinute_);
    hbTmp->append(glTmpColon);
    hbTmp->append(neSecond_);
    hbTmp->append(glTmp15);
  }

  hbTmp->append(
    layout_->vcenter(layout_->vbox(
      layout_->fixed(glTmpStepUp,coSize,coSize*0.6),
      layout_->fixed(glTmpStepDo,coSize,coSize*0.6)
    ))
  );

  // Set body for this glyph.
  body(hbTmp);

  // Append the single NumberEditors to the InputHandler-hierarchy.
  append_input_handler(neYear_);
  append_input_handler(neMonth_);
  append_input_handler(neDay_);
  append_input_handler(neHour_);
  append_input_handler(neMinute_);
  append_input_handler(neSecond_);
}

//-----------------------------------------------------------------------

DateEditor::~DateEditor() {
}

//-----------------------------------------------------------------------

void DateEditor::setValue(RString value) {
  int idx=0, oidx=0;
  int ye=0, mo=1, da=1, ho=0, mi=0, se=0;

  // Parse time string.
  idx=value.index('/');
  ye=::atoi(value.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  idx=value.index(oidx,'/');
  mo=::atoi(value.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  idx=value.index(oidx,' ');
  da=::atoi(value.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  idx=value.index(oidx,':');
  ho=::atoi(value.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  idx=value.index(oidx,':');
  mi=::atoi(value.gSubstrIndex(oidx,idx-1));
  oidx=idx+1;
  se=::atoi(value.gRight(oidx));

  if (type_ & DATE) {
    neYear_->setValue(ye);
    neMonth_->setValue(mo);
    neDay_->setValue(da);
  }
  if (type_ & TIME) {
    neHour_->setValue(ho);
    neMinute_->setValue(mi);
    neSecond_->setValue(se);
  }
}

RString DateEditor::getValue() const {
  ostrstream d;

  // Create date string from values.
  d << setfill('0') << setw(2);
  if (type_ & DATE)
    d << setfill('0') << setw(2) << neYear_->getValue() << "/"
      << setfill('0') << setw(2) << neMonth_->getValue() << "/"
      << setfill('0') << setw(2) << neDay_->getValue() << " ";
  else
    d << "00/01/01 ";
  
  // Create time string from values.
  if (type_ & TIME)
    d << setfill('0') << setw(2) << neHour_->getValue() << ":"
      << setfill('0') << setw(2) << neMinute_->getValue() << ":"
      << setfill('0') << setw(2) << neSecond_->getValue() << ends;
  else
    d << "00:00:00" << ends;

  return RString(d.str());
}

//-----------------------------------------------------------------------

int DateEditor::lenFeb(int year) {
  if (!(year%4) && year%100) return 29;
  return 28;
}

//-----------------------------------------------------------------------

// If month changes the upper constraint 
void DateEditor::monChanged() {
  if (neDay_ && neMonth_) {
    int i=(int)neMonth_->getValue();
    if (monLength[i]==0)
      neDay_->setUpper(lenFeb((int)neYear_->getValue()));
    else
      neDay_->setUpper(monLength[(int)neMonth_->getValue()]);
  }
}

void DateEditor::setLastAdj(int idx) {
  switch (idx) {
  case 0:
    lastAdj_=adYear_;
    break;
  case 1:
    lastAdj_=adMonth_;
    break;
  case 2:
    lastAdj_=adDay_;
    break;
  case 3:
    lastAdj_=adHour_;
    break;
  case 4:
    lastAdj_=adMinute_;
    break;
  case 5:
    lastAdj_=adSecond_;
  }
}

