// ----------------------------------------------------------------------------
//
// Copyright (C) Columbia University, 1994. All Rights Reserved.
// Sameer A. Nene, Shree K. Nayar, Hiroshi Murase
//
// See file LICENSE for details of software license agreement.
//
// ----------------------------------------------------------------------------
//
// xeigencb.C
//
// Author:               Sameer Nene
// Date:                 09/12/94
// Version:              1.0.0
// Modification History:
//   10/27/94: Change necessary due to change in interface matrix.h
//   04/09/96: Fixed scoping problem to work with new ANSI C++ definition
//
// Notes:
//   X module for computation of Eigenvectors
//   
// ----------------------------------------------------------------------------

#define NO_XMSTRINGS

#include <ctype.h>
#include <strstream.h>
#include <stdio.h>
#include <stdlib.h>
#include <Xm/FileSB.h>
#include <Xm/List.h>
#include <Xm/MessageB.h>
#include <Xm/SelectioB.h>
#include <Xm/TextF.h>
#include <Xm/Xm.h>
#include "errorscope.h"
#include "fileiter.h"
#include "ioutil.h"
#include "list.h"
#include "xreporter.h"
#include "vector.h"
#include "matrix.h"
#include "stringary.h"
#include "xeigen.h"
#include "fileinfo.h"

extern void err_dlg(Widget, const char*);
extern void fillList(Widget, const List<FileInfo* > &);
extern void get_path(XmFileSelectionBoxCallbackStruct*, char**, char**);

void popup_cb(Widget, XtPointer p1, XtPointer)
{
  XtPopup(Widget(p1), XtGrabNone);
}

void pulldown_cb(Widget w, XtPointer, XtPointer)
{
  XtPopdown(XtParent(XtParent(w)));
}

void quit_cb(Widget, XtPointer, XtPointer)
{
  exit(0);
}

void typeoptionB_cb(Widget, XtPointer p1, XtPointer)
{
  *((int *)p1) = FileInfo::BINARY;
}

void typeoptionT_cb(Widget, XtPointer p1, XtPointer)
{
  *((int *)p1) = FileInfo::TEXT;
}

void avToggleB_cb(Widget, XtPointer p1, XtPointer)
{
  MainCbLocal *data = (MainCbLocal*)p1;

  data -> d_flag[0] = data -> d_flag[0] == True ? False : True;
  XtSetSensitive(data -> d_text[0], data -> d_flag[0]);
  XtSetSensitive(data -> d_button[0], data -> d_flag[0]);
}

void evToggleB_cb(Widget, XtPointer p1, XtPointer)
{
  MainCbLocal *data = (MainCbLocal*)p1;

  data -> d_flag[1] = data -> d_flag[1] == True ? False : True;
  XtSetSensitive(data -> d_text[2], data -> d_flag[1]);
  XtSetSensitive(data -> d_button[1], data -> d_flag[1]);
}

void hmenu_cb(Widget w, XtPointer, XtPointer)
{
  XmString mesg = XmStringCreateLtoR("Copyright \xA9 Columbia University, 1994. All Rights Reserved.\nSameer Nene, Shree Nayar, Hiroshi Murase", XmSTRING_DEFAULT_CHARSET);
  Widget dlg;

  dlg = XmCreateInformationDialog(XtParent(w), "About", NULL, 0);
  XtVaSetValues(dlg,
		XmNmessageString, mesg,
		XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL,
		NULL);
  XtUnmanageChild(XmMessageBoxGetChild(dlg, XmDIALOG_CANCEL_BUTTON));
  XtUnmanageChild(XmMessageBoxGetChild(dlg, XmDIALOG_HELP_BUTTON));
  
  XmStringFree(mesg);
  XtManageChild(dlg);
}

void arrow_down_cb(Widget, XtPointer p1, XtPointer)
{
  char *buf, buf2[256];
  int n;

  if((n = atoi(buf = XmTextFieldGetString(Widget(p1)))) > 0) {
    sprintf(buf2, "%d", n - 1);
    XmTextFieldSetString(Widget(p1), buf2);
  }
  XtFree(buf);
}

void arrow_up_cb(Widget, XtPointer p1, XtPointer)
{
  char *buf, buf2[256];

  buf = XmTextFieldGetString(Widget(p1));
  sprintf(buf2, "%d", atoi(buf) + 1);
  XtFree(buf);
  XmTextFieldSetString(Widget(p1), buf2);
}

