/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/***************************************************************************
 *            cprquerydialog.cc
 *
 *  Sat Feb 19 17:05:43 CET 2005
 *  Copyright  2005 Bent Bisballe
 *  deva@aasimon.org
 ****************************************************************************/

/*
 *  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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <config.h>
#ifdef USE_GUI

#include <qframe.h>

#include "messagebox.h"
#include "cprquerydialog.h"
#include "miav_config.h"

CPRQueryDialog::CPRQueryDialog(QLabel *lcpr,
                               QLabel *lname,
                               QWidget *parent, 
                               const char *name, 
                               QStatusBar *status)
	: QDialog(parent, name, TRUE)
{
  setCaption(name);
  MiavConfig cfg("cprquery.conf");

  lbl_name = lname;
  lbl_cpr = lcpr;
  statusbar = status;

  //Read configuration
  CPR_HOST = cfg.readString("cpr_host");
  CPR_PORT = cfg.readInt("cpr_port");
  CPR_TIMEOUT = cfg.readInt("cpr_timeout");

  cpr[0] = '\0';
  internalCpr[0] = '\0';	

  cprSocket = new QSocket(this);
  connect(cprSocket, SIGNAL(readyRead()), SLOT(cprSocket_readyRead()));
  connect(cprSocket, SIGNAL(connected()), SLOT(cprSocket_connected()));
  connect(cprSocket, SIGNAL(error(int)), SLOT(cprSocket_error(int)));

  lbl_cpr->setText("Indtast CPR");

  // Setup connection timer
  timer = new QTimer(this);
  connect(timer, SIGNAL(timeout()), SLOT(cprSocket_timeout()));

  // Generate input buttons
	QGridLayout *gl = new QGridLayout(this, 4, 3, 10, 2);

	QButton *b1 =  createButton(this, "1", 1);
	QButton *b2 =  createButton(this, "2", 2);
	QButton *b3 =  createButton(this, "3", 3);
	QButton *b4 =  createButton(this, "4", 4);
	QButton *b5 =  createButton(this, "5", 5);
	QButton *b6 =  createButton(this, "6", 6);
	QButton *b7 =  createButton(this, "7", 7);
	QButton *b8 =  createButton(this, "8", 8);
	QButton *b9 =  createButton(this, "9", 9);
	QButton *b0 =  createButton(this, "0", 0);
	QButton *bbs = createButton(this, "<-", 10);
	QButton *bca = createButton(this, "CA", 11);

	gl->addWidget(b1, 0,0);
	gl->addWidget(b2, 0,1);
	gl->addWidget(b3, 0,2);
	gl->addWidget(b4, 1,0);
	gl->addWidget(b5, 1,1);
	gl->addWidget(b6, 1,2);
	gl->addWidget(b7, 2,0);
	gl->addWidget(b8, 2,1);
	gl->addWidget(b9, 2,2);
	gl->addWidget(b0, 3,1);
	gl->addWidget(bbs, 3,2);
	gl->addWidget(bca, 3,0);

	// Setup signals
	connect(b1, SIGNAL(clicked()), SLOT(b_1_clicked()));
	connect(b2, SIGNAL(clicked()), SLOT(b_2_clicked()));
	connect(b3, SIGNAL(clicked()), SLOT(b_3_clicked()));
	connect(b4, SIGNAL(clicked()), SLOT(b_4_clicked()));
	connect(b5, SIGNAL(clicked()), SLOT(b_5_clicked()));
	connect(b6, SIGNAL(clicked()), SLOT(b_6_clicked()));
	connect(b7, SIGNAL(clicked()), SLOT(b_7_clicked()));
	connect(b8, SIGNAL(clicked()), SLOT(b_8_clicked()));
	connect(b9, SIGNAL(clicked()), SLOT(b_9_clicked()));
	connect(b0, SIGNAL(clicked()), SLOT(b_0_clicked()));
	connect(bbs,SIGNAL(clicked()), SLOT(b_b_clicked()));
	connect(bca,SIGNAL(clicked()), SLOT(b_c_clicked()));
	
	this->move(175,150);
  show();
}

CPRQueryDialog::~CPRQueryDialog()
{
}

/**
 * createButton:
 * A genric button-creater 
 */
QPushButton *CPRQueryDialog::createButton(QWidget *parent, const char *text, int value) 
{
  char buf[32];
  sprintf(buf, "%d", value);
  QPushButton *q = new QPushButton(this, buf);
  
	QFont f("Lucida", 48);
	q->setFixedSize(150, 100);
	q->setText(text);
	q->setFont(f);
	return q;
}

