/***************************************************************************
                          activemirrorcontrol.cpp  -  description
                             -------------------
    begin                : Wed Mar 26 14:23:48 UTC 2003
    copyright            : (C) 2003 by Martin Merck
    email                : merck@astro.uni-wuerzburg.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include "activemirrorcontrol.h"
#include "adjustmirrordlg.h"
#include "amccmdserversocket.h"
#include "amclookuptable.h"
#include "amclog.h"
#include "amcprogdlg.h"
#include "amcreportsocket.h"
#include "amcstate.h"
#include "heatstate.h"
#include "calibratelasermovementdialog.h"
#include "motorthreaddialog.h"
#include "threadcontroller.h"
#include "cc_defs.h"
#include <qdatetime.h>
#include <qlabel.h>
#include <qlcdnumber.h>
#include <qlineedit.h>
#include <qmessagebox.h>
#include <qmultilineedit.h>
#include <qpushbutton.h>
#include <qspinbox.h>
#include <stdio.h>
#include <math.h>

extern AMCState*  g_pAMCState;
extern HEATState* g_pHEATState;
extern AMCLog*	  g_pLog;


ActiveMirrorControl::ActiveMirrorControl(QWidget *parent, const char *name) : ActiveMirrorControlBase(parent, name)
{
	m_zCCMode = true;
	qDebug("Connecting AMCState signal");
	connect( g_pAMCState, SIGNAL( stateChanged( int, int  ) ), SLOT( stateChangedSlot( int, int)));
	qDebug("Connecting HEATState signal");
	connect( g_pHEATState, SIGNAL( stateChanged( int, int  ) ), SLOT( heatStateChangedSlot( int, int)));
	qDebug("Setting AMCState");
	g_pAMCState->setState( AMC_STATE_NOT_AVAILABLE );
	qDebug("Setting HEATState");
	g_pHEATState->setState( HEAT_STATE_NOT_AVAILABLE );

	qDebug("Creating AMCCmdServerSocket");
	m_pCmdServerSocket = new AMCCmdServerSocket();
	connect( m_pCmdServerSocket, SIGNAL( newConnect() ), SLOT( cmdConnected()));
	connect( m_pCmdServerSocket, SIGNAL( endConnect() ), SLOT( cmdDisconnected()));
	connect( m_pCmdServerSocket, SIGNAL( cmdFromCC( QString ) ), SLOT( cmdReceived( QString )));
	connect( m_pCmdServerSocket, SIGNAL( reportFromCC( QString ) ), SLOT( ccReportReceived( QString )));
	connect( m_pCmdServerSocket, SIGNAL( errorFromSocket( QString ) ), SLOT( logMessage( QString )));

	qDebug("Creating AMCReportSocket");
	m_pReportSocket = new AMCReportSocket( this );
	connect( m_pReportSocket, SIGNAL( ccConnected() ), SLOT( ccReportConnection()));
	connect( m_pReportSocket, SIGNAL( ccConnectionRefused() ), SLOT( ccNoConnection()));
	connect( m_pReportSocket, SIGNAL( reportDelivered( QDateTime&, QString& ) ), SLOT( reportDelivered( QDateTime&, QString& )));

  connect( &m_qtCCRetryTimer, SIGNAL( timeout( ) ), SLOT( reconnectCCTimeout() ) );
}

ActiveMirrorControl::~ActiveMirrorControl()
{
	qDebug("Deleting AMCCmdServerSocket");
	delete m_pCmdServerSocket;
	qDebug("Deleting AMCReportSocket");
	delete m_pReportSocket;
}

/** Slot invoked when we got a connection from
Central Control */
void ActiveMirrorControl::cmdConnected()
{
	g_pLog->logInfo( "ActiveMirrorControl::cmdConnected", "Connected to command port of CC" );
}