void loadVectorSetOk_cb(Widget w, XtPointer p3, XtPointer p1)
{
  char *dir, *name, *p2;
  MainCbLocal *data = (MainCbLocal*)p3;
  FileInfo *fi;
  
  get_path((XmFileSelectionBoxCallbackStruct *)p1, &dir, &name);

  for(p2 = dir; isspace(*p2); ++p2);
  if(*p2 == '\0') {
    err_dlg(XtParent(w), "Please supply Directory Name");
    delete[] dir;
    delete[] name;
    return;
  }

  for(p2 = name; isspace(*p2); ++p2);
  if(*p2 == '\0') {
    err_dlg(XtParent(w), "Please supply Vector Set Name");
    delete[] dir;
    delete[] name;
    return;
  }

  fi = new FileInfo(dir, name, data -> d_type);

  ListManip<FileInfo* > m(&(data -> d_list));
  for(; m; ++m)
    if(strcmp(m() -> getName(), name) == 0) {
      delete m();
      m.remove();
      break;
    }
  m.insert(fi);

  fillList(data -> d_listw, data -> d_list);

  delete[] dir;
  delete[] name;
}

void delItem_cb(Widget, XtPointer p1, XtPointer)
{
  char *p2;
  int i, *pos_list = 0, pos_cnt;
  XmString *names;
  MainCbLocal *data = (MainCbLocal*)p1;
  
  XmListGetSelectedPos(data -> d_listw, &pos_list, &pos_cnt);
  XtVaGetValues(data -> d_listw, XmNitems, &names, NULL);

  for(i = 0; i < pos_cnt; ++i) {
    XmStringGetLtoR(names[pos_list[i] - 1], XmSTRING_DEFAULT_CHARSET, &p2);
    for(ListManip<FileInfo* > m(&(data -> d_list)); m; ++m)
      if(strcmp(m() -> getName(), p2) == 0) {
	delete m();
	m.remove();
	break;
      }
    XtFree(p2);
  }

  fillList(data -> d_listw, data -> d_list);
}

void avgVecName_cb(Widget w, XtPointer p3, XtPointer p1)
{
  char *dir, *name, *p2;
  MainCbLocal *data = (MainCbLocal*)p3;
  
  get_path((XmFileSelectionBoxCallbackStruct *)p1, &dir, &name);

  for(p2 = dir; isspace(*p2); ++p2);
  if(*p2 == '\0') {
    err_dlg(XtParent(w), "Please supply Directory Name");
    delete[] dir;
    delete[] name;
    return;
  }

  for(p2 = name; isspace(*p2); ++p2);
  if(*p2 == '\0') {
    err_dlg(XtParent(w), "Please supply Filename");
    delete[] dir;
    delete[] name;
    return;
  }

  delete[] data -> d_avg_p;
  data -> d_avg_p = strcat(strcpy(p2 = new char[strlen(dir) + strlen(name) + 1], dir), name);
  data -> d_avgtype = data -> d_type;

  XmTextFieldSetString(data -> d_text[0], name);

  delete[] dir;
  delete[] name;
  XtPopdown(data -> d_dialog);
}

void eVecName_cb(Widget w, XtPointer p3, XtPointer p1)
{
  char *dir, *name, *p2;
  MainCbLocal *data = (MainCbLocal*)p3;
  
  get_path((XmFileSelectionBoxCallbackStruct *)p1, &dir, &name);

  for(p2 = dir; isspace(*p2); ++p2);
  if(*p2 == '\0') {
    err_dlg(XtParent(w), "Please supply Directory Name");
    delete[] dir;
    delete[] name;
    return;
  }

  for(p2 = name; isspace(*p2); ++p2);
  if(*p2 == '\0') {
    err_dlg(XtParent(w), "Please supply Filename");
    delete[] dir;
    delete[] name;
    return;
  }

  delete[] data -> d_evc_p;
  data -> d_evc_p = strcat(strcpy(p2 = new char[strlen(dir) + strlen(name) + 1], dir), name);
  data -> d_evctype = data -> d_type;

  XmTextFieldSetString(data -> d_text[1], name);

  delete[] dir;
  delete[] name;
  XtPopdown(data -> d_dialog);
}

void eValName_cb(Widget w, XtPointer p3, XtPointer p1)
{
  char *dir, *name, *p2;
  MainCbLocal *data = (MainCbLocal*)p3;
  
  get_path((XmFileSelectionBoxCallbackStruct *)p1, &dir, &name);

  for(p2 = dir; isspace(*p2); ++p2);
  if(*p2 == '\0') {
    err_dlg(XtParent(w), "Please supply Directory Name");
    delete[] dir;
    delete[] name;
    return;
  }

  for(p2 = name; isspace(*p2); ++p2);
  if(*p2 == '\0') {
    err_dlg(XtParent(w), "Please supply Filename");
    delete[] dir;
    delete[] name;
    return;
  }

  delete[] data -> d_evl_p;
  data -> d_evl_p = strcat(strcpy(p2 = new char[strlen(dir) + strlen(name) + 1], dir), name);
  data -> d_evltype = data -> d_type;

  XmTextFieldSetString(data -> d_text[2], name);

  delete[] dir;
  delete[] name;
  XtPopdown(data -> d_dialog);
}