/**
 * Event functions for handling buttonclicks.
 * For button 0-9 the values is sent back.
 * Backspace and clear are handled via special
 * signals.
 * When 10 digits has been input we close the form
 */
void CPRQueryDialog::b_1_clicked() { insert_digit(1); }
void CPRQueryDialog::b_2_clicked() { insert_digit(2); }
void CPRQueryDialog::b_3_clicked() { insert_digit(3); }
void CPRQueryDialog::b_4_clicked() { insert_digit(4); }
void CPRQueryDialog::b_5_clicked() { insert_digit(5); }
void CPRQueryDialog::b_6_clicked() { insert_digit(6); }
void CPRQueryDialog::b_7_clicked() { insert_digit(7); }
void CPRQueryDialog::b_8_clicked() { insert_digit(8); }
void CPRQueryDialog::b_9_clicked() { insert_digit(9); }
void CPRQueryDialog::b_0_clicked() { insert_digit(0);}
void CPRQueryDialog::b_b_clicked() { remove_digit();}
void CPRQueryDialog::b_c_clicked() { remove_all();}

void CPRQueryDialog::b_clicked(int value) 
{
  printf("%d\n", value);
	switch(value) {
		case 10:
			if (digits>0) digits--;
			emit bbs_clicked();
			break;
		case 11:
			digits = 0;
			emit bca_clicked();
			break;
		default:
			digits++;
			emit number_clicked(value);
	}		
	if (digits == 10){
		digits = 0;
		accept();
	}
}

/**
 * remove_digit:
 * Remove one digit from the selected cpr
 * Used when the user pushes backspace in
 * the inputwindow
 */
void CPRQueryDialog::remove_digit() 
{
  int temp;
  temp = strlen(cpr);
  if (temp == 7) /* Remove two characters due to the hyphen */
    strcpy(cpr+temp-2, "\0");
  else if ((temp >0) && (temp <=11))
    strcpy(cpr+temp-1, "\0");
  lbl_cpr->setText(cpr);
}

/**
 * remove_all:
 * Clear the selected cpr 
 */
void CPRQueryDialog::remove_all() 
{
  strcpy(cpr, "");
  lbl_cpr->setText(cpr);
}

/**
 * insert_digit:
 * Respond to what the user types in the inputWindow 
 */
void CPRQueryDialog::insert_digit(int value)
{
  char temp[2];
  switch(strlen(cpr)) {
  case 5: // Automaticaly add a hyphen after the sixth digit
    sprintf(temp, "%d-", value);
    strcat(cpr, temp);
    lbl_cpr->setText(cpr);
    break;
  case 10:
    sprintf(temp, "%d", value);
    strcat(cpr, temp);
    lbl_cpr->setText(cpr);
    verifycpr(cpr);
    break;
  default:
    sprintf(temp, "%d", value);
    strcat(cpr, temp);
    lbl_cpr->setText(cpr);
    break;
  }
}

/**
 * verifycpr:
 * Test a cpr via test_cpr().
 * If cpr is invalid, then ask user what
 * to do via MessageBox
 */
void CPRQueryDialog::verifycpr(char *cpr) 
{
  strncpy(internalCpr, cpr, 6);
  strncpy(internalCpr+6, cpr+7, 4);
  internalCpr[10] = 0;
  
  if (!(test_cpr(internalCpr))) {
    MessageBox ccw(this, CONFIRM_INVALID_CPR_TITLE, CONFIRM_INVALID_CPR, TYPE_YES_NO_CANCEL);
    switch(ccw.exec()) {
    case MSG_CANCEL:
      bedit_clicked();
      break;
    case MSG_NO:
      bcancel_clicked();
      break;
    case MSG_YES:
      accept();
      break;
    }
  } else {
    cprSocket->connectToHost(CPR_HOST->c_str(), CPR_PORT);
    timer->start(CPR_TIMEOUT);
    //    accept();
  }
}


/* Clears all data - alerts user if measurements are not stored */
void CPRQueryDialog::bcancel_clicked()
{
  cpr[0]= '\0';
  lbl_cpr->setText("Indtast CPR");
  lbl_name->setText("");
}

/* This is used when the user enters a cpr that is not valid and wishes to edit
 * the cpr.
 */
void CPRQueryDialog::bedit_clicked() 
{
  cpr[10]= '\0';
  lbl_cpr->setText(cpr);
  lbl_name->setText("");
}


/**
 * test_cpr:
 * Checks that a cpr i valid via a modulo 11 test 
 */