/** Slot called when connection to CC is closed */
void ActiveMirrorControl::cmdDisconnected()
{
	g_pLog->logInfo( "ActiveMirrorControl::cmdDisconnected", "Closed connection from command port of CC" );
	qDebug( "Closed connection from command port of CC" );
	reconnectCCTimeout();
}


/** Slot envoked when we receive a command
from Central Control */
void ActiveMirrorControl::cmdReceived( QString m_qsCmd )
{
	g_pLog->logInfo( "ActiveMirrorControl::cmdReceived", m_qsCmd );
	QDateTime rcvTime = QDateTime::currentDateTime();
	QString dateString;
	dateString.sprintf( "%02d.%02d.%4d %02d:%02d:%02d",
						rcvTime.date().day(), rcvTime.date().month(), rcvTime.date().year(),
						rcvTime.time().hour(), rcvTime.time().minute(), rcvTime.time().second() );
	CommandTime->setText( dateString );
	CommandLabel->setText( m_qsCmd );
	logMessage( m_qsCmd );

  QTextStream qts( m_qsCmd, IO_ReadOnly );
	QString qsCommand;
  qts >> qsCommand;
	if ( qsCommand == "INAMC" )
	{	
		qDebug("FIX ME: INAMC");
	}
	if ( qsCommand == "ADJST" )
	{	
		qDebug("FIX ME: ADJST");
	}
	if ( qsCommand == "INAMC" )
	{	
		qDebug("FIX ME: LSADJ");
	}
	if ( qsCommand == "BREAK" )
	{	
		qDebug("FIX ME: BREAK");
	}
	if ( qsCommand == "SHUTD" )
	{	
		qDebug("FIX ME: SHUTD");
	}
	
}