void avgpopup_cb(Widget, XtPointer p1, XtPointer)
{
  MainCbLocal *data = (MainCbLocal *)p1;
  XmString str1;

  XtRemoveAllCallbacks(data -> d_filesb, XmNokCallback);
  XtAddCallback(data -> d_filesb, XmNokCallback, avgVecName_cb, data);
  XtPopup(data -> d_dialog, XtGrabNone);
  str1 = XmStringCreateSimple("*.vec");
  XmFileSelectionDoSearch(data -> d_filesb, str1);
  XmStringFree(str1);
}

void evcpopup_cb(Widget, XtPointer p1, XtPointer)
{
  MainCbLocal *data = (MainCbLocal *)p1;
  XmString str1;

  XtRemoveAllCallbacks(data -> d_filesb, XmNokCallback);
  XtAddCallback(data -> d_filesb, XmNokCallback, eVecName_cb, data);
  XtPopup(data -> d_dialog, XtGrabNone);
  str1 = XmStringCreateSimple("*.evc");
  XmFileSelectionDoSearch(data -> d_filesb, str1);
  XmStringFree(str1);
}

void evlpopup_cb(Widget, XtPointer p1, XtPointer)
{
  MainCbLocal *data = (MainCbLocal *)p1;
  XmString str1;

  XtRemoveAllCallbacks(data -> d_filesb, XmNokCallback);
  XtAddCallback(data -> d_filesb, XmNokCallback, eValName_cb, data);
  XtPopup(data -> d_dialog, XtGrabNone);
  str1 = XmStringCreateSimple("*.vec");
  XmFileSelectionDoSearch(data -> d_filesb, str1);
  XmStringFree(str1);
}