int CPRQueryDialog::test_cpr(const char *s)
{
  int sum = 0;
  int ctl;
  const char *cptr;
  
  // Is the string to short to be a cpr?
  if(strlen(s) != 10) return 0;
  
  // Are all characters digits?
  for(cptr = s; *cptr; cptr++) if(!isdigit(*cptr)) return 0;

  // Calculate modulo 11 checksum
  sum += (s[0] - '0') * 4;
  sum += (s[1] - '0') * 3;
  sum += (s[2] - '0') * 2;
  sum += (s[3] - '0') * 7;
  sum += (s[4] - '0') * 6;
  sum += (s[5] - '0') * 5;
  sum += (s[6] - '0') * 4;
  sum += (s[7] - '0') * 3;
  sum += (s[8] - '0') * 2;
  ctl = 11 - (sum % 11);

  // Is the checksum correct?
  if(ctl == 11) ctl = 0;
  return s[9] - '0' == ctl;
}

/**
 * cprSocket_error
 * Called if an error occurres in the corsocket connection.
 * Writes out the appropriate error message.
 */
void CPRQueryDialog::cprSocket_error(int errnum)
{
  QString msg = QString("cprSocket encountered an error: ");
  timer->stop();
  
  lbl_name->setText(NAME_NOT_AVAILABLE);

  // Print error message
  switch(errnum) {
  case QSocket::ErrConnectionRefused: // if the connection was refused
    msg.append("ErrConnectionRefused");
    break;
  case QSocket::ErrHostNotFound: // if the host was not found
    msg.append("ErrHostNotFound");
    break;
  case QSocket::ErrSocketRead: // if a read from the socket failed 
    msg.append("ErrSocketRead");
    break;
  }

  if(statusbar) statusbar->message(msg);
  MessageBox(this, "Fejl", msg, TYPE_OK, ICON_ERROR).exec();
  accept();
}

/**
 * cprSocket_readyRead:
 * Uses a patients cpr to look up his or hers name
 * via the departments cpr-server.
 * This is called by the cprSocket when the socket is ready
 */
void CPRQueryDialog::cprSocket_readyRead() 
{
  QString name;
  QString firstname;
  QString lastname;
  int timeout = 0;

  if (!cprSocket->canReadLine()) return;

  QString msg = QString("Recieving name from cpr database...");
  if(statusbar) statusbar->message(msg);
  timer->stop();

  while(cprSocket->canReadLine()) {
    QString s = cprSocket->readLine();
    if (s.startsWith("0001")) {
      name.append(s.right(s.length()-4));
      lastname.append(s.right(s.length()-4));
      name.setLength(name.length()-1);
      if (name.length()) name += QString(", ");
    }
    if (s.startsWith("0002")) {
      name.append(s.right(s.length()-4));
      firstname.append(s.right(s.length()-4));
      name.setLength(name.length()-1);
      cprSocket->close();
      lbl_name->setText(name);
      accept();
      return;
    }
    if (timeout>1000) {
      lbl_name->setText(NAME_NOT_AVAILABLE);
      MessageBox(this, NAME_NOT_AVAILABLE_TITLE, NAME_NOT_AVAILABLE, TYPE_OK, ICON_ERROR).exec();
      accept();
      return;
    }
    timeout++;
  }
  
  accept();
}


/**
 * cprSocket_connected:
 * Writes the selected cpr to the cpr-server
 * when the cprSocket is connected.
 */
void CPRQueryDialog::cprSocket_connected() 
{	
  QString msg = QString("Connected to cpr database, sending cpr number...");
  if(statusbar) statusbar->message(msg);
  timer->stop();

  cprSocket->writeBlock(internalCpr, 10);
  cprSocket->writeBlock("\n", 1);
}

/**
 * cprSocket_timeout:
 * Called when the connection has not emitted a signal in CPR_TIMEOUT miliseconds.
 */
void CPRQueryDialog::cprSocket_timeout()
{
  QString msg = QString("cprSocket timed out doing: ");
  timer->stop();

  lbl_name->setText(NAME_NOT_AVAILABLE);


  // Print connection status
  switch(cprSocket->state()) {
  case QSocket::Idle:       // if there is no connection
    msg.append("Idle");
    break;
  case QSocket::HostLookup: // during a DNS lookup
    msg.append("HostLookup");
    break;
  case QSocket::Connecting: // during TCP connection establishment
    msg.append("Connecting");
    break;
  case QSocket::Connected:  // when there is an operational connection
    msg.append("Conected");
    break;
  case QSocket::Closing:    // if the socket is closing down, but is not yet closed.
    msg.append("Closing");
    break;
  }
  
  if(statusbar) statusbar->message(msg);
  MessageBox(this, "Fejl", msg, TYPE_OK, ICON_ERROR).exec();
  accept();
}

#endif /* USE_GUI */