/** Slot envoked when we receive a command
from Central Control */
void ActiveMirrorControl::ccReportReceived( QString p_qsReport )
{
//	qDebug( "Received CC report" );
//	qDebug( m_qsReport );
//	QDateTime rcvTime = QDateTime::currentDateTime();
//	QString dateString;
//	dateString.sprintf( "%02d.%02d.%4d %02d:%02d:%02d",
//						rcvTime.date().day(), rcvTime.date().month(), rcvTime.date().year(),
//						rcvTime.time().hour(), rcvTime.time().minute(), rcvTime.time().second() );
	
	g_pLog->logInfo( "ActiveMirrorControl::ccReportReceived", p_qsReport );

  QTextStream qts( &p_qsReport, IO_ReadOnly );
  QString tag;
  int iCCState;
  qts >> dec >> tag >> iCCState;
  int iYear, iMonth, iDay;
  int iHour, iMinute, iSecond, iMillis;
	qts >> iYear >> iMonth >> iDay;
  qts >> iHour >> iMinute >> iSecond >> iMillis;
	int iStateWeather, iStateUPS, iStateThresholds, iStateDelays, iStateGPS;
	qts >> iStateWeather >> iStateUPS >> iStateThresholds >> iStateDelays >> iStateGPS;
  int iStateDAQ, iStateCAOS, iStateDrive, iStateStarGuider;
  qts >> iStateDAQ >> iStateCAOS >> iStateDrive >> iStateStarGuider;
  int iStateCaCo, iStateCalib, iStateSentinel, iStateHVPS, iStateLid;
  int iStateLV, iStateCool, iStateHV, iStateDC, iStateLEDs;
  int iStateFADCFans, iStateCalCan, iStateCalIO, iStateCalLV;
  qts >> iStateCaCo >> iStateCalib >> iStateSentinel >> iStateHVPS >> iStateLid;
  qts >> iStateLV >> iStateCool >> iStateHV >> iStateDC >> iStateLEDs;
  qts >> iStateFADCFans >> iStateCalCan >> iStateCalIO >> iStateCalLV;
  int iStateAMC, iStateHEAT;
  qts >> iStateAMC >> iStateHEAT;
  int iStateLIDAR, iStateOptical, iStateL2T, iStateAUX, iStateGRB;
  qts >> iStateLIDAR >> iStateOptical >> iStateL2T >> iStateAUX >> iStateGRB;
	double dZd, dAz, dDec, dRA;
	qts >> dZd >> dAz >> dDec >> dRA;
	double dTemp, dSolar, dWind, dHum;
	qts >> dTemp >> dSolar >> dWind >> dHum;
	double dUPS_V, dRub_GPS_us;
	qts >> dUPS_V >> dRub_GPS_us;
  qts >> tag;
  if ( tag != "OVER" )
		qDebug("Error decoding string"  );

//	CommandTime->setText( dateString );
	CCStateLCD->display( iCCState );

  QString qsLID, qsLEDs;
  switch (iStateLid)
	{
		case 0:
			qsLID.sprintf("Error");
      break;
		case 1:
			qsLID.sprintf("Limit");
      break;
		case 4:
			qsLID.sprintf("Closed");
      break;
		case 5:
			qsLID.sprintf("Open");
      break;
		case 6:
			qsLID.sprintf("Moving");
      break;
		case 7:
			qsLID.sprintf("Stopped");
      break;
		case 9:
			qsLID.sprintf("Not available");
      break;
		default:
			qsLID.sprintf("Unknown");
  }
  switch (iStateLEDs)
	{
		case 0:
			qsLEDs.sprintf("Error");
      break;
		case 4:
			qsLEDs.sprintf("Off");
      break;
		case 5:
			qsLEDs.sprintf("On");
      break;
		case 9:
			qsLEDs.sprintf("Not available");
      break;
		default:
			qsLEDs.sprintf("Unknown");
  }

	ActualAlt->setText( QString::number( dZd ) );
	ActualAz->setText( QString::number( dAz ) );

	QString strReport;					// short readable representation of Report important to CC
	strReport.sprintf( "CC_REPORT %04d %02d %02d %02d %02d %02d -- Lid: %s LEDs: %s -- Zd: %4.1f Az: %5.1f",
                     iYear, iMonth, iDay, iHour, iMinute, iSecond, qsLID.latin1(), qsLEDs.latin1(), dZd, dAz);
	logMessage( strReport );

  int iState;
	if( m_zCCMode )
		iState = g_pAMCState->getState();
  else
		iState = m_iStoredState;
	if ( ( iState == AMC_STATE_INITIALIZED )
       ||
       ( iState == AMC_STATE_ADJUSTED )
       ||
       ( iState == AMC_STATE_LASERADJUSTED ) )
  {
    bool zOk;
		double dFocusZd = LUTAlt->text().toDouble( &zOk );
		if ( ( dZd > 1.0 ) && ( fabs( dZd - dFocusZd ) > 3.0 ) )
		{
		  // Set the state information
			if( m_zCCMode )
				g_pAMCState->setState( AMC_STATE_READJUST );
  		else
			{
				m_iStoredState = AMC_STATE_READJUST;
        adjustSlot();
			}
    }
	}
}

/** Log a message to the statuis window */
void ActiveMirrorControl::logMessage( QString p_qsMsg )
{
	InfoField->append( p_qsMsg.stripWhiteSpace() );
	InfoField->setCursorPosition( InfoField->numLines()+1, 0 );
}

/** Slot envoked when we get a reporting connection to the CC
  */
void ActiveMirrorControl::ccReportConnection()
{
	g_pLog->logInfo( "ActiveMirrorControl::ccReportConnection", "Opened Report connection to CC" );
	m_qtCCRetryTimer.stop();
  g_pAMCState->setState( AMC_STATE_UNDEFINED );
  g_pHEATState->setState( HEAT_STATE_OFF );
}