void compute_cb(Widget, XtPointer p1, XtPointer)
{
  char *p2, errmessg[256];
  ostrstream ostr(errmessg, 256);
  ErrorScope::Error error;
  int i, j, k, numeigen, mem, half, l, n, size;
  double t;
  Vector v1, v2;
  MainCbLocal *data = (MainCbLocal*)p1;

  XReporter::showWindow(data -> d_topshell, "Computing Average Vector",
			XReporter::INTERRUPTIBLE);

  if(data -> d_flag[0] == True) {
    ListIter<FileInfo* > it(data -> d_list);
    for(k = 0; it; ++it) {
      FileList fl(it() -> getDirName(), it() -> getName(), "vec");
      for(FileListIter it(fl); it; ++it, ++k) {
	if(XReporter::display_status() == XReporter::BUTTONPRESSED) {
	  XReporter::destroyWindow();
	  return;
	}
	if((error = IOUtil::get(&v2, it())) != ErrorScope::OK) {
	  ostr << error << ": " << it();
	  XReporter::destroyWindow();
	  err_dlg(data -> d_topshell, errmessg);
	  return;
	}
	if(k == 0)
	  v1 = v2;
	else
	  v1 += v2;
      }
    }
    v1 /= k;
    if((error = IOUtil::put(v1, data -> d_avg_p)) != ErrorScope::OK) {
      ostr << error << ": " << data -> d_avg_p;
      XReporter::destroyWindow();
      err_dlg(data -> d_topshell, errmessg);
      return;
    }
  }

  ListIter<FileInfo* > it(data -> d_list);
  for(k = 0; it; ++it) {
    FileList fl(it() -> getDirName(), it() -> getName(), "vec");
    k += fl.getSize();
  }
  
  StringAry sa(k);
  ListIter<FileInfo* > it2(data -> d_list);
  for(k = 0; it2; ++it2) {
    FileList fl(it2() -> getDirName(), it2() -> getName(), "vec");
    for(FileListIter it2(fl); it2; ++it2, ++k)
      sa.set(k, it2());
  }

  numeigen = atoi(p2 = XmTextFieldGetString(data -> d_num[0]));
  XtFree(p2);
  if(numeigen <= 0 || numeigen > sa.getSize()) {
    XReporter::destroyWindow();
    err_dlg(data -> d_topshell, "Error in number of eigenvectors");
    return;
  }
  
  if(XReporter::display_status("Computing Implicit Covariance Matrix") == XReporter::BUTTONPRESSED) {
    XReporter::destroyWindow();
    return;
  }

  size = k;
  Matrix m(size, size);

  {
    if((error = IOUtil::get(&v2, sa[0])) != ErrorScope::OK) {
      ostr << error << ": " << sa[0];
      XReporter::destroyWindow();
      err_dlg(data -> d_topshell, errmessg);
      return;
    }
    mem = atoi(p2 = XmTextFieldGetString(data -> d_num[1]));
    XtFree(p2);
    if(mem <= 0) {
      XReporter::destroyWindow();
      err_dlg(data -> d_topshell, "Error in memory size");
      return;
    }
    
    half = ((mem * 1048576) >> 1) / (v2.length() * sizeof(double));
    VectAry va(half << 1);
    VectAryIter it1(va), it2(va);
    
    for(i = 0; i < size; i += half) {
      for(l = 0, k = 0, it1.set(0); l < half && i + l < size; ++l, ++k, ++it1) {
	if((error = IOUtil::get(&it1(), sa[i + l])) != ErrorScope::OK) {
	  ostr << error << ": " << sa[i + l];
	  XReporter::destroyWindow();
	  err_dlg(data -> d_topshell, errmessg);
	  return;
	}
	if(data -> d_flag[0] == True)
	  it1() -= v1;
      }
      
      for(l = 0, it1.set(0); l < k; ++l, ++it1) {
	if(XReporter::display_status() == XReporter::BUTTONPRESSED) {
	  XReporter::destroyWindow();
	  return;
	}
	for(n = l, it2.set(l); n < k; ++n, ++it2) {
	  t = it1() * it2();
	  m.element(i + l, i + n, t);
	  m.element(i + n, i + l, t);
	}
      }
      
      for(j = i + half; j < size; j += half) {
	for(l = 0, k = 0, it1.set(half); l < half && j + l < size; ++l, ++k, ++it1) {
	  if((error = IOUtil::get(&it1(), sa[j + l])) != ErrorScope::OK) {
	    ostr << error << ": " << sa[j + l];
	    XReporter::destroyWindow();
	    err_dlg(data -> d_topshell, errmessg);
	    return;
	  }
	  if(data -> d_flag[0] == True)
	    it1() -= v1;
	}
	
	for(l = 0, it1.set(0); l < half; ++l, ++it1) {
	  if(XReporter::display_status() == XReporter::BUTTONPRESSED) {
	    XReporter::destroyWindow();
	    return;
	  }
	  for(n = 0, it2.set(half); n < k; ++n, ++it2) {
	    t = it1() * it2();
	    m.element(i + l, j + n, t);
	    m.element(j + n, i + l, t);
	  }
	}
      }
    }
  }

  XReporter::destroyWindow();
  XReporter::showWindow(data -> d_topshell, "Computing Implicit Eigenvectors",
			XReporter::NONINTERRUPTIBLE);

  Vector ev(numeigen);
  VectAry va(numeigen), vb(numeigen),
  vc(n = half << 1);
  VectAryIter ita(va), itb(vb), itc(vc);
  
  MatrixUtil::getMaxEigenVectors(&m, &va, &ev); // m is destroyed.

  if(data -> d_flag[1] == True)
    if((error = IOUtil::put(ev, data -> d_evl_p)) != ErrorScope::OK) {
      ostr << error << ": " << data -> d_evl_p;
      XReporter::destroyWindow();
      err_dlg(data -> d_topshell, errmessg);
      return;
    }
  
  XReporter::destroyWindow();
  XReporter::showWindow(data -> d_topshell, "Computing Eigenvectors",
			XReporter::INTERRUPTIBLE);

  for(i = 0; i < size; i += n) {
    for(j = 0, itc.set(0); j < n && i + j < size; ++j, ++itc) {
      if((error = IOUtil::get(&itc(), sa[i + j])) != ErrorScope::OK) {
	ostr << error << ": " << sa[i + j];
	XReporter::destroyWindow();
	err_dlg(data -> d_topshell, errmessg);
	return;
      }
      if(data -> d_flag[0] == True)
	itc() -= v1;
    }
    
    for(ita.set(0), itb.set(0); ita; ++ita, ++itb) {
      if(XReporter::display_status() == XReporter::BUTTONPRESSED) {
	XReporter::destroyWindow();
	return;
      }
      for(j = 0, itc.set(0); j < n && i + j < size; ++j, ++itc)
	if(i == 0 && j == 0)
	  itb() = itc() * ita().element(i);
	else
	  itb() += itc() * ita().element(i + j);
    }
  }
  
  for(itb.set(0); itb; ++itb)
    itb() /= itb()();
  
  if((error = IOUtil::put(vb, data -> d_evc_p)) != ErrorScope::OK) {
    ostr << error << ": " << data -> d_evc_p;
    XReporter::destroyWindow();
    err_dlg(data -> d_topshell, errmessg);
    return;
  }

  XReporter::destroyWindow();
}