/** Slot envoked each time a report is send to CC */
void ActiveMirrorControl::reportDelivered( QDateTime& p_qtReportTime , QString& p_qsReport )
{
	QString dateString;
	dateString.sprintf( "%02d.%02d.%4d %02d:%02d:%02d",
						p_qtReportTime.date().day(), p_qtReportTime.date().month(), p_qtReportTime.date().year(),
						p_qtReportTime.time().hour(), p_qtReportTime.time().minute(), p_qtReportTime.time().second() );
	ReportTime->setText( dateString );

  QTextStream qts( &p_qsReport, IO_ReadOnly );

  QString tag1;
  int iAMCState;
  qts >> dec >> tag1 >> iAMCState;
  int iYear, iMonth, iDay;
  int iHour, iMinute, iSecond, iMillis;
	qts >> iYear >> iMonth >> iDay;
  qts >> iHour >> iMinute >> iSecond >> iMillis;
  int iComState;
  qts >> iComState;
  int iComYear, iComMonth, iComDay;
  int iComHour, iComMinute, iComSecond, iComMillis;
	qts >> iComYear >> iComMonth >> iComDay;
  qts >> iComHour >> iComMinute >> iComSecond >> iComMillis;
  int iHEATState;
  qts >> iHEATState;
  int iInstalledPanels, iErrorPanels;
  QString tag2, tag3;
  qts >> tag2 >> iInstalledPanels >> iErrorPanels >> tag3;
  if ( tag3 != "OVER" )
  {
		qDebug("Error decoding string");
    qDebug("%s %02d %04d %02d %02d %s %02d %02d %s",
           tag1.latin1(), iAMCState, iYear, iMonth, iDay,
           tag2.latin1(), iInstalledPanels, iErrorPanels, tag3.latin1() );
  }

  QString qsReport;
	qsReport.sprintf("AMC: %02d HEAT: %02d -- PANELS: %03d installed, %03d errors",
                    iAMCState, iHEATState, iInstalledPanels, iErrorPanels);
	ReportLabel->setText( qsReport );
//  qDebug(p_qsReport);
}

void ActiveMirrorControl::userModeSlot()
{
	g_pLog->logInfo( "ActiveMirrorControl::userModeSlot", "Going to user mode" );
	
	if( m_zCCMode )
	{
		ModeButton->setText( "Go to CC mode  " );
		ManualButton->setEnabled( true );
		AdjustButton->setEnabled( true );
		LaserAdjustButton->setEnabled( true );
		CenterAllButton->setEnabled( true );
		CalButton->setEnabled( true );
		m_zCCMode = false;
		m_iStoredState = g_pAMCState->getState( );
		g_pAMCState->setState( AMC_STATE_USER );
	}
	else
	{
		ModeButton->setText( "Go to user mode" );
		ManualButton->setEnabled( false );
		AdjustButton->setEnabled( false );
		LaserAdjustButton->setEnabled( false );
		CenterAllButton->setEnabled( false );
		CalButton->setEnabled( false );
		m_zCCMode = true;
		g_pAMCState->setState( m_iStoredState );
	}

}

void ActiveMirrorControl::stateChangedSlot( int p_iOldState, int p_iNewState )
{
	QString qsMsg;
  qsMsg.sprintf("AMC State changed: %d -> %d", p_iOldState, p_iNewState );
	g_pLog->logInfo( "ActiveMirrorControl::stateChangedSlot", qsMsg );

	StateLCD->display( p_iNewState );
  StateLabel->setText( g_pAMCState->getText( p_iNewState ) );

	if ( ( p_iNewState == AMC_STATE_READJUST ) && m_zAutoFocus )
     adjustSlot();
}

void ActiveMirrorControl::heatStateChangedSlot( int p_iOldState, int p_iNewState )
{
	QString qsMsg;
  qsMsg.sprintf("HEAT State changed: %d -> %d", p_iOldState, p_iNewState );
	g_pLog->logInfo( "ActiveMirrorControl::heatStateChangedSlot", qsMsg );

//	StateLCD->display( p_iNewState );
//  StateLabel->setText( g_pHEATState->getText( p_iNewState) );
}

/** Slot invoked when no connection to CC is possible. */
void ActiveMirrorControl::ccNoConnection()
{
	m_qtCCRetryTimer.start( 15000 );
}

/** No descriptions */
void ActiveMirrorControl::reconnectCCTimeout()
{
//  qDebug("!! reconnectCCTimeout");
  m_pReportSocket->connectToHost( CC_HOST_ADDRESS, AMC_REP_PORT);
}

/** Move the mirrors manually. */
void ActiveMirrorControl::moveManuallySlot()
{
	g_pLog->logInfo( "ActiveMirrorControl::moveManuallySlot", "Move Panels manually" );

  AdjustMirrorDlg* pMyDialog = new AdjustMirrorDlg( this, "Manual Mode", true );
	pMyDialog->move(10,10);
	int ret = pMyDialog->exec();
	delete pMyDialog;
}

/** Slot invoked to start the calibration dialog. */
void ActiveMirrorControl::calibrateSlot()
{
	g_pLog->logInfo( "ActiveMirrorControl::calibrateSlot", "Calibarte all Panels" );

  CalibrateLaserMovementDialog* pDialog = new CalibrateLaserMovementDialog( this, "Calibration", true );
	int ret = pDialog->exec();
	delete pDialog;
}

/** Slot invoked to start the calibration dialog. */
void ActiveMirrorControl::centerAllSlot()
{
	g_pLog->logInfo( "ActiveMirrorControl::centerAllSlot", "Centering and setting to Roque-Lamp focus" );

  //Set state to moving.
	if( m_zCCMode )
		g_pAMCState->setState( AMC_STATE_MOVING );

  QString qsMsg("Initializing");
	AMCProgDlg* pDialog = new AMCProgDlg( MOTOR_THREAD_CTRL, this, qsMsg, true );
	
	qsMsg.sprintf("Centering and setting to Roque-Lamp focus" );
	pDialog->setText( qsMsg );
	pDialog->start();
	pDialog->exec();
//  MotorThreadDialog* pDialog = new MotorThreadDialog( this, "Centering", true );
//	int ret = pDialog->exec();
	delete pDialog;

  LUTAlt->setText( QString::number( 75 ) );
  LUTAz->setText( QString::number( 0 ) );

  // Set the state information
	if( m_zCCMode )
		g_pAMCState->setState( AMC_STATE_INITIALIZED );
  else
		m_iStoredState = AMC_STATE_INITIALIZED;

}

/** Perform a lookup table adjustment. */
void ActiveMirrorControl::adjustSlot()
{
	// Get Altitude and Zenith Distance and
	double dZd, dAz;
	if ( ! getZdAz( dZd, dAz ) )
		return;

  //Set state to moving.
	if( m_zCCMode )
		g_pAMCState->setState( AMC_STATE_MOVING );

  QString qsMsg("Adjusting all mirrors");
	AMCProgDlg* pDialog = new AMCProgDlg( ADJUST_THREAD_CTRL, this, qsMsg, true );

	QString qsLUTName = AMCLookUpTable::findLUT( dZd, dAz );	
	qsMsg.sprintf("Using lookup table  %s  for Zd: %3.0f Az: %4.0f", qsLUTName.latin1(), dZd, dAz );
	pDialog->setText( qsMsg );

	qsMsg;
  qsMsg.sprintf("Adjust for Zd: %6.2f Az: %6.2f using LUT %s", dZd, dAz, qsLUTName.latin1());
	g_pLog->logInfo( "ActiveMirrorControl::adjustSlot", qsMsg );

	pDialog->start();
	pDialog->exec();

	delete pDialog;

	int iFileZd = qsLUTName.mid(25,2).toInt();
	int iFileAz = qsLUTName.mid(28,3).toInt();
  LUTAlt->setText( QString::number( iFileZd ) );
  LUTAz->setText( QString::number( iFileAz ) );

  // Set the state information
	if( m_zCCMode )
		g_pAMCState->setState( AMC_STATE_ADJUSTED );
  else
		m_iStoredState = AMC_STATE_ADJUSTED;
}

/** Perform a laser adjustment. */
void ActiveMirrorControl::laserAdjustSlot()
{
	double dZd, dAz;
	if ( ! getZdAz( dZd, dAz ) )
		return;

	if( m_zCCMode )
		g_pAMCState->setState( AMC_STATE_MOVING );

	QString qsMsg;
  qsMsg.sprintf("Laser adjust for Zd: %6.2f Az: 6.2f", dZd, dAz);
	g_pLog->logInfo( "ActiveMirrorControl::laserAdjustSlot", qsMsg );

  qsMsg.sprintf("Laser Adjustment");
	AMCProgDlg* pDialog = new AMCProgDlg( LASER_ADJUST_THREAD_CTRL, this, qsMsg, true );
	// To not have to synchronize on the framegrabber, we run through all
	// mirror panels sequentially.
	pDialog->setParallel( false );
	
	qsMsg.sprintf("Zd: %3.0f Az: %4.0f", dZd, dAz );
	pDialog->setText( qsMsg );
	pDialog->start();
	pDialog->exec();
	AMCLookUpTable::saveLUT( dZd, dAz );	
  LUTAlt->setText( QString::number( dZd ) );
  LUTAz->setText( QString::number( dAz ) );

	delete pDialog;

  // Set the state information
	if( m_zCCMode )
		g_pAMCState->setState( AMC_STATE_LASERADJUSTED );
  else
		m_iStoredState = AMC_STATE_LASERADJUSTED;
}

/** Perform a error testing loop. */
void ActiveMirrorControl::errorTestSlot()
{
  m_iLoops = LoopsSpinBox->value();
	qDebug("numLoops: %d", m_iLoops );

  QString qsMsg;
	qsMsg.sprintf("Communication Error Testing: %d Loops", m_iLoops );
	AMCProgDlg* pDialog = new AMCProgDlg( ERROR_TEST_THREAD_CTRL, this, qsMsg, true );
	// To not have to synchronize on the framegrabber, we run through all
	// mirror panels sequentially.
//	pDialog->setParallel( false );
	
	qsMsg.sprintf("Tersting for communication errors");
	pDialog->setText( qsMsg );
	pDialog->start();
	pDialog->exec();
	delete pDialog;
}

bool ActiveMirrorControl::getZdAz( double& p_dZd, double& p_dAz)
{
  bool zOk;
	p_dZd = ActualAlt->text().toDouble( &zOk );
	if( zOk == true )
		p_dAz  = ActualAz->text().toDouble( &zOk );
	
	if( zOk == false )
	{
		QMessageBox::warning( this, "Active Mirror Control",
													"Wrong format of Azimuth or Zenith Distance",
													QMessageBox::Ok | QMessageBox::Default,
												QMessageBox::NoButton );
		return false;
	}
	if( p_dZd < 0.0 || p_dZd > 95.0 )
	{
		QMessageBox::warning( this, "Active Mirror Control",
													"Zenith distance out of range ( 0 - 95 )",
													QMessageBox::Ok | QMessageBox::Default,
												QMessageBox::NoButton );
		return false;
	}

	if( p_dAz < -90.0 || p_dAz > 318.0 )
	{
		QMessageBox::warning( this, "Active Mirror Control",
													"Azimuth out of range ( -90 - 318 )",
													QMessageBox::Ok | QMessageBox::Default,
													QMessageBox::NoButton );
		return false;
	}

	return true;
}

/** No descriptions */
void ActiveMirrorControl::autoFocusSlot( bool p_zOn )
{
	if ( p_zOn )
		qDebug( "Turning AutoFocus ON" );
  else
		qDebug( "Turning AutoFocus OFF" );
  m_zAutoFocus = p_zOn;
}
